Skip to main content
Search lets your integration answer “who is this customer, and what have we talked about before?” without paging through every ticket. Look up a contact by email or name, list all of that contact’s tickets, or find tickets by words. Ticket search and contact search use the same search as the inbox search bar. They find the same tickets and contacts an agent finds when they type the same words. All search endpoints are read-only and return data from the organization that owns your API key only.

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.
Contact 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.
Use 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.
Each result is a ticket in the same shape as 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

Pass pagination.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 email over name when 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:read only 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.