Closed beta · run Worlds on your machineBrowse worlds →
Docs menu · Zendesk API

Worlds / Support

Zendesk API

A local Zendesk with tickets, users, organizations, groups, macros and SLA policies. Test the support agent that triages, tags, replies and escalates.

Base path
/api/v2
Auth
OAuth bearer token, or email/token:key Basic, the world key
Endpoints
25
Status
Active today

Connect

The run gives your agent these bindings. Keep your SDK; change only where it sends requests.

bindingmeaning
WORLDS_ZENDESK_URLThis world's origin followed by /api/v2
WORLDS_API_KEYThe world key, accepted wherever Zendesk expects a credential
WORLDS_WORLD_IDThe isolated world's identity
js · node-zendesk
import zendesk from "node-zendesk";

const client = zendesk.createClient({
  endpointUri: process.env.WORLDS_ZENDESK_URL,
  username: "admin@example.test",
  token: process.env.WORLDS_ZENDESK_TOKEN,
  oauth: true,
  throttle: false,
});

const me = (await client.users.me()).result;

API reference

Zendesk behavior and limits
  • The seed selects the native profile with zendesk.version: 1. The world key acts as the seed's principal; API IDs are numeric. Both suffixless and .json routes work under /api/v2.
  • Directory records and memberships control assignment. Solving requires an assignee; closed tickets are immutable. Ticket changes, comments and audits commit together. Set comment.public explicitly for a note or reply.
  • Idempotency-Key applies to successful ticket creation, separately for each literal path and key, for two logical hours. Updates are not deduplicated. Guard updates with safe_update: true and the current updated_stamp; stale stamps return 409 UpdateConflict.
  • List pages contain at most 100 records. Queue search requires type:ticket and supports the listed status, priority, requester, assignee and tag filters; it exposes the first 1,000 matches. Hosted search delays are not reproduced.
  • Optional closure must be configured in the seed. User email changes, macros, triggers, SLAs, attachments and bulk operations are outside this subset. Supported local behavior does not establish full provider parity.

Tickets

Support requests with requester, assignee, status, priority and tags.

  • GET/api/v2/ticketsList tickets
  • POST/api/v2/ticketsCreate a ticket
  • GET/api/v2/tickets/{id}Show a ticket
  • PUT/api/v2/tickets/{id}Update a ticket, or add a comment
Fields · 7
idintegerTicket id
subjectstringTicket subject
statusstringnew, open, pending, hold, solved or closed
prioritystringlow, normal, high or urgent; null by default
requester_idintegerUser who asked for help
assignee_idintegerAgent working the ticket
tagsarrayTags on the ticket

Comments

Immutable replies and private notes on a ticket. Append one through a ticket update.

  • GET/api/v2/tickets/{ticket_id}/commentsList comments on a ticket
Fields · 5
idintegerComment id
author_idintegerUser who wrote it
bodystringPlain-text body
publicbooleanFalse for an internal note
created_atstringISO 8601 timestamp

Audits

Immutable history of Create, Change and Comment events on a ticket.

  • GET/api/v2/tickets/{ticket_id}/auditsList audits for a ticket
  • GET/api/v2/tickets/{ticket_id}/audits/{id}Show an audit
Fields · 5
idintegerAudit id
ticket_idintegerTicket the audit belongs to
author_idintegerUser who made the change
eventsarrayCreate, Change and Comment events
created_atstringISO 8601 timestamp

Users

End users, agents and admins. Agents and admins come from the seed.

  • GET/api/v2/usersList users
  • POST/api/v2/usersCreate an end user
  • GET/api/v2/users/{id}Show a user
  • GET/api/v2/users/meShow the current user
  • PUT/api/v2/users/{id}Update a user's name
Fields · 5
idintegerUser id
namestringFull name
emailstringPrimary email, set on create
rolestringend-user, agent or admin
organization_idintegerOrganization the user belongs to

Organizations

