@kody/zendesk
README.md
256 lines · 9.9 KB · Markdown@kody/zendesk
Intent
Provide reusable, account-agnostic Zendesk helpers so Kody agents can read and
update tickets, users, and Help Center articles through a saved zendesk /
zendesk-* OAuth integration or an API token plus agent email — without
hand-rolling REST. The Support subdomain is an input (or packageStorage
config.subdomain) so this package is not locked to one account. Mutations
are previewable with dryRun: true and only run live after confirm: true.
This listing is meant to be forked. After you fork, connect your Zendesk
account. Do not treat the live @kody/zendesk package storage as yours.
Share: https://kody.codes/@kody/zendesk
When To Use
- List, search, or inspect tickets, ticket comments, users, and articles
- Create or update tickets, users, and articles after explicit confirmation
- Call an unwrapped Zendesk REST path through
./request - Connect more than one Zendesk account via
integrationName/account
Auth
Zendesk has no built-in Kody OAuth app. Choose one lane. Both need your
Support subdomain (acme from https://acme.zendesk.com). Pass
subdomain on each call, or store it once with ./configure.
| Lane | When to use | Credential |
|---|---|---|
| OAuth (recommended) | Refresh tokens, scoped access, multi-account | Saved integration zendesk or zendesk-<purpose> |
| API token + email | Fastest for a single account | User secrets zendeskApiUsername ({email}/token) and zendeskApiToken |
OAuth sends Authorization: Bearer …. API tokens use HTTP Basic
{email}/token:{api_token}. Required API host:
{your-subdomain}.zendesk.com. Approve it in the account secrets UI.
Store your subdomain
import configure from 'kody:@kody/zendesk/configure'
export default async function main() {
return await configure({
subdomain: 'acme',
email: 'agent@example.com',
})
}That writes packageStorage config for this saved package copy. Substitute
your subdomain. Never paste tokens into chat.
Lane A — BYO OAuth
- In Admin Center open Apps and integrations → APIs → OAuth clients
(
https://your-subdomain.zendesk.com/admin/apps-integrations/apis/oauth_clients) - Create a Confidential client
- Set the redirect URI exactly to
https://kody.codes/connect/oauth - Connect while signed in to Kody (replace
your-subdomain):
- Paste the Zendesk client id and client secret into the Kody wizard (never
into chat). Approve host
{your-subdomain}.zendesk.com. - Reconnect later with https://kody.codes/connect/oauth?provider=zendesk
OAuth authorize URL: https://{subdomain}.zendesk.com/oauth/authorizations/new.
Token URL: https://{subdomain}.zendesk.com/oauth/tokens. Flow: confidential
(client secret). Scopes are space-separated.
To connect a second account, change provider (for example
provider=zendesk-work) and pass integrationName: 'zendesk-work' plus that
account's subdomain on every call.
Lane B — API token + email
- Create a token at Apps and integrations → APIs → Zendesk API
(
https://your-subdomain.zendesk.com/admin/apps-integrations/apis/zendesk-api/settings) - Save the Basic username
{agent-email}/token(do not paste the value in chat):
- Save the API token:
For a second account, use distinct secret names such as
zendeskApiToken-work / zendeskApiUsername-work and pass
secretName / usernameSecretName (or account: 'work').
Scopes
| Scope | Needed for |
|---|---|
tickets:read | ./list-tickets, ./get-ticket, ./search-tickets, ./list-ticket-comments |
tickets:write | ./create-ticket, ./update-ticket, ./add-ticket-comment |
users:read | ./list-users, ./get-user, ./search-users, ./me, ./smoke-test |
users:write | ./create-user, ./update-user |
hc:read | ./list-articles, ./get-article, ./search-articles |
hc:write | ./create-article, ./update-article |
read / write | Broad access when granular scopes are unavailable |
If Zendesk returns 401/403 or an insufficient-scope error, helpers throw a message that names the missing scope and the next setup URL (reconnect OAuth with that scope, or save an API token).
Multiple accounts
Every export accepts:
subdomain— required unless stored via./configureemail— optional API-token hint; also stored by./configureintegrationName/integration— exact saved OAuth name (zendesk-work)account—work→zendesk-work;zendesk-workused as-is; omitted →zendesksecretName/usernameSecretName— API-token secret overridesauth—'oauth'or'apiToken'when both exist
Do not hard-code a personal subdomain as the only option.
Safety
Mutating helpers require confirm: true. Pass dryRun: true to inspect the
REST payload without calling Zendesk. ./request treats GET and search paths
as read-only; POST / PUT / PATCH / DELETE need confirmation.
The account-health job is disabled by default.
Exports
| Export | Description |
|---|---|
. | Package overview, connect URLs, export map |
./accounts | Resolve integration/secret names and report what is connected |
./configure | Store subdomain / email in this package's packageStorage |
./smoke-test | Local helper checks plus optional live /users/me (no email) |
./me | Authenticated agent id / name / role |
./list-tickets | List tickets |
./get-ticket | Get one ticket |
./search-tickets | Search tickets (type:ticket is added when missing) |
./create-ticket | Preview or create a ticket |
./update-ticket | Preview or update a ticket |
./list-ticket-comments | List comments on a ticket |
./add-ticket-comment | Preview or add a ticket comment |
./list-users | List users |
./get-user | Get one user |
./search-users | Search users |
./create-user | Preview or create a user |
./update-user | Preview or update a user |
./list-articles | List Help Center articles |
./get-article | Get one article |
./search-articles | Search Help Center articles |
./create-article | Preview or create an article (sectionId) |
./update-article | Preview or update an article |
./request | Generic REST escape hatch |
./scheduled-account-health | Disabled job wrapper: /users/me plus first-page counts |
./types | Shared TypeScript types |
Smoke test
import smokeTest from 'kody:@kody/zendesk/smoke-test'
export default async function main() {
return await smokeTest()
}Without credentials this returns { ok: true, live: false } plus the connect
and secret URLs. After OAuth or an API token is saved and a subdomain is
available it reads /users/me and returns { live: true, hasUserId } without
email.
Preview a mutation without credentials:
import createTicket from 'kody:@kody/zendesk/create-ticket'
export default async function main() {
return await createTicket({
subject: 'Follow up on onboarding',
comment: 'Customer asked for a status update.',
subdomain: 'acme',
dryRun: true,
})
}Examples
import listTickets from 'kody:@kody/zendesk/list-tickets'
import searchUsers from 'kody:@kody/zendesk/search-users'
export default async function main() {
const tickets = await listTickets({ subdomain: 'acme', perPage: 10 })
const users = await searchUsers({ subdomain: 'acme', query: 'pat@example.com' })
return { ticketCount: tickets.items.length, userCount: users.items.length }
}import createTicket from 'kody:@kody/zendesk/create-ticket'
export default async function main() {
const preview = await createTicket({
subject: 'Investigate checkout timeout',
comment: 'Seen on mobile Safari.',
subdomain: 'acme',
dryRun: true,
})
// After the user confirms the exact ticket:
return await createTicket({
subject: 'Investigate checkout timeout',
comment: 'Seen on mobile Safari.',
subdomain: 'acme',
confirm: true,
})
}Unwrapped REST:
import request from 'kody:@kody/zendesk/request'
export default async function main() {
return await request({
path: '/users/me',
subdomain: 'acme',
})
}Notes
- REST base:
https://{subdomain}.zendesk.com/api/v2 - Helpers project slim objects. Use
./requestwhen you need extra fields. ./search-ticketsprefixestype:ticketwhen the query does not already include it.- Creating an article needs a Help Center
sectionId. - This package is not affiliated with or endorsed by Zendesk, Inc.
Branding
The community icon is Zendesk's official Z symbol (the four-shape mark published
in Zendesk brand resources / Simple Icons). Paths are unmodified. Fill is
Zendesk Green Kelp #03363D. Zendesk® is a trademark of Zendesk, Inc.