Skip to main content
The Helpdesk API is the same ticketing that powers the Helpdesk tab in the dashboard. You can create tickets on behalf of customers, list and search them, and change a ticket’s status, assignee, or team. Each ticket carries a message thread you can read and reply to. You can also read the Attendance report and Activity timeline of the people who work those tickets. Tickets are numbered per AI agent. The ticketNumber path parameter is that per-agent number, not a global id. A ticket’s channel records where it originated, such as email, the chat widget, or WhatsApp. Tickets created through this API always have channel: "api".

Statuses

Each ticket has a status, and each status belongs to one of six fixed categories: new, on_you, on_customer, on_hold, closed, cancelled. The statuses themselves are configured per agent in the dashboard; every category has exactly one default status. Write endpoints accept a status in one of two forms, at most one per request: List ticket statuses returns the AI agent’s active statuses with their ids, categories, and labels. Each status has two labels: externalLabel is what the customer sees, internalLabel is what the dashboard shows.

Assignment and routing

When you create a ticket, the assignee fields decide whether auto-assignment runs. assigneeId and assigneeEmail are a mutually exclusive pair; sending both is a 400. Update a ticket never auto-assigns. Omitted fields keep their current value, assigneeId: null unassigns the ticket, and teamId: null clears the team. List teams returns the AI agent’s teams; exactly one is the default.

Messages

A ticket’s thread contains three message types: metadata.eventType names the kind of change. Changes made by a trigger carry triggerId, triggerName, and runId, and every event from the same trigger run shares its runId. Webhook events report deliveryStatus (pending, delivered, or failed) and the statusCode your endpoint returned. New event kinds may be added, so treat an unknown eventType as a generic change rather than an error. Add a message to a ticket currently accepts only type: "reply", attributed to a team member via authorId or authorEmail. The body is GitHub-flavored Markdown; raw HTML is stripped. Delivery to the customer is asynchronous, so a 201 means the reply was recorded, not that it reached the customer.

Attendance and activity

Two read-only endpoints return your operators’ Helpdesk Attendance report and Activity timeline. Use them to load your operators’ hours into your own reporting on a schedule, instead of exporting files by hand. Attendance and Activity explain what each value means. Both take the same query parameters: The values are typed rather than CSV text. Durations are whole minutes, percentages are numbers, and times are ISO 8601 instants in UTC. An operator in several teams is one attendance row with a teams array sorted by name, where the CSV repeats the row for each team. Both include only the AI agent’s current Helpdesk operators, the same as the dashboard. A day that hasn’t ended yet returns what has happened so far. Pull days that have ended when you want numbers that won’t change.

Activity segments

Activity segments are the raw timeline the Activity tab is built from, so their totals can differ slightly from the Activity export:
  • Two segments with 2 minutes or less between them stay separate. The dashboard joins them.
  • Times aren’t rounded.
  • A segment that crosses midnight isn’t split. It’s listed only in the range where it started.
For the dashboard’s totals, use timeLoggedMinutes and productiveTimeMinutes from the attendance rows, or the Activity export. A segment lasts at most 24 hours. When the same activity goes on longer, a new segment starts the moment the previous one ends, so a long stretch comes as several segments with no gap between them. A segment that is still open has endedAt: null, and its lastSeenAt is refreshed every few minutes while the Helpdesk is open, also when the operator is idle or the tab is in the background. So a daily pull of yesterday sees a segment that was still open at midnight without its end. Pull the last two days each time and keep the newest copy of each segment by id. A segment ends within about a day of its start, so the next day’s pull gets its end.

Access

The AI agent must have the Helpdesk turned on, or both endpoints return 403 HELPDESK_NOT_ENABLED. A key or connected app limited to certain permissions needs analytics:read, shown as Analytics on the consent screen. helpdesk_tickets:read doesn’t include it.

Rate limit

The two endpoints share a limit of 30 calls per 10 seconds per account. It counts every API key and connected app of the account together, and applies on top of the general rate limit. Over it, they return 429 RATE_LIMIT_TOO_MANY_REQUESTS with a Retry-After header that gives the seconds to wait. Reading one page at a time stays under the limit, at about 10 to 20 calls per 10 seconds. You only reach it by running several reads at once, or several tools against the same account.

Paging

These two lists differ from the others in two ways:
  • There is no pagination.total. Keep requesting until pagination.cursor is null.
  • A cursor only works with the startDate, endDate and timezone it came from. Changing any of them returns 400 VALIDATION_CURSOR_QUERY_MISMATCH.
Attendance rows go by day, then operator name. Activity segments go by start time, across all operators. If operators, shifts, time off or activity change between two calls, later pages reflect the change. Reading days that have ended avoids this.

Pull yesterday’s data every day

For a backfill, split the history into ranges of 31 days or less, such as calendar months, and read each range to its end with limit=1000. A year is 12 or 13 ranges per report. Reading two or three ranges at once is enough: more won’t finish sooner, because they share the rate limit of 30 calls per 10 seconds. On a 429, wait for Retry-After and send the same request again.

Endpoints

List tickets

Filterable, sortable, paginated list

Search tickets

Free-text search over ticket messages

Create a ticket

Open a ticket on behalf of a customer

Get a ticket

Retrieve a single ticket by number

Update a ticket

Change status, assignee, or team

List ticket messages

Read a ticket’s thread

Add a message to a ticket

Post an agent reply

List teams

The AI agent’s teams and the default

List ticket statuses

Configured statuses with ids and labels

List attendance rows

Each operator’s day against their shift

List activity segments

Ticket, untracked and idle time per operator

Error codes

Helpdesk-specific error codes beyond the standard authentication and rate-limiting errors:
CodeHTTPDescription
HELPDESK_NOT_ENABLED403The Helpdesk is not turned on for this AI agent.
VALIDATION_CURSOR_QUERY_MISMATCH400On attendance and activity, the cursor came from a request with a different startDate, endDate or timezone.
TICKET_NOT_FOUND404No ticket matches this number for the AI agent.
CONVERSATION_NOT_TAKEN_OVER409The ticket is linked to a live conversation that has not been taken over from the AI agent, so a human reply cannot be posted. Take over the conversation from the dashboard first.
MESSAGE_CONTENT_NOT_RENDERABLE422The message body rendered to empty HTML. This happens when it consists only of raw HTML, which is stripped. Send Markdown or plain text.
TICKET_INVALID_STATUS422statusId does not belong to a status for this AI agent.
TICKET_ARCHIVED_STATUS422statusId refers to an archived status, which cannot be applied.
TICKET_TEAM_MEMBER_NOT_FOUND422Neither assigneeId nor assigneeEmail resolved to a team member on this account.
TICKET_TEAM_NOT_FOUND422teamId does not belong to a team for this AI agent.
TEAM_MEMBER_NOT_FOUND422On message creation, neither authorId nor authorEmail resolved to a team member on this account.