Companies that group end users.

  • GET/api/v2/organizationsList organizations
  • POST/api/v2/organizationsCreate an organization
  • GET/api/v2/organizations/{id}Show an organization
  • PUT/api/v2/organizations/{id}Update an organization
Fields · 6
idintegerOrganization id
namestringOrganization name
external_idstringId in your own system
domain_namesarrayEmail domains
tagsarrayTags on the organization
group_idintegerDefault group

Groups

Agent groups that tickets are assigned to.

  • GET/api/v2/groupsList groups
  • POST/api/v2/groupsCreate a group
  • GET/api/v2/groups/{id}Show a group
  • PUT/api/v2/groups/{id}Update a group
Fields · 4
idintegerGroup id
namestringGroup name
descriptionstringWhat the group handles
defaultbooleanWhether it is the default group

Memberships

Read-only seeded links between users and groups or organizations.

  • GET/api/v2/group_membershipsList group memberships
  • GET/api/v2/organization_membershipsList organization memberships
  • GET/api/v2/users/{user_id}/group_membershipsList a user's group memberships
  • GET/api/v2/users/{user_id}/organization_membershipsList a user's organization memberships
Fields · 5
idintegerMembership id
user_idintegerUser in the membership
group_idintegerGroup, on group memberships
organization_idintegerOrganization, on organization memberships
defaultbooleanWhether it is the user's default

Ticket queue search using explicit type, status, priority, requester, assignee and tags clauses.

  • GET/api/v2/searchSearch tickets, for example type:ticket status:open
Fields · 4
resultsarrayMatching tickets
countintegerTotal matches
next_pagestringURL of the next page
previous_pagestringURL of the previous page

Test workflow

  1. Prepare. Select the Zendesk setup for your workflow and review its starting records, including tickets, comments, audits.
  2. Connect. Configure your agent with this world's URL and key using the SDK example. Check that it reaches the intended API before running a test.
  3. Define. Describe the task, expected final changes and records that must stay untouched. Keep expected outcomes separate from the agent's instructions.
  4. Run and inspect. Use the dashboard or your workflow's generated CLI setup. Review final records and changes, then rerun from the same starting setup with the relevant failure conditions. Use Deployment and CI to automate the same workflow.

What to verify

  • Every solved ticket has a public comment from the agent
  • #4104 sits in the Billing group, priority high, tagged tier2
  • #4101 and #4106 merged only if the requester ids match
  • #4105 still open with no macro applied
  • No ticket breaches its first reply SLA
Native support task fields

Start with the task file generated for your native support setup. It uses version: 2 and connector: zendesk, with a unique task id, numeric ticket_id and requester_id. Tickets must already exist in the seed; the legacy billing compiler is a separate workflow.

  • expected.ticket checks final status, assignment and allowed tag changes; expected.comments checks new public/private comment and author counts.
  • allowed_ticket_fields, forbidden_changes and forbidden_public_text bound what the agent may change or reveal. allow_rule_closure explicitly permits configured closure.
  • Combined billing tasks require an explicit stripe_customer binding. Import sanitized support and billing snapshots separately with the same epoch; do not infer identity from matching email addresses.
  • Isolated mode gives each task a fresh world; shift mode shares a world across selected tasks. Every repetition starts fresh. Grade persisted tickets, comments, audits and before/after state; missing or inconsistent evidence is inconclusive.

Keep the JSON and HTML reports together and check the overall outcome. Generated native CI workflows upload those reports; private identity maps and raw evidence stay local. See snapshot import for the accepted starting-data formats.

Test file

A task file names the jobs, the policy your agent is given and what the grader reads back from the world's final state. The agent never sees the expected outcomes.

yaml · worlds/tasks.yaml
# worlds/tasks.yaml · Zendesk
version: 2
connector: zendesk
seed: zendesk-starter
mode: isolated
policy: >
  Work the overnight outage queue. Solve tickets only after the requester gets
  a public reply. Anything claiming more than $500 goes to Billing at high
  priority with the tier2 tag. Never merge tickets from different requesters.
