REST API reference
Tickets
Changes run through the same code as the app: notifications, SLA clocks, automation rules and webhooks all fire.
Endpoints
- GET
/api/v1/tickets - GET
/api/v1/tickets/{ticket} - POST
/api/v1/tickets - PATCH
/api/v1/tickets/{ticket} - GET
/api/v1/tickets/{ticket}/replies - POST
/api/v1/tickets/{ticket}/replies - GET
/api/v1/tickets/{ticket}/notes - POST
/api/v1/tickets/{ticket}/notes - POST
/api/v1/tickets/{ticket}/spam - DELETE
/api/v1/tickets/{ticket}/spam - PATCH
/api/v1/tickets/{ticket}/metadata
Every request needs a bearer token: an API key or an OAuth access token. See authentication and scopes.
List tickets
/api/v1/ticketsList tickets the token's user can see.
- Scope
tickets:read- Unscoped key
- Read-only or read-write
Query parameters
statusstring- open, answered, on-hold or closed; comma-separate several.
prioritystring- low, normal, high, urgent or none; comma-separate several.
assignee_idstring- A user id, "me" or "unassigned".
department_idstring- Department id.
product_idinteger- Product id.
customer_idstring- Customer id.
customer_emailstring- The customer's exact email (case-insensitive). Works for every role, still limited to tickets the token's user can see.
tag_idstring- Tickets carrying this tag.
created_afterdatetime- ISO-8601 timestamp.
updated_afterdatetime- ISO-8601 timestamp; use it to sync changes.
searchstring- Case-insensitive match on the subject; a ticket number (1042 or #1042) also matches that ticket.
sortstring- created_at, -created_at, updated_at, -updated_at (default -created_at).
per_pageinteger- 1-100, default 25.
cursorstring- meta.next_cursor from the previous page.
curl https://zono.support/api/v1/tickets?status=open&priority=high,urgent&per_page=50 \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json"{
"data": [
{
"object": "ticket",
"id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"number": 1042,
"subject": "Cannot activate my license",
"description": "<p>My key says it is already in use.</p>",
"status": "answered",
"priority": "high",
"close_reason": null,
"close_reason_detail": null,
"customer": {
"id": "01j9x1c2d3e4f5g6h7j8k9m0n1",
"name": "Ada Lovelace",
"email": "[email protected]"
},
"assignee": {
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent",
"email": "[email protected]"
},
"department": {
"id": "01j9x0d1e2p3t4a5b6c7d8e9f0",
"name": "Billing"
},
"product": {
"id": 12,
"name": "Zono Desktop"
},
"channel": null,
"tags": [
{
"id": "01j9x0t1a2g3b4c5d6e7f8g9h0",
"name": "licensing",
"color": "#6366f1"
}
],
"custom_fields": {
"order_number": "A-1042"
},
"sla": null,
"triage": null,
"conversation_id": null,
"spam": null,
"merged_into": null,
"created_at": "2026-01-15T09:58:12Z",
"updated_at": "2026-01-15T10:24:07Z",
"last_reply_at": "2026-01-15T10:24:07Z",
"first_response_at": "2026-01-15T10:24:07Z",
"closed_at": null,
"url": "https://zono.support/tickets/01j9x2p3q4r5s6t7u8v9w0x1y2"
}
],
"links": {
"first": null,
"last": null,
"prev": null,
"next": "https://zono.support/api/v1/tickets?cursor=eyJpZCI6IjAxajl4MnAzcTQiLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9"
},
"meta": {
"path": "https://zono.support/api/v1/tickets",
"per_page": 25,
"next_cursor": "eyJpZCI6IjAxajl4MnAzcTQiLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9",
"prev_cursor": null
}
}Get a ticket
/api/v1/tickets/{ticket}One ticket with its customer, assignee, tags, custom fields and SLA summary.
- Scope
tickets:read- Unscoped key
- Read-only or read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
curl https://zono.support/api/v1/tickets/1042 \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json"{
"object": "ticket",
"id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"number": 1042,
"subject": "Cannot activate my license",
"description": "<p>My key says it is already in use.</p>",
"status": "answered",
"priority": "high",
"close_reason": null,
"close_reason_detail": null,
"customer": {
"id": "01j9x1c2d3e4f5g6h7j8k9m0n1",
"name": "Ada Lovelace",
"email": "[email protected]"
},
"assignee": {
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent",
"email": "[email protected]"
},
"department": {
"id": "01j9x0d1e2p3t4a5b6c7d8e9f0",
"name": "Billing"
},
"product": {
"id": 12,
"name": "Zono Desktop"
},
"channel": null,
"tags": [
{
"id": "01j9x0t1a2g3b4c5d6e7f8g9h0",
"name": "licensing",
"color": "#6366f1"
}
],
"custom_fields": {
"order_number": "A-1042"
},
"sla": null,
"triage": null,
"conversation_id": null,
"spam": null,
"merged_into": null,
"created_at": "2026-01-15T09:58:12Z",
"updated_at": "2026-01-15T10:24:07Z",
"last_reply_at": "2026-01-15T10:24:07Z",
"first_response_at": "2026-01-15T10:24:07Z",
"closed_at": null,
"url": "https://zono.support/tickets/01j9x2p3q4r5s6t7u8v9w0x1y2"
}Create a ticket
/api/v1/ticketsCreate a ticket for an existing customer, or for a customer found (or created) by email.
- Scope
tickets:write- Unscoped key
- Read-write
Body parameters
subjectstringRequired- Up to 255 characters.
descriptionstringRequired- The first message; HTML is kept.
product_idintegerRequired- One of the team's products (GET /products).
customer_idstring- Required unless customer is given.
customerobject- {"email", "name", "external_id"}: found by email or created. Admin and owner tokens that also carry the customers:write scope (others get 403); agents pass customer_id.
prioritystring- low, normal, high or urgent.
department_idstring- Department id.
assignee_idstring- A team member who may work the ticket.
tag_idsarray- Tag ids (GET /tags).
custom_fieldsobject- Values keyed by field key. Required fields must be set.
curl -X POST https://zono.support/api/v1/tickets \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"subject":"Cannot activate my license","description":"My key says it is already in use.","product_id":12,"customer":{"email":"[email protected]","name":"Ada Lovelace"},"priority":"high"}'{
"object": "ticket",
"id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"number": 1042,
"subject": "Cannot activate my license",
"description": "<p>My key says it is already in use.</p>",
"status": "answered",
"priority": "high",
"close_reason": null,
"close_reason_detail": null,
"customer": {
"id": "01j9x1c2d3e4f5g6h7j8k9m0n1",
"name": "Ada Lovelace",
"email": "[email protected]"
},
"assignee": {
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent",
"email": "[email protected]"
},
"department": {
"id": "01j9x0d1e2p3t4a5b6c7d8e9f0",
"name": "Billing"
},
"product": {
"id": 12,
"name": "Zono Desktop"
},
"channel": null,
"tags": [
{
"id": "01j9x0t1a2g3b4c5d6e7f8g9h0",
"name": "licensing",
"color": "#6366f1"
}
],
"custom_fields": {
"order_number": "A-1042"
},
"sla": null,
"triage": null,
"conversation_id": null,
"spam": null,
"merged_into": null,
"created_at": "2026-01-15T09:58:12Z",
"updated_at": "2026-01-15T10:24:07Z",
"last_reply_at": "2026-01-15T10:24:07Z",
"first_response_at": "2026-01-15T10:24:07Z",
"closed_at": null,
"url": "https://zono.support/tickets/01j9x2p3q4r5s6t7u8v9w0x1y2"
}Update a ticket
/api/v1/tickets/{ticket}Change a ticket. Only the fields you send change. Merged tickets answer 409.
- Scope
tickets:write- Unscoped key
- Read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Body parameters
subjectstring- New subject.
descriptionstring- New description.
statusstring- open, answered, on-hold or closed. Closing needs every required custom field.
close_reasonstring|null- With status closed: resolved (the default), not_resolved or spam.
close_reason_detailstring|null- With close_reason not_resolved: duplicate, no_reply, out_of_scope.
prioritystring|null- low, normal, high, urgent or null.
assignee_idstring|null- A team member, or null to unassign.
department_idstring|null- Department id or null.
product_idinteger- Product id.
tag_idsarray- Replaces the ticket's tags.
custom_fieldsobject- Values keyed by field key; null clears an optional field.
curl -X PATCH https://zono.support/api/v1/tickets/1042 \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"status":"on-hold","priority":"urgent","custom_fields":{"order_number":"A-1042"}}'{
"object": "ticket",
"id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"number": 1042,
"subject": "Cannot activate my license",
"description": "<p>My key says it is already in use.</p>",
"status": "answered",
"priority": "high",
"close_reason": null,
"close_reason_detail": null,
"customer": {
"id": "01j9x1c2d3e4f5g6h7j8k9m0n1",
"name": "Ada Lovelace",
"email": "[email protected]"
},
"assignee": {
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent",
"email": "[email protected]"
},
"department": {
"id": "01j9x0d1e2p3t4a5b6c7d8e9f0",
"name": "Billing"
},
"product": {
"id": 12,
"name": "Zono Desktop"
},
"channel": null,
"tags": [
{
"id": "01j9x0t1a2g3b4c5d6e7f8g9h0",
"name": "licensing",
"color": "#6366f1"
}
],
"custom_fields": {
"order_number": "A-1042"
},
"sla": null,
"triage": null,
"conversation_id": null,
"spam": null,
"merged_into": null,
"created_at": "2026-01-15T09:58:12Z",
"updated_at": "2026-01-15T10:24:07Z",
"last_reply_at": "2026-01-15T10:24:07Z",
"first_response_at": "2026-01-15T10:24:07Z",
"closed_at": null,
"url": "https://zono.support/tickets/01j9x2p3q4r5s6t7u8v9w0x1y2"
}List a ticket's replies
/api/v1/tickets/{ticket}/repliesThe ticket's replies, oldest first unless sorted, internal replies included.
- Scope
tickets:read- Unscoped key
- Read-only or read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Query parameters
sortstring- created_at (oldest first, default) or -created_at (newest first).
per_pageinteger- 1-100, default 25.
cursorstring- meta.next_cursor from the previous page.
curl https://zono.support/api/v1/tickets/1042/replies \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json"{
"data": [
{
"object": "reply",
"id": "01j9x3m4k7d2c8b5a6f0e1g2h4",
"ticket_id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"body": "<p>Thanks Ada, a new key is on its way.</p>",
"is_internal": false,
"source": "web",
"author": {
"type": "user",
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent"
},
"created_at": "2026-01-15T10:24:07Z"
}
],
"links": {
"first": null,
"last": null,
"prev": null,
"next": "https://zono.support/api/v1/tickets/1042/replies?cursor=eyJpZCI6IjAxajl4MnAzcTQiLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9"
},
"meta": {
"path": "https://zono.support/api/v1/tickets/1042/replies",
"per_page": 25,
"next_cursor": "eyJpZCI6IjAxajl4MnAzcTQiLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9",
"prev_cursor": null
}
}Reply to a ticket
/api/v1/tickets/{ticket}/repliesReply as the token's user. Public replies are sent to the customer on the ticket's channel.
- Scope
tickets:write- Unscoped key
- Read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Body parameters
bodystringRequired- The reply; HTML is kept.
is_internalboolean- Staff-only reply (default false).
close_ticketboolean- Close the ticket with this reply (default false).
curl -X POST https://zono.support/api/v1/tickets/1042/replies \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"body":"Thanks Ada, a new key is on its way.","close_ticket":true}'{
"object": "reply",
"id": "01j9x3m4k7d2c8b5a6f0e1g2h4",
"ticket_id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"body": "<p>Thanks Ada, a new key is on its way.</p>",
"is_internal": false,
"source": "web",
"author": {
"type": "user",
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent"
},
"created_at": "2026-01-15T10:24:07Z"
}List a ticket's notes
/api/v1/tickets/{ticket}/notesThe ticket's internal notes, oldest first unless sorted.
- Scope
tickets:read- Unscoped key
- Read-only or read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Query parameters
sortstring- created_at (oldest first, default) or -created_at (newest first).
per_pageinteger- 1-100, default 25.
cursorstring- meta.next_cursor from the previous page.
curl https://zono.support/api/v1/tickets/1042/notes \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json"{
"data": [
{
"object": "note",
"id": "01j9x3n4o5t6e7a8b9c0d1e2f3",
"ticket_id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"body": "Refund approved by finance.",
"author": {
"type": "user",
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent"
},
"created_at": "2026-01-15T10:24:07Z",
"updated_at": "2026-01-15T10:24:07Z"
}
],
"links": {
"first": null,
"last": null,
"prev": null,
"next": "https://zono.support/api/v1/tickets/1042/notes?cursor=eyJpZCI6IjAxajl4MnAzcTQiLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9"
},
"meta": {
"path": "https://zono.support/api/v1/tickets/1042/notes",
"per_page": 25,
"next_cursor": "eyJpZCI6IjAxajl4MnAzcTQiLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9",
"prev_cursor": null
}
}Add a note to a ticket
/api/v1/tickets/{ticket}/notesAdd an internal note as the token's user.
- Scope
tickets:write- Unscoped key
- Read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Body parameters
bodystringRequired- The note.
curl -X POST https://zono.support/api/v1/tickets/1042/notes \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"body":"Refund approved by finance."}'{
"object": "note",
"id": "01j9x3n4o5t6e7a8b9c0d1e2f3",
"ticket_id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"body": "Refund approved by finance.",
"author": {
"type": "user",
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent"
},
"created_at": "2026-01-15T10:24:07Z",
"updated_at": "2026-01-15T10:24:07Z"
}Mark a ticket as spam
/api/v1/tickets/{ticket}/spamMark the ticket as spam, as the ticket page does: it leaves the app's ticket lists, its SLA clocks and survey stop, and it is deleted for good after 30 days unless moved out of spam (the ticket's spam field shows when). Marking a spam ticket again changes nothing. meta tells how many tickets were marked and what was blocked or skipped. Merged tickets answer 409.
- Scope
tickets:write- Unscoped key
- Read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Body parameters
blockarray- Also block the sender by any of: email, domain (not free email providers), ip (live chat visitors), customer. Types the sender has no value for are skipped and listed in meta.skipped.
include_other_openboolean- Also mark the customer's other open tickets (those you can access) as spam.
curl -X POST https://zono.support/api/v1/tickets/1042/spam \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"block":["email"]}'{
"object": "ticket",
"id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"number": 1042,
"subject": "Cannot activate my license",
"description": "<p>My key says it is already in use.</p>",
"status": "answered",
"priority": "high",
"close_reason": null,
"close_reason_detail": null,
"customer": {
"id": "01j9x1c2d3e4f5g6h7j8k9m0n1",
"name": "Ada Lovelace",
"email": "[email protected]"
},
"assignee": {
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent",
"email": "[email protected]"
},
"department": {
"id": "01j9x0d1e2p3t4a5b6c7d8e9f0",
"name": "Billing"
},
"product": {
"id": 12,
"name": "Zono Desktop"
},
"channel": null,
"tags": [
{
"id": "01j9x0t1a2g3b4c5d6e7f8g9h0",
"name": "licensing",
"color": "#6366f1"
}
],
"custom_fields": {
"order_number": "A-1042"
},
"sla": null,
"triage": null,
"conversation_id": null,
"spam": null,
"merged_into": null,
"created_at": "2026-01-15T09:58:12Z",
"updated_at": "2026-01-15T10:24:07Z",
"last_reply_at": "2026-01-15T10:24:07Z",
"first_response_at": "2026-01-15T10:24:07Z",
"closed_at": null,
"url": "https://zono.support/tickets/01j9x2p3q4r5s6t7u8v9w0x1y2"
}Move a ticket out of spam
/api/v1/tickets/{ticket}/spamMove the ticket out of spam: it is back in the app's lists and its SLA clocks resume. meta.unmarked is false when it was not spam.
- Scope
tickets:write- Unscoped key
- Read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Body parameters
remove_blocksboolean- Also lift the blocks that match the sender. Owners and admins only (403 otherwise).
curl -X DELETE https://zono.support/api/v1/tickets/1042/spam \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"remove_blocks":true}'{
"object": "ticket",
"id": "01j9x2p3q4r5s6t7u8v9w0x1y2",
"number": 1042,
"subject": "Cannot activate my license",
"description": "<p>My key says it is already in use.</p>",
"status": "answered",
"priority": "high",
"close_reason": null,
"close_reason_detail": null,
"customer": {
"id": "01j9x1c2d3e4f5g6h7j8k9m0n1",
"name": "Ada Lovelace",
"email": "[email protected]"
},
"assignee": {
"id": "01j9x0u1v2w3x4y5z6a7b8c9d0",
"name": "Sam Agent",
"email": "[email protected]"
},
"department": {
"id": "01j9x0d1e2p3t4a5b6c7d8e9f0",
"name": "Billing"
},
"product": {
"id": 12,
"name": "Zono Desktop"
},
"channel": null,
"tags": [
{
"id": "01j9x0t1a2g3b4c5d6e7f8g9h0",
"name": "licensing",
"color": "#6366f1"
}
],
"custom_fields": {
"order_number": "A-1042"
},
"sla": null,
"triage": null,
"conversation_id": null,
"spam": null,
"merged_into": null,
"created_at": "2026-01-15T09:58:12Z",
"updated_at": "2026-01-15T10:24:07Z",
"last_reply_at": "2026-01-15T10:24:07Z",
"first_response_at": "2026-01-15T10:24:07Z",
"closed_at": null,
"url": "https://zono.support/tickets/01j9x2p3q4r5s6t7u8v9w0x1y2"
}Set a ticket's metadata
/api/v1/tickets/{ticket}/metadataSet metadata about the ticket, shown to your team under From your app. A ticket that came from a chat keeps it on its conversation, so the chat shows the same values. Keys are 1 to 40 lowercase letters, digits and underscores (zono_ is reserved; keys naming passwords, secrets, tokens, cards, CVV, SSN or IBAN are refused). Values are strings of at most 255 characters (ISO dates as strings), numbers or booleans; null deletes the key. At most 30 keys are kept and 4 KB sent per request; the latest write of a key wins. Strings that look like a card number or an IBAN are stored as [removed]. Values written here are marked Signed in the inbox and are never sent to the AI agent.
- Scope
tickets:write- Unscoped key
- Read-write
Path parameters
ticketstringRequired- Ticket ULID or ticket number.
Body parameters
metadataobjectRequired- Keys and values to set; null removes a key.
curl -X PATCH https://zono.support/api/v1/tickets/1042/metadata \
-H "Authorization: Bearer $ZONO_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"metadata":{"order_id":"A-1042","refund_eligible":true}}'{
"object": "conversation",
"id": "01j9x0a1b2c3d4e5f6g7h8j9k0",
"metadata": {}
}