# List threads

`GET /v1/email/threads`

Returns a paginated list of conversations across the workspace's mailboxes, most recently active first. `label` selects the view: the inbox (the default when omitted), `archive`, `spam`, `blocked`, or any custom label. You can also filter by mailbox, by linked contact, by participant address, or by a subject substring.

This listing filters; it does not search message content.
Conversations whose every message is trashed are excluded; restoring a message
returns the conversation to the list.

`before` and `after` filter by time. To page through the results, pass the response cursors back as `starting_after` or `ending_before`.

## Code samples

**TypeScript**

```ts
for await (const thread of bird.email.threads.list({ mailbox_id: "mbx_01abc" })) {
  console.log(thread.id, thread.subject);
}
```

Examples: [TypeScript](/docs/api/reference/list-email-threads.ts.md) · [Python](/docs/api/reference/list-email-threads.py.md) · [Go](/docs/api/reference/list-email-threads.go.md) · [PHP](/docs/api/reference/list-email-threads.php.md) · [CLI](/docs/api/reference/list-email-threads.cli.md) · [MCP](/docs/api/reference/list-email-threads.mcp.md) · [cURL](/docs/api/reference/list-email-threads.curl.md)

## Example response `200`

```json
{
  "data": [
    {
      "id": "thr_01krdgeqcxet5s7t44vh8rt9mg",
      "mailbox_id": "mbx_01krdgeqcxet5s7t44vh8rt9mg",
      "channel": "email",
      "contact_id": "con_01krdgeqcxet5s7t44vh8rt9mg",
      "subject": "Re: Your order",
      "last_direction": "inbound",
      "labels": [
        "inbox",
        "urgent"
      ],
      "highlights": {
        "subject": [
          "Re: your **order** **4821**"
        ],
        "text": [
          "confirming your **order** **4821** shipped"
        ]
      }
    }
  ],
  "next_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9",
  "prev_cursor": null,
  "refresh_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"
}
```

## Query parameters

- `mailbox_id` (string): Filter to conversations in a specific mailbox.
- `contact_id` (string): Filter to conversations linked to a specific contact.
- `label` (array)

  Filter to conversations that have this label. Repeat the parameter to ask for more than one: only conversations that have every label you list are returned.

  A placement label picks a folder: `inbox`, `archive`, `spam`, or `blocked`. A custom label matches a conversation in any folder. Leave this out and you get the inbox.
- `has_unread` (boolean): When `true`, only conversations with unread messages are returned. This filters on the conversation's unread state, so you can combine it with `label`, for example to get unread conversations in the archive. The `unread` label itself lives on individual messages; this filter uses the conversation's aggregate unread state.
- `participant` (string): Conversations involving this address, matching the sender or any recipient. The match is case-insensitive and matches on any part of the address, so a fragment works as well as the whole address.
- `subject` (string): Conversations whose subject contains this text (case-insensitive).
- `after` (string): Filter to conversations whose most recent message is at or after this time. Use the response cursors for pagination.
- `before` (string): Filter to conversations whose most recent message is at or before this time. Use the response cursors for pagination.
- `limit` (integer): Maximum number of items to return per page.
- `starting_after` (string): Cursor from the `next_cursor` field of a previous list response. Returns items immediately after the cursor position in the current sort order.
- `ending_before` (string): Cursor from the `prev_cursor` or `refresh_cursor` field of a previous list response. Returns items immediately before the cursor position in the current sort order. `prev_cursor` returns the preceding page. `refresh_cursor` anchors at the first row of that response, which on a newest-first sort is how to fetch the items that have appeared since.

## Response body

- `data` (array of object, required)
- `data.id` (string, required): Thread ID.
- `data.mailbox_id` (string, required): Mailbox this conversation belongs to.
- `data.channel` (string, required): Channel this conversation lives on. Always `email`.
- `data.contact_id` (nullable string, required): Contact linked to this conversation, or null when none is linked.
- `data.subject` (nullable string, required): Subject of the conversation, taken from its first message. Null when that message had no subject.
- `data.participants` (array of string, required): Addresses that appear on the retained messages in this conversation, including the mailbox's own address.
- `data.message_count` (integer, required): Number of retained messages in this conversation, both directions.
- `data.unread_count` (integer, required): Number of retained received messages that are still unread. Spam and blocked mail is not counted.
- `data.last_message_at` (string, required): When the most recent retained message in this conversation was received or sent.
- `data.last_direction` (string, required)

  Direction of the most recent message: `inbound` for a received message, `outbound` for a sent one.

  Possible values: `inbound`, `outbound`
- `data.labels` (array of string, required)

  Labels on this conversation. Exactly one system placement label is always present, set by the message that started the conversation:

  - `inbox`: The conversation is in the inbox.
  - `archive`: The conversation was filed away and is done for now.
  - `spam`: The conversation's opening message is filed in Spam.
  - `blocked`: The conversation's opening message was rejected by the mailbox's receive policy or rules.

  Move a conversation by updating its labels. Add `spam` to file it as spam, add `archive` to clean it out of the inbox, and add `inbox`, or remove `spam`, `blocked`, or `archive`, to bring it back. An archived conversation returns to the inbox by itself when a new message arrives. Custom labels share the same list, and a conversation has at most 20 labels in total.
- `data.created_at` (string, required): When the thread was created.
- `data.updated_at` (string, required): When the thread last changed.
- `data.highlights` (object): Matched search fragments, keyed by the field that matched. Returned only by thread search. Omitted when listing threads.
- `data.highlights.subject` (array of string): Matched fragments from the conversation's subject.
- `data.highlights.text` (array of string): Matched fragments from a message's body text.
- `next_cursor` (nullable string, required): Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.
- `prev_cursor` (nullable string, required): Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.
- `refresh_cursor` (nullable string, required): Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.

## Related resources

- [Getting started with email](/learn/email/getting-started-with-email) (video)
- [Email](/email-api) (product)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=email)