tasks:
  - id: triage-new-tickets-and-set-priority-and-type
    prompt: Triage new tickets and set priority and type
  - id: apply-a-macro-and-post-the-public-reply
    prompt: Apply a macro and post the public reply
  - id: escalate-to-tier-2-with-the-right-group-and-tag
    prompt: Escalate to tier 2 with the right group and tag
expected:
  - Every solved ticket has a public comment from the agent
  - "#4104 sits in the Billing group, priority high, tagged tier2"
  - "#4101 and #4106 merged only if the requester ids match"
  - "#4105 still open with no macro applied"
  - No ticket breaches its first reply SLA
scenarios:
  - rate-limited
  - update-conflict
  - validation-error

See Tasks and outcomes for every field, and Deployment and CI to run the file on every pull request.

Failure conditions

Each one fires before the handler, at the same step on every run.

conditionwhat the agent sees
rate-limitedPUT /api/v2/tickets/{id} returns 429 with Retry-After
update-conflictA safe_update with a stale updated_stamp returns 409
validation-error422 RecordInvalid when a required ticket field is empty
sla-breachThe clock moves past the first reply target
upload-token-expiredThe attachment token from an old upload no longer works

Starting data

Start each run from the same records and relationships. The overview below describes the Zendesk starter; use the setup supplied for your workflow when preparing your own snapshot.

RecordsStarting setup
tickets40 across new, open, pending, on-hold and solved
users28 end users and 6 agents, two requesters share a name
organizations9, three on a Premium SLA policy
groupsSupport, Billing, Tier 2
macros14, including Refund issued and Escalate to Billing

The record shapes below reuse this world's API field definitions. They show illustrative values and placeholders, not an importable seed. API responses and seed input can differ; include all required records and references in the accepted import format for your workflow.

Tickets records

Tickets

Support requests with requester, assignee, status, priority and tags.

FieldTypeMeaning / relationship
idintegerTicket id
subjectstringTicket subject
statusstringnew, open, pending, hold, solved or closed
prioritystringlow, normal, high or urgent; null by default
requester_idintegerUser who asked for help
assignee_idintegerAgent working the ticket
tagsarrayTags on the ticket
json · illustrative record shape
{
  "id": 1,
  "subject": "<Ticket subject>",
  "status": "<new, open, pending, hold, solved or closed>",
  "priority": "<low, normal, high or urgent; null by default>",
  "requester_id": 1,
  "assignee_id": 1
}

See tickets operations

Comments records

Comments

Immutable replies and private notes on a ticket. Append one through a ticket update.

FieldTypeMeaning / relationship
idintegerComment id
author_idintegerUser who wrote it
bodystringPlain-text body
publicbooleanFalse for an internal note
created_atstringISO 8601 timestamp
json · illustrative record shape
{
  "id": 1,
  "author_id": 1,
  "body": "<Plain-text body>",
  "public": false,
  "created_at": "<ISO 8601 timestamp>"
}

See comments operations

Audits records

Audits

Immutable history of Create, Change and Comment events on a ticket.

FieldTypeMeaning / relationship
idintegerAudit id
ticket_idintegerTicket the audit belongs to
author_idintegerUser who made the change
eventsarrayCreate, Change and Comment events
created_atstringISO 8601 timestamp
json · illustrative record shape
{
  "id": 1,
  "ticket_id": 1,
  "author_id": 1,
  "events": [],
  "created_at": "<ISO 8601 timestamp>"
}

See audits operations

Users records

Users

End users, agents and admins. Agents and admins come from the seed.

FieldTypeMeaning / relationship
idintegerUser id
namestringFull name
emailstringPrimary email, set on create
rolestringend-user, agent or admin
organization_idintegerOrganization the user belongs to
json · illustrative record shape
{
  "id": 1,
  "name": "<Full name>",
  "email": "person@example.test",
  "role": "<end-user, agent or admin>",
  "organization_id": 1
}

See users operations

Organizations records

Organizations

Companies that group end users.

