> ## Documentation Index
> Fetch the complete documentation index at: https://docs.armin.cx/llms.txt
> Use this file to discover all available pages before exploring further.

# Search contacts and tickets

> Find a customer by email or name, list all of their tickets, and search tickets the same way the inbox search bar does.

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`.

| Parameter | Matches |
| - | - |
| `email` | The contact's email or any additional email address (case-insensitive). A different address on the same domain does not match. Duplicate contacts that share the address are all returned. |
| `name` | At least 3 characters, whole words (`Jane` finds `Jane Doe`, but `Jan` does not). |

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.

```bash theme={null}
curl -sS -H "cx-api-key: $KEY" \
  "https://api.armin.cx/public/v1/contacts/search?email=jane@example.com"
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "0193a7b2-0000-7000-8000-000000000001",
      "firstname": "Jane",
      "lastname": "Doe",
      "full_name": "Jane Doe",
      "email": "jane@example.com",
      "created_at": "2026-01-01T10:00:00.000Z"
    }
  ]
}
```

Contact `id`s 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](https://docs.armin.cx/api/pagination.md) as `GET /tickets`.

```bash theme={null}
curl -sS -H "cx-api-key: $KEY" \
  "https://api.armin.cx/public/v1/contacts/0193a7b2-0000-7000-8000-000000000001/tickets?limit=40"
```

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.

| Parameter | Description |
| - | - |
| `q` | Words to find, 2 to 100 characters |
| `sort` | `relevance` (default, best match first), `newest`, or `oldest`. Newest and oldest use the ticket's creation date. |
| `limit` | Results per page, 1 to 50 (default 20) |
| `cursor` | `pagination.next_cursor` from the previous page |

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`.

```bash theme={null}
curl -sS -G -H "cx-api-key: $KEY" \
  "https://api.armin.cx/public/v1/tickets/search" \
  --data-urlencode "q=arrived broken"
```

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](https://docs.armin.cx/api/rate-limits.md).
* 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](https://docs.armin.cx/api/filtering-and-saved-views.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.