List tickets API
This article and the rest of the API documentation in this section are written for a technical audience — integrators and developers connecting external systems to Tickiti. Familiarity with HTTP, REST, JSON and bearer-token authentication is assumed.
The list endpoint returns the ticket list as Tickiti itself renders it — through a perspective. A perspective is a saved view (its filters, conditions and ordering) configured in the Tickiti interface; the API runs the same query the staff ticket browser does, so the results, ordering and permission filtering match exactly what the acting user would see on screen.
Use it to mirror a Tickiti queue into an external dashboard, to poll for tickets matching a saved filter, to look tickets up by number, subject or participant, or to reconcile state between Tickiti and another system.
Endpoint
POST /api/v1/tickets/queryHeaders
Authorization: Bearer YOUR_TOKEN— required. Token must have thetickets:readability. Atickets:writetoken also works (write implies read).Content-Type: application/json— required. NoIdempotency-Keyis needed (this is a read).
Body
All fields are optional. With an empty body the endpoint returns the built-in All perspective.
perspective_id— the numeric id of the perspective to list. This is the most direct selector when you are not searching.search_object.criteria— ad-hoc search filters; see Searching with criteria.search_object.search_perspective_idorsearch_object.search_perspective— the perspective a criteria search runs within, by id or by name (e.g."All"). Defaults to All.row_limit— the maximum number of rows to return. Defaults to100. Send it at the top level of the body; arow_limitinsidesearch_objectis used when there is none at the top level.
Tickiti chooses the perspective in this order:
- A token whose owner is not a staff account, and a lone ticket-number lookup (see below), always use All.
- When
search_object.criteriaholds at least one criterion with tokens, the search runs withinsearch_perspective_id, elsesearch_perspective, else All. - Otherwise
perspective_idis listed; with noperspective_id, All.
Results are always scoped by the permissions of the staff user the token acts as: a ticket is included only if that user can view its queue, is a participant on it, or belongs to the team it is assigned to. This means a token can never read tickets its owner could not read in the interface.
Searching with criteria
Pass search_object.criteria to apply ad-hoc filters — the same ones the ticket browser’s search bar builds. Each entry is a { "mode": ..., "tokens": [...], "match": ... } object: mode names the field to match, tokens is an array of values, and the optional match ("all" or "any") says how several tokens combine. A criterion with no tokens is ignored.
- Across criteria: criteria that share a mode are combined with OR; criteria of different modes are combined with AND. So
[{"mode":"status","tokens":["open"]},{"mode":"queue","tokens":["Sales"]}]reads “open tickets in the Sales queue”. - Within one criterion — text modes (
subject,content,subject_content): every token must match, unless the criterion has"match": "any", in which case any one token is enough.{"mode":"subject","tokens":["Vapor","plate"]}finds subjects containing both words. - Within one criterion — single-value modes (
assigned,participant,priority,raised,queue,status,watchlist,ticket_number,hashtag): a ticket matches when it matches any of the tokens.{"mode":"ticket_number","tokens":["220009","220014"]}returns exactly those two tickets, and numbers that match nothing return nothing. - Date modes take a single
YYYY-MM-DDtoken and compare as UTC.
The modes:
| Mode | Matches |
|---|---|
subject | Ticket subject contains the token (substring). |
content | Any response body or tag comment contains the token. |
subject_content | Subject or any response body contains the token. |
assigned | The assignee: a staff email (which also matches tickets assigned to that person’s teams), a team key team:<id> (the team’s own tickets), team-and-members:<id> (the team’s tickets and those assigned to any member personally), or Unassigned. |
participant | A participant email contains the token. |
priority | Priority code — 10, 20, 30 or 40 (exact). |
raised | Originator (raiser) email contains the token. |
queue | Queue name (exact). |
status | Ticket status — open, closed, on-hold, … (exact). |
watchlist | Id of a watchlist the ticket is on. |
hashtag | Ticket carries the hashtag (a leading # is optional). |
ticket_number | 6-digit ticket number (exact; a leading # is optional). |
created_from | Ticket raised on or after a YYYY-MM-DD date. |
created_to | Ticket raised on or before a YYYY-MM-DD date. |
updated_from | Last activity on or after a YYYY-MM-DD date. |
updated_to | Last activity on or before a YYYY-MM-DD date. |
An unknown mode is refused with 422, and the message lists the valid modes. POST /api/v1/tickets/search-modes returns the same vocabulary and the combination rules at runtime, so a client can discover them rather than hard-coding the list — see the Ticket operations API. Team keys for assigned come from POST /api/v1/tickets/teams.
A lone criterion with a single 6-digit token — in ticket_number mode, or in any mode when written as #220009 — is a direct ticket lookup: it searches the All perspective for that number, whichever perspective the request names.
Example
List a saved perspective:
curl -X POST https://support.sole-provider.example/api/v1/tickets/query \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"perspective_id": 3
}'Or search with criteria — open Sales tickets, up to 50 rows:
curl -X POST https://support.sole-provider.example/api/v1/tickets/query \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"row_limit": 50,
"search_object": {
"criteria": [
{ "mode": "status", "tokens": ["open"] },
{ "mode": "queue", "tokens": ["Sales"] }
]
}
}'Tickets whose subject mentions either of two quote references, in the Sales or Returns queue:
{
"search_object": {
"search_perspective": "All",
"criteria": [
{ "mode": "subject", "tokens": ["Q-10068", "Q-10015"], "match": "any" },
{ "mode": "queue", "tokens": ["Sales", "Returns"] }
]
}
}Successful response
A tickets array of rows, each summarising one ticket in the perspective’s order, plus show_more (true when more rows matched than row_limit allowed) and total_count (the number of tickets that match, after permission and search filtering). The core row fields are stable; rows also carry the display fields the ticket browser uses, such as since_created, since_updated, opened and locked.
{
"tickets": [
{
"ticket_id": 1,
"number": 220009,
"status": "open",
"on_hold_until": null,
"queue": "Returns",
"priority": "20",
"subject": "Carbon plate cracked on first marathon",
"assigned_to_email": "[email protected]",
"originator_email": "[email protected]",
"created_at": "2026-05-01 09:14:02",
"updated_at": "2026-05-02 11:30:48",
"since_created": "1 day ago",
"since_updated": "2 hours ago"
}
],
"show_more": false,
"total_count": 1
}The list is the same materialised, cached set the ticket browser uses, so it reflects the latest ticket state at query time. Poll it on a sensible interval rather than in a tight loop.
Error responses
422— a criterion names an unknown mode. The body carries amessageand, undererrors["search_object.criteria"], the unknown modes and the list of valid ones.404— the requested perspective (by id or name) does not exist. The body carries amessage.401 unauthenticated— bad or missing token.403 forbidden— token does not have thetickets:read(ortickets:write) ability.
To read a full conversation behind a row, follow up with the Show ticket API. See the API overview for the shared request and authentication conventions.