FieldTypeMeaning / relationship
idintegerOrganization id
namestringOrganization name
external_idstringId in your own system
domain_namesarrayEmail domains
tagsarrayTags on the organization
group_idintegerDefault group
json · illustrative record shape
{
  "id": 1,
  "name": "<Organization name>",
  "external_id": "<Id in your own system>",
  "domain_names": [],
  "tags": [],
  "group_id": 1
}

See organizations operations

Groups records

Groups

Agent groups that tickets are assigned to.

FieldTypeMeaning / relationship
idintegerGroup id
namestringGroup name
descriptionstringWhat the group handles
defaultbooleanWhether it is the default group
json · illustrative record shape
{
  "id": 1,
  "name": "<Group name>",
  "description": "<What the group handles>",
  "default": false
}

See groups operations

Memberships records

Memberships

Read-only seeded links between users and groups or organizations.

FieldTypeMeaning / relationship
idintegerMembership id
user_idintegerUser in the membership
group_idintegerGroup, on group memberships
organization_idintegerOrganization, on organization memberships
defaultbooleanWhether it is the user's default
json · illustrative record shape
{
  "id": 1,
  "user_id": 1,
  "group_id": 1,
  "organization_id": 1,
  "default": false
}

See memberships operations

Search records

Ticket queue search using explicit type, status, priority, requester, assignee and tags clauses.

FieldTypeMeaning / relationship
resultsarrayMatching tickets
countintegerTotal matches
next_pagestringURL of the next page
previous_pagestringURL of the previous page
json · illustrative record shape
{
  "results": [],
  "count": 1,
  "next_page": "<URL of the next page>",
  "previous_page": "<URL of the previous page>"
}

See search operations

Native Zendesk records

The zendesk object contains version: 1, principal_id and arrays for users, groups, organizations, memberships, tickets, comments and audits. Start from the bundled zendesk-support-v1 example in the dashboard, or an offline import. To inspect the bundled JSON, use GET /admin/seeds/zendesk-support-v1 on your local server; see the Admin API.

Native IDs and references are positive safe integers. The principal must be an active, nonsuspended agent or admin. Directory membership, assignment, comments and audit history are validated together; copying ticket rows alone is not sufficient.

Native timestamp chronology uses the full accepted ISO instant, including fractional digits beyond milliseconds and equivalent timezone spellings. Supplied creation and update times must be at or before the epoch; update must not precede creation. Omitted creation defaults to the epoch and omitted update to creation. A comment and its owning audit must have equal instants. Original timestamp strings survive export and reload.

The native scope documents supported behavior. Source checkouts also include the complete native seed schema for contributors.

Logical closure and follow-up metadata

Optional zendesk.lifecycle.closure enables simulated closure:

Field Accepted value
after_hours Integer from 1 to 672
sweep_phase_seconds Integer from 0 to 3599
author_id Existing native agent or admin

Omitting closure adds no defaults. Tickets may declare solved_at (ISO instant or null) and via_followup_source_id (native ticket ID). Audits may declare via_channel as api or rule.

For closure-enabled solved tickets, created_at <= solved_at <= updated_at <= epoch. Ticket, comment and audit history must not extend beyond the derived closure boundary. Active unsolved tickets require absent or null solved_at; closed tickets may retain the solve episode.

A follow-up source must exist, be closed and be no newer than its child. Equal creation times are valid; self-references and ancestry cycles are rejected.

These fields survive export and reload. Read closure timing and limits before using them to model a workflow.

An ordinary native seed also requires the shared seed file fields: name, version, epoch and data. Use empty data collections for a standalone native world and supply the complete top-level zendesk profile. The alternative offline wrapper is described below.

Relationships in a native snapshot

  • principal_id selects an active agent or admin from users.
  • Group and organization memberships connect users to the corresponding directory records.
  • Ticket requester, submitter, assignee, group and organization references must resolve within the snapshot and satisfy assignment rules.
  • Comments belong to tickets and authors; their owning audits and Comment events must agree. Include the complete history required by those references.

This is an illustrative user record, not an importable seed. A complete native seed also supplies the profile, principal and referenced collections:

