When to use it
- Identify a customer: an order system or AI agent has an email address and needs the matching contact, including duplicates.
- Show customer history: list every ticket of a contact: open, resolved, and closed.
- Find past conversations: search for a tracking number, product name, or phrase across tickets, including old ones.
Look up a contact
GET /public/v1/contacts/search needs the contacts:read scope. Pass exactly one of email or name.
Both use the same search as the inbox search bar. They return the best matches first, at most 12. The search index updates asynchronously, so a contact created moments ago may not be found yet.
ids are stable. Contacts that were merged into another are not searchable. You get the surviving contact, and the tickets moved with the merge. An empty data array means no contact matched.
List a contact’s tickets
GET /public/v1/contacts/{contactId}/tickets needs the tickets:read scope. It returns every ticket of the contact in every status, in the same shape, order, and cursor pagination as GET /tickets.
ticket_number with GET /tickets/{ticketNumber} and GET /tickets/{ticketNumber}/messages to read a ticket. A contact ID that does not exist in your organization returns 404 CONTACT_NOT_FOUND.
Search tickets
GET /public/v1/tickets/search needs the tickets:read scope.
A ticket matches on its subject, AI summary, ticket number, attachment file names, conversation messages, or the name and email of its contact. Matching uses whole words:
refund does not find refunds.
GET /tickets. Results include every status. By default they come best match first. Pass sort=newest or sort=oldest to order by creation date instead.
The inbox search operators work in q: from:, to:, channel:, has:attachment, and newer_than:. from:me and to:me are ignored, because an API key has no signed-in agent.
Pagination
Passpagination.next_cursor unchanged to get the next page. A page can hold fewer than limit tickets, for example when a ticket was just deleted. Keep going while pagination.has_more is true.
Search reaches about the best 500 matches. To go deeper, make the query more specific.
The search index updates asynchronously, so a ticket or message created or edited moments ago may not be found yet.
Best practices
- Prefer
emailovernamewhen you have it. It is the more precise lookup, and it returns duplicates you may need to reconcile. - Back off on
429. Search has its own limit of 30 requests per minute. See Rate limits. - Grant
contacts:readonly to keys that need to look up customers. Keys with explicit scopes don’t gain it automatically, so create a new key with the scope. Legacy keys without scopes keep full access.
Limits
- Search returns tickets and contacts only. It does not return message snippets or explain why a ticket matched.
- Phone numbers, custom ticket fields, and filters such as status or tag are not searchable. To filter tickets, use a saved view.