padosoft / askmydocs-connector-zendesk
Zendesk connector for AskMyDocs: resumable ingestion of Support tickets, comments, attachments, customers, organizations and Help Center articles, plus read-only live tools for the chat.
Package info
github.com/padosoft/askmydocs-connector-zenddesk
pkg:composer/padosoft/askmydocs-connector-zendesk
Requires
- php: ^8.3
- ext-curl: *
- ext-fileinfo: *
- ext-zip: *
- illuminate/contracts: ^12.0|^13.0
- illuminate/database: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/queue: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- league/html-to-markdown: ^5.1
- padosoft/askmydocs-connector-base: ^1.6
Requires (Dev)
- laravel/pint: ^1.18
- mockery/mockery: ^1.6
- orchestra/testbench: ^10.0|^11.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-11 19:53:50 UTC
README
Zendesk connector for AskMyDocs — tickets, comments, attachments, customers, organizations and Help Center articles, plus read-only live tools for the chat.
Drop-in Laravel package. Install it next to askmydocs-connector-base and the Zendesk card appears in the admin UI.
Table of contents
- Why this package
- Features
- Architecture at a glance
- Installation
- Credential setup (step by step)
- Activation inside AskMyDocs
- What gets ingested
- Sync semantics
- Live tools for the chat
- Agent usage profile
- Trust, privacy and access control
- Configuration reference
- Queues and operations
- Testing and development
- Troubleshooting
- Documentation index
- License
Why this package
A customer-service desk is one of the richest sources of company knowledge: every resolved ticket is a worked example, every Help Center article is an approved answer, and every customer and organization profile is context an agent would otherwise have to look up by hand. AskMyDocs is an enterprise RAG and canonical-knowledge platform; this package pipes a Zendesk Support account into it.
It does two complementary things:
- Indexed knowledge — a resumable, checkpointed import turns tickets (with their comments), attachments, customers, organizations and published Help Center articles into KB documents, so the chat can find "how did we resolve this before?" semantically.
- Live answers — eleven read-only tools let the chat ask Zendesk right now about a specific ticket, customer, organization or article, so "what is the status of ticket #4821?" is never answered from a stale snapshot.
It follows the same contract as the sibling connectors (askmydocs-connector-freshdesk, -jira, -confluence, -google-drive, …): it extends Padosoft\AskMyDocsConnectorBase\BaseConnector, is discovered through composer.json extra.askmydocs.connectors, and never touches host classes — everything host-side goes through ConnectorIngestionContract.
Features
- 🔌 Zero-config discovery — Laravel auto-discovery registers the service provider; the connector is registered under the key
zendeskand also added toconnectors.built_in, so it works with a Composerpathrepository during development. - 🔐 Two authentication modes — API token (
{admin email}/token+ token, HTTP Basic) or an OAuth access token (Bearer). Secrets live only in the encrypted credential vault, never inconfig_json. - 🎫 Tickets with their conversation — subject, status, priority, type, requester, assignee, organization, channel, tags and every comment, with author names resolved.
- 🔒 Internal notes are a switch — private agent comments are included by default and can be excluded; changing the setting re-imports already imported tickets.
- 📎 Attachments as documents — PDF, DOCX, TXT and Markdown (images too when the host enables OCR). Size, count, malware-scan result, real MIME and DOCX structure are checked; credentials are sent only to the account host.
- 👤 Customers and 🏢 organizations — one profile document each (end users only; agents are never imported as customers).
- 📚 Help Center articles — published articles only; drafts are never imported and a draft removes the existing document. Optional locale filter.
- ♻️ Resumable, idempotent scans — durable checkpoints, per-installation lock, content fingerprints,
Retry-Afterhonoured, deletions only on confirmed evidence. - 🧭 Cursor-based incremental exports — tickets and users use Zendesk's cursor-based incremental exports, the documented way to walk large accounts without skipping records.
- 🛠️ Eleven read-only live tools — opt-in per installation, project-scoped, size-capped, with explicit coverage and pagination metadata.
- 🧠 Agent usage profile —
resources/agent-usage.jsontells the orchestrator when to go live, when to use the index and when to combine both. - 🏷️ Provenance and ACL reporting — implements
DeclaresProvenance(tickets are customer-authored, so the default isuntrusted-external) andSupportsSourceAcl(agents-only records, public or segment-restricted articles). - 🏢 Multi-account, multi-tenant — several Zendesk accounts per tenant, each bound to its own KB project; every query is tenant-scoped.
Architecture at a glance
┌──────────────────────────────┐
Composer │ padosoft/askmydocs-connector-│
require ───────▶│ zendesk (this package) │
└──────────────┬───────────────┘
│ extra.askmydocs.connectors
▼
┌──────────────────────────────┐
│ askmydocs-connector-base ^1.6│
│ ConnectorRegistry · Vault · │
│ SyncScheduler · TenantContext│
└──────────────┬───────────────┘
│ resolves ZendeskConnector
▼
syncFull / syncIncremental ─▶ SyncManager::start() (queues a scan, returns at once)
│
▼ ProcessSyncBatch (queue "zendesk", one bounded batch per job)
┌──────────── phase machine, state in zendesk_sync_runs.checkpoint ────────────┐
│ tickets → users → organizations → articles → article_check → customer_check │
└───────────────┬──────────────────────────────────────────────┬───────────────┘
│ ZendeskClient (read-only, capped, retried) │ DocumentImporter
▼ ▼
https://{subdomain}.zendesk.com/api/v2 ConnectorIngestionContract (host)
├─ redactContent() PII at the boundary
├─ dispatchIngestion()
└─ softDeleteByRemoteId()
chat ──▶ ZendeskTools::catalog() / execute() ──▶ ZendeskClient (live, read-only)
Key design decisions (full rationale in docs/architecture.md):
- Scheduling is separate from importing.
syncFull()andsyncIncremental()only queue or resume a scan; the work happens inProcessSyncBatchjobs. The$sinceargument ofsyncIncremental()is deliberately ignored — the package's own durable checkpoints are the watermark. - One write path. Every document kind goes through
DocumentImporter::write(): redaction, fingerprint, stable path, source-aware metadata, reported ACL, counters. - The package never decides who may read a document. It reports what Zendesk says (
SupportsSourceAcl); the host maps those identifiers to its own subjects.
Installation
Requirements: PHP 8.3+, Laravel 12 or 13, ext-curl, ext-fileinfo, ext-zip, and padosoft/askmydocs-connector-base ^1.6. The host must bind ConnectorIngestionContract (AskMyDocs ships the reference bridge) and run an asynchronous queue.
composer config repositories.askmydocs-connector-base vcs https://github.com/padosoft/askmydocs-connector-base
composer config repositories.askmydocs-connector-zendesk vcs https://github.com/padosoft/askmydocs-connector-zenddesk
composer require 'padosoft/askmydocs-connector-zendesk:^1.0'
php artisan migrate
The Composer package is
padosoft/askmydocs-connector-zendesk; the GitHub repository slug isaskmydocs-connector-zenddesk(historical spelling). The current release isv1.0.0; it has been validated with simulated HTTP only, so run a first scan against a Zendesk sandbox before pointing it at production (see docs/zendesk-api.md).
Optional publishes:
php artisan vendor:publish --tag=connector-zendesk-config # config/connector-zendesk.php php artisan vendor:publish --tag=connector-zendesk-assets # public/connectors/zendesk.svg
The package ships one migration that creates two tables, loaded automatically: zendesk_sync_runs (one row per scan, with its checkpoint) and zendesk_source_states (what was last delivered for each remote object). The connector_installations / connector_credentials tables belong to connector-base.
Developing against a local checkout
# composer.local.json is git-ignored: a copy of composer.json whose `repositories` # entry is a `path` repository pointing at your connector-base checkout. COMPOSER=composer.local.json composer install
See docs/development.md.
Credential setup (step by step)
Full walkthrough with troubleshooting: docs/credential-setup.md. The short version:
Option A — API token (simplest)
- In Zendesk open Admin Center → Apps and integrations → APIs → Zendesk API.
- On the Settings tab make sure Token access is enabled.
- Click Add API token, give it a description such as
AskMyDocs, copy the token once (Zendesk will not show it again) and save. - Decide which agent the token acts as. An API token impersonates the agent whose email you pair it with, so use a dedicated agent (for example
askmydocs@your-company.com) that holds the Administrator role: the incremental export endpoints the import relies on are admin-only, and the connector refuses non-admin credentials when you save the form. Keep the account dedicated and its token secret — the connector itself only ever issuesGETrequests. - In AskMyDocs choose Connectors → Zendesk → Install and enter:
- Zendesk subdomain —
companyforcompany.zendesk.com(the full host orhttps://company.zendesk.comis accepted too); - Authentication — API token;
- Admin email — the dedicated administrator's email;
- API token — the token from step 3.
- Zendesk subdomain —
Zendesk's credential format is {email_address}/token:{api_token} over HTTP Basic; the package builds it for you.
Option B — OAuth access token
Zendesk recommends OAuth over API tokens because a token can be scoped. Create an OAuth client in Admin Center → Apps and integrations → APIs → OAuth Clients, obtain an access token that carries the read scope, then choose OAuth access token in the form and paste it. The package sends it as Authorization: Bearer ….
Limitation (v1): the package consumes an access token you provide; it does not run the OAuth authorization-code dance or refresh expiring tokens. Use a non-expiring token, or re-enter the token when it expires (the installation shows an errored state and a 401 in its health check).
What the form checks
Saving the form calls GET /api/v2/users/me before anything reaches the vault. It must return a real user with the role admin: Zendesk answers 200 with a null identity to anonymous callers, and the incremental export APIs the import uses are admin-only, so anonymous, end-user and agent credentials are rejected. A failed re-configuration leaves the previous secret untouched.
Activation inside AskMyDocs
- Open Settings → Connectors in the AskMyDocs admin UI.
- The Zendesk card appears; click Install, fill in the credential form, choose a label (for example
support-eu) and the KB project the documents should land in. - Open the installation's settings to review the sync window, internal notes, customers, organizations, Help Center, attachments and trust level (see Configuration reference).
- Click Sync now, or wait for the scheduler (default cadence is set by
connector-base). Fetch all starts the one-time historical import.
You can connect several Zendesk accounts (labels) to one tenant, each with its own project binding.
What gets ingested
| Kind | Source | Document | Notes |
|---|---|---|---|
ticket |
Incremental ticket export + GET /tickets/{id} + GET /tickets/{id}/comments |
Markdown: header facts, then every comment oldest-first with author and timestamp | Requester, assignee and organization names are resolved; internal notes are labelled and optional |
attachment |
content_url of comment attachments |
The file itself (pdf, docx, txt, md; images when kb.ocr.enabled) |
Child of its ticket; inline images are skipped |
user |
Incremental user export | Markdown profile: email, role, organization, phone, tags, details, notes | End users only; agents are never customers |
organization |
Incremental organization export | Markdown profile: domains, tags, details, notes | |
article |
GET /help_center/articles |
Markdown converted from the article HTML | Published only; drafts are never imported |
Documents are written to {project}/connectors/zendesk/tenant-{sha256(tenant)}/installation-{id}/{kind}/{remote id}.{ext} and carry, under metadata.converter_hints.zendesk, the vendor fields (status, priority, ids, tags, timestamps, …) plus the standard _derived block (search_tags, status_active, recency_bucket). The remote identifier is stored as metadata.zendesk_remote_id = "{installation}:{kind}:{id}" — the key used for deletions and for readAcl().
Text bodies and titles pass through ConnectorIngestionContract::redactContent() before they are written or dispatched, so the host's PII policy applies to tickets, comments, profiles and articles. Binary attachments are not redacted (regex redaction cannot inspect PDFs or images); see docs/security.md.
Sync semantics
Two scan modes share one pipeline:
| Mode | Start | Ticket / user / organization period |
|---|---|---|
Ordinary (window) |
SyncManager::start($installation) — also what syncFull() and syncIncremental() call |
First scan: the last date_window_days (default 90). Later scans continue from the previous completed scan, minus a 120-second overlap, as long as the window did not grow |
Historical (history, Fetch all) |
SyncManager::start($installation, history: true) |
From 1970-01-01 up to the scan start, regardless of the ordinary window |
Help Center articles are enumerated in full in both modes (they are not windowed).
Phases run in a fixed order and skip what you disabled: tickets → users → organizations → articles → article_check, plus customer_check in history mode. State lives in zendesk_sync_runs.checkpoint; a failed run resumes where it stopped.
- Cursor-based exports. Tickets and users are read from Zendesk's cursor-based incremental exports (
start_time, then the returnedafter_cursoruntilend_of_stream). A cursor that does not advance fails the scan loudly instead of looping. - Idempotency. Each document has a SHA-256 fingerprint of its body, fields and reported ACL. Unchanged content is not delivered again; a changed ACL is.
- Rate limits. A
429keeps the checkpoint and re-queues the batch afterRetry-Afterseconds (60 if absent). Incremental exports are limited by Zendesk to 10 requests per minute (30 with the High Volume add-on) — the batch loop is small by design. - Deletions need evidence.
- A ticket is removed (with its attachments) when the export reports it with status
deleted. A404on a ticket is not evidence (archived or inaccessible tickets answer 404). - A customer is removed when the export reports them as non-end-user or inactive; in history mode, customers and organizations the full export did not return are verified with
show_manyand removed if Zendesk omits them. - An article is removed when it becomes a draft, or when the article detail answers
404after it vanished from the list. Absence from a list alone never deletes. - An attachment Zendesk later marks as deleted or malware is removed; one that is merely over a size or count limit is not.
- Tickets that fall out of the ordinary window are kept.
- A ticket is removed (with its attachments) when the export reports it with status
- Settings that change content or destination (internal notes, attachment options, OCR, the KB project binding) are hashed; on change the next scan first re-imports every known ticket, customer and organization.
- Disabling a content type (customers, organizations, Help Center) stops importing it and its history reconciliation, but does not delete what was already imported.
- Rebinding to another project re-delivers every known object to the new project through that refresh pass and retires the copy in the previous one.
Details and failure semantics: docs/sync-semantics.md.
Live tools for the chat
Tools\ZendeskTools exposes a catalog and an executor, independent of the importer. For every active installation bound to the requested project that an operator has explicitly enabled (installation setting Expose live Zendesk tools to the chat, live_tools.enabled, off by default) there are eleven read-only tools, named zendesk_{installationId}_{action} (for example zendesk_42_get_ticket):
| Action | Arguments | Purpose |
|---|---|---|
list_tickets |
one of requester_id, organization_id, email — plus cursor?, page?, per_page? |
Tickets of a verified requester or organization, newest first, all statuses; without a selector, the most recently updated tickets |
search_tickets |
query, page? |
Zendesk search syntax (free text + filters such as status:open, priority>normal, assignee_id:123); type:ticket is forced; max 1000 results |
search_users |
term, page? |
Resolve a customer (end user) by name or email; agents are not returned |
get_user |
id |
A user (customer or agent) by ID |
search_agents |
term, page? |
Resolve a support agent (role:agent) |
search_organizations |
term |
Resolve an organization by name (autocomplete) |
get_organization |
id |
An organization by ID |
get_ticket |
id |
A ticket by ID |
list_comments |
id, cursor?, per_page? |
A page of comments; internal notes follow the installation setting |
search_articles |
term, page? |
Published Help Center articles by keyword |
get_article |
id |
A published article by ID |
use Padosoft\AskMyDocsConnectorZendesk\Tools\ZendeskTools; // The host middleware or job has already activated the right tenant. $tools = app(ZendeskTools::class); $catalog = $tools->catalog('support'); // project key is mandatory $result = $tools->execute('zendesk_42_get_ticket', ['id' => 4821], 'support'); // → ['data' => …] or ['error' => …, 'error_code' => …], plus physical_request_count and provenance
Trust model. Live calls run with the installation's admin credential, not with the Zendesk permissions of the user asking. An agent restricted to assigned tickets, groups or brands in Zendesk could therefore read, through the chat, tickets and internal notes Zendesk would deny them. That is why the setting is off by default: enable it only when everyone with access to the bound KB project may read the whole account. Indexed documents carry an unknown ACL for the same reason.
Guarantees:
- Scoped twice.
catalog()lists only active installations of the current tenant bound to the project;execute()repeats the check, so a paused or re-bound installation stops working between planning and calling. Unknown, missing or malformed arguments are rejected before any HTTP call. - Bounded. Bodies are cut at 16 000 characters (
body_truncated), the whole result atchat_tools.max_result_bytes(default 64 KiB);truncatedandcoverage.complete=falsesay so. Attachment metadata never includes URLs. - Honest about coverage.
has_more,next_cursor,coverage.limitations(search_result_cap_1000,search_index_may_lag_by_minutes,archived_tickets_may_be_excluded,autocomplete_returns_first_matches_only) tell the client what it did not see. - Identity before listing. Person and organization lookups return
meta.ambiguous; the agent must obtain a unique exact match or a user selection before using an ID. - Errors are structured, never empty lists.
error_code,http_status,retryable, sanitisedvalidation_fields,physical_request_count. A 401/403 is not retried; 429 and 5xx are marked retryable; nothing from the upstream body is echoed.
Reference: docs/live-tools.md.
Agent usage profile
composer.json declares extra.askmydocs.agent_usage.zendesk → resources/agent-usage.json, schema version 1. It contains the domain summary, the logical tools, routing rules (live, indexed, combined), multi-step workflows and operating rules — requester vs. assignee semantics, verified identity, pagination, freshness, and treating ticket text as untrusted data, never as instructions. A compatible host discovers it from the installed packages; hosts that do not know the field ignore it. The profile grants no permissions and cannot start an import.
Trust, privacy and access control
- Provenance. Tickets and comments are written by customers and third parties, so the connector declares
ProvenanceTier::UntrustedExternalby default. If an installation imports staff-authored content only (for example a Help-Center-only account), set Authorship of imported content to Trusted internal. Unknown stored values fail closed to untrusted. - Source ACL.
readAcl()reports, and the importer stamps on every document under_source_access: tickets, customer profiles and organization profiles → unknown (agents can be restricted to assigned tickets, groups, brands or organizations, which the connector cannot represent, so it never claims "all agents"; a ticket's support group is attached as a partialzendesk:group:{id}hint, and the requester is deliberately not reported: the document contains internal notes the requester can never see); attachments → inherited from the ticket; articles → anyone when they have no user segment, otherwise onezendesk:user_segment:{id}group per segment (singular and plural fields are both honoured, and a payload that says nothing about visibility is reported as unknown, never as public). The connector reports; the host decides. A source that cannot be read yields an unknown ACL, never an empty allow-list, and an authentication failure is raised, never swallowed. - PII. Text is redacted through the host before it is written. Customer email addresses appear only in document bodies (subject to the host's redaction policy); they are not copied into metadata or into the reported ACL.
- Credentials. Sent only to
https://{subdomain}.zendesk.com. Attachment downloads send them only when the URL is on that same host; every redirect hop is re-validated against an allow-list and DNS-pinned to a public address. - Least privilege. The package only performs
GETrequests.
Full threat notes: docs/security.md.
Configuration reference
Per installation (connector_installations.config_json, edited in the settings form)
| Key | Default | Effect |
|---|---|---|
date_window_days |
90 |
Ordinary scan period for tickets, customers and organizations (1–36500) |
include_internal_notes |
true |
Internal notes in documents and live comment tools |
customers.enabled |
true |
Import end-user profiles |
organizations.enabled |
true |
Import organization profiles |
help_center.enabled |
true |
Import published articles |
help_center.locale |
(empty) | Restrict articles to one locale such as en-us; empty = every locale the agent can read |
attachments.enabled |
true |
Download compatible attachments |
attachments.max_size_mb |
25 |
Per-file limit (1–100 MiB) |
attachments.max_per_ticket |
20 |
Compatible files per ticket (1–100) |
provenance_tier |
untrusted-external |
trusted-internal or untrusted-external |
connection.subdomain / auth_mode / email |
— | Set by the credential form; the secret is in the vault |
Package (config/connector-zendesk.php)
| Key | Default | Effect |
|---|---|---|
http.timeout / connect_timeout |
30 / 5 s |
API timeouts |
http.attempts |
3 |
Tries for connection failures and 5xx |
http.max_json_bytes |
16 000 000 |
Cap on one API response |
http.resolve_dns |
true |
DNS-pin public addresses; false is for simulated HTTP in tests only |
attachments.allowed_hosts |
*.zendesk.com, *.zdusercontent.com, *.zdassets.com, S3 hosts |
Where attachments (and their redirects) may come from |
sync.batch_items / batch_seconds |
5 / 60 |
Steps and indicative duration of one batch |
sync.overlap_seconds |
120 |
Re-scan overlap before the previous watermark |
sync.export_min_age_seconds |
90 |
Keeps the export start_time safely in the past |
sync.ticket_support_type_scope |
(unset) | CONNECTOR_ZENDESK_TICKET_SCOPE: agent, ai_agent or all, sent as support_type_scope to the ticket export. Unset keeps Zendesk's default; use all to include AI-agent tickets (not yet verified live). Changing it restarts the ticket window on the next scan |
attachments.allow_unscanned |
false |
CONNECTOR_ZENDESK_ALLOW_UNSCANNED: also import files whose malware scan is not_scanned, failed_to_scan or missing. Off by default: only malware_not_found is imported |
sync.queue / sync.connection |
zendesk / (default) |
CONNECTOR_ZENDESK_QUEUE, CONNECTOR_ZENDESK_CONNECTION |
sync.middleware |
[] |
Job middleware for ProcessSyncBatch and StartSync |
chat_tools.enabled |
true |
Global kill switch: false removes every live tool. Tools also need the per-installation live_tools.enabled setting (off by default) |
chat_tools.max_result_bytes |
65536 |
Cap on one tool result |
Queues and operations
php artisan queue:work zendesk --queue=zendesk --timeout=600 --tries=3
- Use an asynchronous queue (a synchronous
queue.defaultorCONNECTOR_ZENDESK_CONNECTIONis refused) and a cache store with atomic locks (Redis): one batch per installation runs at a time, others are deferred by 30 seconds. - The queue connection's
retry_aftermust be greater than 600 seconds. StartSync($installationId, $tenantId)is the entry point for your own scheduler; it queues or resumes a scan without marking anything complete.- A worker timeout leaves a resumable failure (the installation shows
errored) and keeps the checkpoint; starting a scan again resumes it. No exception body is ever persisted. - Pausing or disconnecting an installation cancels its scan at the next step.
Testing and development
composer install # or: COMPOSER=composer.local.json composer install composer test # PHPUnit + Testbench, SQLite, simulated HTTP composer analyse # PHPStan level 6 vendor/bin/pint --test # code style
The suite covers credential verification and state tokens, both auth modes, full scans and idempotency, internal-note and content-type switches, deletions on evidence only, attachments (credentials scoping, redirects, MIME, size, malware flag), rate limits, resume after failure, locks, cancellation, tenant isolation, every live tool, the agent profile, provenance and ACL. CI runs PHP 8.3/8.4 × Laravel 12/13. No test talks to a real Zendesk account; see Troubleshooting and docs/zendesk-api.md for what has and has not been verified against a live instance.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Install says Zendesk did not recognise the credentials | users/me returned an anonymous identity: wrong token, wrong subdomain, or token access disabled |
Re-check the token, the admin email and Token access in Admin Center |
| Use an admin account | The email belongs to an agent or end user | Pair the token with an admin email |
Zendesk rejected the credentials (401) in health |
Token deleted/rotated, or OAuth token expired | Create a new token and re-enter credentials |
The Zendesk account does not permit this operation (403) |
The agent's role cannot read that resource (for example users or Help Center) | Grant the role access, or switch the content type off |
| Scan stays queued | No worker on the zendesk queue, or a sync queue |
Run the worker command above; use an async queue |
Zendesk rate limit reached |
Incremental exports are limited per minute | Nothing to do: the batch resumes after Retry-After |
| export cursor did not advance | Zendesk returned the same cursor without ending the stream | Re-run; if persistent, open an issue with the run's checkpoint |
| Ticket appears in search but not in the index | It is outside date_window_days |
Widen the window or use Fetch all |
| New ticket missing from live search | Zendesk search is indexed with a delay of minutes | Use get_ticket, or retry shortly |
| Internal notes still visible after switching off | The re-import runs on the next scan | Trigger Sync now |
| Deleted customer still in the KB | Ordinary scans only see exports inside the window | Run Fetch all once; it verifies absent customers |
More: docs/troubleshooting.md.
Documentation index
| Page | Contents |
|---|---|
| docs/architecture.md | Components, data flow, design decisions and trade-offs |
| docs/credential-setup.md | Creating tokens/OAuth credentials, roles, rotation |
| docs/sync-semantics.md | Phases, checkpoints, deletions, failure and resume behaviour |
| docs/live-tools.md | Tool reference, argument rules, result and error shapes |
| docs/data-model.md | Tables, document layout, metadata, remote identifiers |
| docs/security.md | Threat model, SSRF/credential handling, PII, ACL, provenance |
| docs/zendesk-api.md | Every endpoint used, with its verification status |
| docs/troubleshooting.md | Symptom → cause → fix |
| docs/development.md | Local setup, tests, extending the package |
| CHANGELOG.md | Release notes |
License
Apache-2.0 — see LICENSE.
Zendesk is a trademark of Zendesk, Inc. This project is independent and is not endorsed by or affiliated with Zendesk. See public/icons/README.md for the icon's provenance.
Built and maintained by Padosoft. Part of the AskMyDocs connector ecosystem.