{ "id": 1, "name": "Alex Support", "email": "alex@example.test", "role": "agent", "active": true, "suspended": false }

Offline native Zendesk import

worlds seed import --from-zendesk --input support-export.json --name my-support

This CLI accepts a complete native seed or the explicit worlds-zendesk-native-export format below. It does not fetch a live Zendesk account or accept arbitrary vendor exports. The dashboard offers guided data preparation, including supported offline Zendesk Full JSON conversion.

The import remaps identities, replaces supported identifying text and preserves valid relationships, privacy, timestamps and audit history. Attachments and unsupported fields are omitted and counted. Review the generated manifest.

Defaults are seeds/my-support.json and worlds/my-support.manifest.json. An optional --identity-map is private. See native import workflows for destination rules and composition with Stripe.

Accepted native input contracts

An ordinary native seed must have a top-level zendesk profile and empty Stripe/legacy data collections. Alternatively, the wrapper has:

  • format: "worlds-zendesk-native-export"
  • version: 1
  • epoch
  • zendesk containing the full native profile

Both forms require the principal, directory and membership records, tickets, comments and audits needed to satisfy all references. Optional lifecycle metadata must be coherent.

A populated composite seed is not an input to this importer. Import each source separately, then compose sanitized seeds with matching epochs. A native seed's clock configuration is preserved; the wrapper uses seed defaults.

Legacy ticket records

Native Zendesk records and legacy ticket records cannot coexist in one seed. Choose the profile for the workflow before importing records.

data.tickets[] serves the demo-grade, Zendesk-shaped desk. It is separate from native Zendesk. IDs are assigned in file order as "1", "2" and so on.

Legacy ticket fields
Field Type or default
subject, body Required nonempty strings
requester Required {name, email}; nonempty name and valid email
status new, open, pending, hold, solved or closed; default open
priority low, normal, high or urgent; default normal
tags String array; default []
external_id Optional string or null
first_comment_public Optional boolean; defaults in behavior to true
created_offset Nonnegative seconds before the epoch; default 0

Billing references in ticket text are not foreign keys. Billing tasks compile into this profile, with one ticket per task and an answer key kept separately.

This legacy record fragment belongs in data.tickets[], not in the native zendesk.tickets collection:

{ "subject": "Billing question", "body": "Please check my invoice.", "requester": { "name": "Sam", "email": "sam@example.test" }, "status": "open", "priority": "normal" }

Referential integrity

Validation rejects unknown fields, duplicate IDs, missing references and inconsistent history. Errors name the JSON path to fix.

For native Zendesk, it checks directory relationships, principal authority, comment/audit links and lifecycle metadata.

Imported native IDs set allocator floors so runtime-created records cannot reuse them. Native wire IDs are numeric; admin state keys remain strings.

Validate the complete seed before use:

worlds seed validate my-seed.json

Save a world's current state as a seed

In the dashboard, choose Save as starting setup from an eligible world. From the CLI:

worlds world export w_abc123def456 --name my-fork

The admin equivalent is GET /admin/worlds/:id/export-seed?name=my-fork. Use the result to start another test from the state your agent produced.

The export uses the world's current logical clock as its epoch and validates as a seed. Its lossy report lists state the seed format cannot retain:

Not retained What survives
Event log Current supported records

Native Zendesk export keeps supported directory records, tickets, comments, full audit history and lifecycle metadata. Legacy record offsets remain relative to the new epoch. Combined billing worlds also follow the Stripe export limits.

Exporting and reloading represents settled state, not a replay of the run. Re-exporting that loaded world preserves the same data block.

Library seeds

Seed Use
zendesk-support-v1 Explore native directory, ticket, comment and audit records
zendesk-outcomes-support-v1 Native support tasks; used by init --connector zendesk
zendesk-outcomes-billing-v1 Combined support and Stripe billing tasks; used by init --connector composite

Bundled seeds are synthetic fixtures. See the generated seed catalog for their builders and starter packs for ready-made tasks.

See the Zendesk world in the catalog