---
title: "Threads"
description: "List, search, read, update and delete conversation threads in MachineMail, across the whole organization, within a pod or in a single inbox."
canonical: "https://machinemail.dev/docs/reference/api/threads"
---

# Threads

> List, search, read, update and delete conversation threads in MachineMail, across the whole organization, within a pod or in a single inbox.

Source: https://machinemail.dev/docs/reference/api/threads

| Method   | Path                                                       | Summary                                         |
| -------- | ---------------------------------------------------------- | ----------------------------------------------- |
| `GET`    | `/v0/inboxes/{inbox_id}/threads`                           | [List Threads](#list-threads)                   |
| `GET`    | `/v0/inboxes/{inbox_id}/threads/search`                    | [Search Threads](#search-threads)               |
| `GET`    | `/v0/inboxes/{inbox_id}/threads/{thread_id}`               | [Get Thread](#get-thread)                       |
| `PATCH`  | `/v0/inboxes/{inbox_id}/threads/{thread_id}`               | [Update Thread](#update-thread)                 |
| `DELETE` | `/v0/inboxes/{inbox_id}/threads/{thread_id}`               | [Delete Thread](#delete-thread)                 |
| `GET`    | `/v0/pods/{pod_id}/inboxes/{inbox_id}/threads`             | [List Threads in Pod](#list-threads-in-pod)     |
| `GET`    | `/v0/pods/{pod_id}/inboxes/{inbox_id}/threads/search`      | [Search Threads in Pod](#search-threads-in-pod) |
| `GET`    | `/v0/pods/{pod_id}/inboxes/{inbox_id}/threads/{thread_id}` | [Get Thread in Pod](#get-thread-in-pod)         |
| `PATCH`  | `/v0/pods/{pod_id}/inboxes/{inbox_id}/threads/{thread_id}` | [Update Thread in Pod](#update-thread-in-pod)   |
| `DELETE` | `/v0/pods/{pod_id}/inboxes/{inbox_id}/threads/{thread_id}` | [Delete Thread in Pod](#delete-thread-in-pod)   |
| `GET`    | `/v0/pods/{pod_id}/threads`                                | [List Threads in Pod](#list-threads-in-pod)     |
| `GET`    | `/v0/pods/{pod_id}/threads/search`                         | [Search Threads in Pod](#search-threads-in-pod) |
| `GET`    | `/v0/pods/{pod_id}/threads/{thread_id}`                    | [Get Thread in Pod](#get-thread-in-pod)         |
| `PATCH`  | `/v0/pods/{pod_id}/threads/{thread_id}`                    | [Update Thread in Pod](#update-thread-in-pod)   |
| `DELETE` | `/v0/pods/{pod_id}/threads/{thread_id}`                    | [Delete Thread in Pod](#delete-thread-in-pod)   |
| `GET`    | `/v0/threads`                                              | [List Threads](#list-threads)                   |
| `GET`    | `/v0/threads/search`                                       | [Search Threads](#search-threads)               |
| `GET`    | `/v0/threads/{thread_id}`                                  | [Get Thread](#get-thread)                       |
| `PATCH`  | `/v0/threads/{thread_id}`                                  | [Update Thread](#update-thread)                 |
| `DELETE` | `/v0/threads/{thread_id}`                                  | [Delete Thread](#delete-thread)                 |

## List Threads

```http
GET /v0/inboxes/{inbox_id}/threads
```

Threads, most recently active first. Pass `q` to keep only threads with a message matching the words.

Threads with a spam, blocked, trash, quarantined or unauthenticated message are hidden unless you pass the matching `include_*` flag or ask for the label in `labels`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only items with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `include_spam` | `boolean` |  | Include spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |
| `q` | `string` |  | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. |

#### Response

**200** A page of threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/threads" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Search Threads

```http
GET /v0/inboxes/{inbox_id}/threads/search
```

Threads with a message whose subject, sender, recipients, body or attachment file names contain every word of `q`, ordered by thread activity (not ranked by relevance). Scans the newest 500 matching messages; the response has `truncated: true` when it hit that cap.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `q` | `string` | required | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. (min length 1) |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only threads with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `include_spam` | `boolean` |  | Include threads with spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include threads with blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include threads with unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include threads with trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |

#### Response

**200** A page of matching threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Matching threads, ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/threads/search?q=invoice" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Get Thread

```http
GET /v0/inboxes/{inbox_id}/threads/{thread_id}
```

The thread with its messages, which carry their bodies and trust reports. Messages come in pages of `limit` (default and maximum 500): the first page has the newest messages, oldest first within the page; follow `next_page_token` for older ones.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |
| `thread_id` | `string` | required | ID of the thread. |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of messages to return. Default and maximum 500. |
| `page_token` | `string` |  | next_page_token from the previous response: the next, older page of messages. |

#### Response

**200** The thread with a page of its messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `count` | `integer` | required | Number of messages in this page. |
| `limit` | `integer` |  | Maximum number of messages requested for this page. |
| `next_page_token` | `string` |  | Token for the next, older page of messages. Absent when this page completes the thread. |
| `messages` | `Message[]` | required | Messages in this page, oldest first. The first page has the newest messages; follow next_page_token for older ones. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

Each item of `messages` is a [Message](https://machinemail.dev/docs/reference/api/objects#message):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `message_id` | `string` | required | ID of the message. |
| `labels` | `string[]` | required | Labels of the message. |
| `timestamp` | `string (date-time)` | required | Time at which the message was sent or received. |
| `from` | `string` | required | Address of the sender. In format username@domain.com or Display Name <username@domain.com>. |
| `to` | `string[]` | required | Addresses of the recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `cc` | `string[]` |  | Addresses of the CC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `bcc` | `string[]` |  | Addresses of the BCC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `subject` | `string` |  | Subject of the message. |
| `preview` | `string` |  | Text preview of the message. |
| `attachments` | `Attachment[]` |  | Attachments of the message. |
| `in_reply_to` | `string` |  | ID of the message being replied to. |
| `references` | `string[]` |  | IDs of previous messages in the thread. |
| `headers` | `Record<string, string>` |  | Headers of the message. |
| `authentication_results` | `MessageAuthenticationResults` |  | SPF, DKIM and DMARC verdicts for a received message, each with the domain it is about, as recorded when the message reached MachineMail: by the receiving mail provider (for example Amazon SES), or by the Authentication-Results header of the server that delivered it to MachineMail. A mechanism that was not evaluated is absent. The whole field is absent on sent messages, on messages injected through POST /inbound/raw (their headers are not believed), when nothing was evaluated, and on messages received before the field was introduced. As in AgentMail, only verdicts that speak for the sender are kept. spf is present only when spf_domain is aligned with the sender (shares an organizational domain with the From domain, or with the Sender domain when From spans several domains): an unaligned SPF result is withheld whatever its value. dkim and dmarc are withheld when From spans more than one domain or could not be parsed. Unlike the trust report (trust.authentication), which collapses each result to the values MachineMail scores on and adds aligned, these are the uncollapsed results: permerror and temperror stay as they are. dkim_domain is the signing domain as written: compare it with the From domain before treating a DKIM pass as proof of the sender, and use trust.level (or the UNAUTHENTICATED label) to decide whether to act on the message. Do not reject mail on dmarc: fail alone: forwarding breaks it. |
| `size` | `integer` | required | Size of the message in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the message was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the message was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `direction` | `"inbound" \| "outbound"` | required | Whether the message was received or sent. |
| `delivery_status` | `"sent" \| "delivered" \| "bounced" \| "complained"` |  | Outbound only: last delivery state reported by the transport. |
| `trust_level` | `"verified" \| "known" \| "unknown" \| "suspicious" \| "quarantined"` |  | Inbound only: trust.level, for list badges. |
| `reply_to` | `string[]` |  | Reply-to addresses. In format username@domain.com or Display Name <username@domain.com>. |
| `text` | `string` |  | Plain text body. Untrusted: agents should read trust.content.text or extracted_text. |
| `html` | `string` |  | HTML body. Untrusted. |
| `extracted_text` | `string` |  | New text content, without quoted replies. |
| `extracted_html` | `string` |  | New HTML content, without quoted replies. |
| `received_via` | `"raw"` |  | raw when the message was injected with POST /inbound/raw (authentication not verified). |
| `held_at` | `string (date-time)` |  | When the message arrived, for inbound mail that was held because the organization reached a Free plan limit. created_at is then when it was delivered (released). |
| `trust` | `TrustReport` |  | Trust report of an inbound message. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "messages": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "from": "Support Agent <agent@machinemail.dev>",
      "to": ["customer@example.com"],
      "cc": ["team@example.com"],
      "bcc": ["audit@example.com"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "in_reply_to": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "references": ["string"],
      "headers": {
        "key": "value"
      },
      "authentication_results": {
        "spf": "pass",
        "dkim": "pass",
        "dmarc": "pass",
        "spf_domain": "string",
        "dkim_domain": "string",
        "dmarc_domain": "string",
        "arc_sealer": "string"
      },
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "direction": "inbound",
      "delivery_status": "sent",
      "trust_level": "verified",
      "reply_to": ["agent@machinemail.dev"],
      "text": "Thanks for the quick reply.",
      "html": "<p>Thanks for the quick reply.</p>",
      "extracted_text": "Thanks for the quick reply.",
      "extracted_html": "<p>Thanks for the quick reply.</p>",
      "received_via": "raw",
      "held_at": "2026-10-01T12:00:00.000Z",
      "trust": {
        "level": "verified",
        "score": 0.8,
        "basis": "trusted_domain",
        "authentication": {
          "spf": "pass",
          "dkim": "pass",
          "dmarc": "pass",
          "aligned": false
        },
        "sender": {
          "address": "agent@machinemail.dev",
          "domain": "example.com",
          "prior_correspondence": false,
          "on_allow_list": false,
          "on_block_list": false,
          "free_mail_provider": false,
          "trusted_domain": {
            "id": "string",
            "domain": "example.com",
            "scope": "org"
          },
          "trusted_domain_shadowed_by": "block"
        },
        "content": {
          "text": "Thanks for the quick reply.",
          "html": "<p>Thanks for the quick reply.</p>",
          "segments": [{}],
          "hidden_text_found": false,
          "zero_width_found": false,
          "tracking_pixels": 1,
          "agent_instructions_detected": false,
          "warnings": ["string"]
        },
        "links": [
          {
            "href": "string",
            "text": "Thanks for the quick reply.",
            "final_url": "https://example.com",
            "domain": "example.com",
            "display_mismatch": false,
            "shortener": false,
            "suspicious": false,
            "reasons": []
          }
        ],
        "attachments": [
          {
            "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
            "filename": "invoice.pdf",
            "content_type": "application/pdf",
            "risk": "low",
            "reasons": [],
            "extracted_text": "Thanks for the quick reply."
          }
        ],
        "classification": {
          "is_spam": 0.8,
          "is_phishing": 0.8,
          "contains_instructions_to_agent": 0.8,
          "requests_credentials_or_payment": 0.8,
          "urgency": 0.8,
          "category": "transactional",
          "language": "string",
          "backend": "string",
          "input_hash": "string",
          "cached": false
        },
        "provider_verdicts": {
          "spam": "pass",
          "virus": "pass",
          "source": "string"
        },
        "reasons": ["string"],
        "computed_at": "2026-10-01T12:00:00.000Z"
      }
    }
  ]
}
```

## Update Thread

```http
PATCH /v0/inboxes/{inbox_id}/threads/{thread_id}
```

Adds and removes labels on every message in the thread (up to 500 messages); the thread labels are their union. Labels are free strings, system labels included. Returns the updated thread without messages.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `message_update`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |
| `thread_id` | `string` | required | ID of the thread. |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `add_labels` | `string \| string[]` |  | Label or labels to add to every message in the thread. |
| `remove_labels` | `string \| string[]` |  | Label or labels to remove from every message in the thread. Applied after add_labels. |

#### Response

**200** The updated thread, without messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                           |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                        |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                  |
| `402`  | Free plan: removing SPAM or QUARANTINE makes the message count toward the messages cap and storage from now on, and the organization is over one (limit\_key messages\_per\_month, messages\_per\_day or storage\_bytes). The labels are unchanged. Paid plans are never refused. |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register).                                            |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                              |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                                                              |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                          |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X PATCH "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "add_labels": "string"
}'
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
}
```

## Delete Thread

```http
DELETE /v0/inboxes/{inbox_id}/threads/{thread_id}
```

Permanently deletes the thread and its messages (up to 500).

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_delete`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |
| `thread_id` | `string` | required | ID of the thread. |

#### Response

**200** Deleted (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `ok` | `true` | required |  |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X DELETE "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "ok": true
}
```

## List Threads in Pod

```http
GET /v0/pods/{pod_id}/inboxes/{inbox_id}/threads
```

Same as List Threads (`GET /v0/inboxes/{inbox_id}/threads`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only items with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `include_spam` | `boolean` |  | Include spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |
| `q` | `string` |  | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. |

#### Response

**200** A page of threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/inboxes/agent@machinemail.dev/threads" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Search Threads in Pod

```http
GET /v0/pods/{pod_id}/inboxes/{inbox_id}/threads/search
```

Same as Search Threads (`GET /v0/inboxes/{inbox_id}/threads/search`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `q` | `string` | required | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. (min length 1) |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only threads with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `include_spam` | `boolean` |  | Include threads with spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include threads with blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include threads with unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include threads with trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |

#### Response

**200** A page of matching threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Matching threads, ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/inboxes/agent@machinemail.dev/threads/search?q=invoice" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Get Thread in Pod

```http
GET /v0/pods/{pod_id}/inboxes/{inbox_id}/threads/{thread_id}
```

Same as Get Thread (`GET /v0/inboxes/{inbox_id}/threads/{thread_id}`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |
| `thread_id` | `string` | required | ID of the thread. |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of messages to return. Default and maximum 500. |
| `page_token` | `string` |  | next_page_token from the previous response: the next, older page of messages. |

#### Response

**200** The thread with a page of its messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `count` | `integer` | required | Number of messages in this page. |
| `limit` | `integer` |  | Maximum number of messages requested for this page. |
| `next_page_token` | `string` |  | Token for the next, older page of messages. Absent when this page completes the thread. |
| `messages` | `Message[]` | required | Messages in this page, oldest first. The first page has the newest messages; follow next_page_token for older ones. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

Each item of `messages` is a [Message](https://machinemail.dev/docs/reference/api/objects#message):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `message_id` | `string` | required | ID of the message. |
| `labels` | `string[]` | required | Labels of the message. |
| `timestamp` | `string (date-time)` | required | Time at which the message was sent or received. |
| `from` | `string` | required | Address of the sender. In format username@domain.com or Display Name <username@domain.com>. |
| `to` | `string[]` | required | Addresses of the recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `cc` | `string[]` |  | Addresses of the CC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `bcc` | `string[]` |  | Addresses of the BCC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `subject` | `string` |  | Subject of the message. |
| `preview` | `string` |  | Text preview of the message. |
| `attachments` | `Attachment[]` |  | Attachments of the message. |
| `in_reply_to` | `string` |  | ID of the message being replied to. |
| `references` | `string[]` |  | IDs of previous messages in the thread. |
| `headers` | `Record<string, string>` |  | Headers of the message. |
| `authentication_results` | `MessageAuthenticationResults` |  | SPF, DKIM and DMARC verdicts for a received message, each with the domain it is about, as recorded when the message reached MachineMail: by the receiving mail provider (for example Amazon SES), or by the Authentication-Results header of the server that delivered it to MachineMail. A mechanism that was not evaluated is absent. The whole field is absent on sent messages, on messages injected through POST /inbound/raw (their headers are not believed), when nothing was evaluated, and on messages received before the field was introduced. As in AgentMail, only verdicts that speak for the sender are kept. spf is present only when spf_domain is aligned with the sender (shares an organizational domain with the From domain, or with the Sender domain when From spans several domains): an unaligned SPF result is withheld whatever its value. dkim and dmarc are withheld when From spans more than one domain or could not be parsed. Unlike the trust report (trust.authentication), which collapses each result to the values MachineMail scores on and adds aligned, these are the uncollapsed results: permerror and temperror stay as they are. dkim_domain is the signing domain as written: compare it with the From domain before treating a DKIM pass as proof of the sender, and use trust.level (or the UNAUTHENTICATED label) to decide whether to act on the message. Do not reject mail on dmarc: fail alone: forwarding breaks it. |
| `size` | `integer` | required | Size of the message in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the message was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the message was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `direction` | `"inbound" \| "outbound"` | required | Whether the message was received or sent. |
| `delivery_status` | `"sent" \| "delivered" \| "bounced" \| "complained"` |  | Outbound only: last delivery state reported by the transport. |
| `trust_level` | `"verified" \| "known" \| "unknown" \| "suspicious" \| "quarantined"` |  | Inbound only: trust.level, for list badges. |
| `reply_to` | `string[]` |  | Reply-to addresses. In format username@domain.com or Display Name <username@domain.com>. |
| `text` | `string` |  | Plain text body. Untrusted: agents should read trust.content.text or extracted_text. |
| `html` | `string` |  | HTML body. Untrusted. |
| `extracted_text` | `string` |  | New text content, without quoted replies. |
| `extracted_html` | `string` |  | New HTML content, without quoted replies. |
| `received_via` | `"raw"` |  | raw when the message was injected with POST /inbound/raw (authentication not verified). |
| `held_at` | `string (date-time)` |  | When the message arrived, for inbound mail that was held because the organization reached a Free plan limit. created_at is then when it was delivered (released). |
| `trust` | `TrustReport` |  | Trust report of an inbound message. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/inboxes/agent@machinemail.dev/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "messages": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "from": "Support Agent <agent@machinemail.dev>",
      "to": ["customer@example.com"],
      "cc": ["team@example.com"],
      "bcc": ["audit@example.com"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "in_reply_to": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "references": ["string"],
      "headers": {
        "key": "value"
      },
      "authentication_results": {
        "spf": "pass",
        "dkim": "pass",
        "dmarc": "pass",
        "spf_domain": "string",
        "dkim_domain": "string",
        "dmarc_domain": "string",
        "arc_sealer": "string"
      },
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "direction": "inbound",
      "delivery_status": "sent",
      "trust_level": "verified",
      "reply_to": ["agent@machinemail.dev"],
      "text": "Thanks for the quick reply.",
      "html": "<p>Thanks for the quick reply.</p>",
      "extracted_text": "Thanks for the quick reply.",
      "extracted_html": "<p>Thanks for the quick reply.</p>",
      "received_via": "raw",
      "held_at": "2026-10-01T12:00:00.000Z",
      "trust": {
        "level": "verified",
        "score": 0.8,
        "basis": "trusted_domain",
        "authentication": {
          "spf": "pass",
          "dkim": "pass",
          "dmarc": "pass",
          "aligned": false
        },
        "sender": {
          "address": "agent@machinemail.dev",
          "domain": "example.com",
          "prior_correspondence": false,
          "on_allow_list": false,
          "on_block_list": false,
          "free_mail_provider": false,
          "trusted_domain": {
            "id": "string",
            "domain": "example.com",
            "scope": "org"
          },
          "trusted_domain_shadowed_by": "block"
        },
        "content": {
          "text": "Thanks for the quick reply.",
          "html": "<p>Thanks for the quick reply.</p>",
          "segments": [{}],
          "hidden_text_found": false,
          "zero_width_found": false,
          "tracking_pixels": 1,
          "agent_instructions_detected": false,
          "warnings": ["string"]
        },
        "links": [
          {
            "href": "string",
            "text": "Thanks for the quick reply.",
            "final_url": "https://example.com",
            "domain": "example.com",
            "display_mismatch": false,
            "shortener": false,
            "suspicious": false,
            "reasons": []
          }
        ],
        "attachments": [
          {
            "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
            "filename": "invoice.pdf",
            "content_type": "application/pdf",
            "risk": "low",
            "reasons": [],
            "extracted_text": "Thanks for the quick reply."
          }
        ],
        "classification": {
          "is_spam": 0.8,
          "is_phishing": 0.8,
          "contains_instructions_to_agent": 0.8,
          "requests_credentials_or_payment": 0.8,
          "urgency": 0.8,
          "category": "transactional",
          "language": "string",
          "backend": "string",
          "input_hash": "string",
          "cached": false
        },
        "provider_verdicts": {
          "spam": "pass",
          "virus": "pass",
          "source": "string"
        },
        "reasons": ["string"],
        "computed_at": "2026-10-01T12:00:00.000Z"
      }
    }
  ]
}
```

## Update Thread in Pod

```http
PATCH /v0/pods/{pod_id}/inboxes/{inbox_id}/threads/{thread_id}
```

Same as Update Thread (`PATCH /v0/inboxes/{inbox_id}/threads/{thread_id}`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `message_update`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |
| `thread_id` | `string` | required | ID of the thread. |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `add_labels` | `string \| string[]` |  | Label or labels to add to every message in the thread. |
| `remove_labels` | `string \| string[]` |  | Label or labels to remove from every message in the thread. Applied after add_labels. |

#### Response

**200** The updated thread, without messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                           |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                        |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                  |
| `402`  | Free plan: removing SPAM or QUARANTINE makes the message count toward the messages cap and storage from now on, and the organization is over one (limit\_key messages\_per\_month, messages\_per\_day or storage\_bytes). The labels are unchanged. Paid plans are never refused. |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register).                                            |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                              |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                                                              |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                          |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X PATCH "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/inboxes/agent@machinemail.dev/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "add_labels": "string"
}'
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
}
```

## Delete Thread in Pod

```http
DELETE /v0/pods/{pod_id}/inboxes/{inbox_id}/threads/{thread_id}
```

Same as Delete Thread (`DELETE /v0/inboxes/{inbox_id}/threads/{thread_id}`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_delete`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `inbox_id` | `string` | required | ID of the inbox: its email address, as in AgentMail (inbox_id == email). |
| `thread_id` | `string` | required | ID of the thread. |

#### Response

**200** Deleted (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `ok` | `true` | required |  |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X DELETE "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/inboxes/agent@machinemail.dev/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "ok": true
}
```

## List Threads in Pod

```http
GET /v0/pods/{pod_id}/threads
```

Same as List Threads (`GET /v0/threads`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only items with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `include_spam` | `boolean` |  | Include spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |
| `q` | `string` |  | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. |

#### Response

**200** A page of threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/threads" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Search Threads in Pod

```http
GET /v0/pods/{pod_id}/threads/search
```

Same as Search Threads (`GET /v0/threads/search`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `q` | `string` | required | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. (min length 1) |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only threads with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `include_spam` | `boolean` |  | Include threads with spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include threads with blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include threads with unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include threads with trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |

#### Response

**200** A page of matching threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Matching threads, ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/threads/search?q=invoice" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Get Thread in Pod

```http
GET /v0/pods/{pod_id}/threads/{thread_id}
```

Same as Get Thread (`GET /v0/threads/{thread_id}`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `thread_id` | `string` | required | ID of the thread. |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of messages to return. Default and maximum 500. |
| `page_token` | `string` |  | next_page_token from the previous response: the next, older page of messages. |

#### Response

**200** The thread with a page of its messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `count` | `integer` | required | Number of messages in this page. |
| `limit` | `integer` |  | Maximum number of messages requested for this page. |
| `next_page_token` | `string` |  | Token for the next, older page of messages. Absent when this page completes the thread. |
| `messages` | `Message[]` | required | Messages in this page, oldest first. The first page has the newest messages; follow next_page_token for older ones. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

Each item of `messages` is a [Message](https://machinemail.dev/docs/reference/api/objects#message):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `message_id` | `string` | required | ID of the message. |
| `labels` | `string[]` | required | Labels of the message. |
| `timestamp` | `string (date-time)` | required | Time at which the message was sent or received. |
| `from` | `string` | required | Address of the sender. In format username@domain.com or Display Name <username@domain.com>. |
| `to` | `string[]` | required | Addresses of the recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `cc` | `string[]` |  | Addresses of the CC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `bcc` | `string[]` |  | Addresses of the BCC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `subject` | `string` |  | Subject of the message. |
| `preview` | `string` |  | Text preview of the message. |
| `attachments` | `Attachment[]` |  | Attachments of the message. |
| `in_reply_to` | `string` |  | ID of the message being replied to. |
| `references` | `string[]` |  | IDs of previous messages in the thread. |
| `headers` | `Record<string, string>` |  | Headers of the message. |
| `authentication_results` | `MessageAuthenticationResults` |  | SPF, DKIM and DMARC verdicts for a received message, each with the domain it is about, as recorded when the message reached MachineMail: by the receiving mail provider (for example Amazon SES), or by the Authentication-Results header of the server that delivered it to MachineMail. A mechanism that was not evaluated is absent. The whole field is absent on sent messages, on messages injected through POST /inbound/raw (their headers are not believed), when nothing was evaluated, and on messages received before the field was introduced. As in AgentMail, only verdicts that speak for the sender are kept. spf is present only when spf_domain is aligned with the sender (shares an organizational domain with the From domain, or with the Sender domain when From spans several domains): an unaligned SPF result is withheld whatever its value. dkim and dmarc are withheld when From spans more than one domain or could not be parsed. Unlike the trust report (trust.authentication), which collapses each result to the values MachineMail scores on and adds aligned, these are the uncollapsed results: permerror and temperror stay as they are. dkim_domain is the signing domain as written: compare it with the From domain before treating a DKIM pass as proof of the sender, and use trust.level (or the UNAUTHENTICATED label) to decide whether to act on the message. Do not reject mail on dmarc: fail alone: forwarding breaks it. |
| `size` | `integer` | required | Size of the message in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the message was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the message was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `direction` | `"inbound" \| "outbound"` | required | Whether the message was received or sent. |
| `delivery_status` | `"sent" \| "delivered" \| "bounced" \| "complained"` |  | Outbound only: last delivery state reported by the transport. |
| `trust_level` | `"verified" \| "known" \| "unknown" \| "suspicious" \| "quarantined"` |  | Inbound only: trust.level, for list badges. |
| `reply_to` | `string[]` |  | Reply-to addresses. In format username@domain.com or Display Name <username@domain.com>. |
| `text` | `string` |  | Plain text body. Untrusted: agents should read trust.content.text or extracted_text. |
| `html` | `string` |  | HTML body. Untrusted. |
| `extracted_text` | `string` |  | New text content, without quoted replies. |
| `extracted_html` | `string` |  | New HTML content, without quoted replies. |
| `received_via` | `"raw"` |  | raw when the message was injected with POST /inbound/raw (authentication not verified). |
| `held_at` | `string (date-time)` |  | When the message arrived, for inbound mail that was held because the organization reached a Free plan limit. created_at is then when it was delivered (released). |
| `trust` | `TrustReport` |  | Trust report of an inbound message. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "messages": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "from": "Support Agent <agent@machinemail.dev>",
      "to": ["customer@example.com"],
      "cc": ["team@example.com"],
      "bcc": ["audit@example.com"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "in_reply_to": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "references": ["string"],
      "headers": {
        "key": "value"
      },
      "authentication_results": {
        "spf": "pass",
        "dkim": "pass",
        "dmarc": "pass",
        "spf_domain": "string",
        "dkim_domain": "string",
        "dmarc_domain": "string",
        "arc_sealer": "string"
      },
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "direction": "inbound",
      "delivery_status": "sent",
      "trust_level": "verified",
      "reply_to": ["agent@machinemail.dev"],
      "text": "Thanks for the quick reply.",
      "html": "<p>Thanks for the quick reply.</p>",
      "extracted_text": "Thanks for the quick reply.",
      "extracted_html": "<p>Thanks for the quick reply.</p>",
      "received_via": "raw",
      "held_at": "2026-10-01T12:00:00.000Z",
      "trust": {
        "level": "verified",
        "score": 0.8,
        "basis": "trusted_domain",
        "authentication": {
          "spf": "pass",
          "dkim": "pass",
          "dmarc": "pass",
          "aligned": false
        },
        "sender": {
          "address": "agent@machinemail.dev",
          "domain": "example.com",
          "prior_correspondence": false,
          "on_allow_list": false,
          "on_block_list": false,
          "free_mail_provider": false,
          "trusted_domain": {
            "id": "string",
            "domain": "example.com",
            "scope": "org"
          },
          "trusted_domain_shadowed_by": "block"
        },
        "content": {
          "text": "Thanks for the quick reply.",
          "html": "<p>Thanks for the quick reply.</p>",
          "segments": [{}],
          "hidden_text_found": false,
          "zero_width_found": false,
          "tracking_pixels": 1,
          "agent_instructions_detected": false,
          "warnings": ["string"]
        },
        "links": [
          {
            "href": "string",
            "text": "Thanks for the quick reply.",
            "final_url": "https://example.com",
            "domain": "example.com",
            "display_mismatch": false,
            "shortener": false,
            "suspicious": false,
            "reasons": []
          }
        ],
        "attachments": [
          {
            "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
            "filename": "invoice.pdf",
            "content_type": "application/pdf",
            "risk": "low",
            "reasons": [],
            "extracted_text": "Thanks for the quick reply."
          }
        ],
        "classification": {
          "is_spam": 0.8,
          "is_phishing": 0.8,
          "contains_instructions_to_agent": 0.8,
          "requests_credentials_or_payment": 0.8,
          "urgency": 0.8,
          "category": "transactional",
          "language": "string",
          "backend": "string",
          "input_hash": "string",
          "cached": false
        },
        "provider_verdicts": {
          "spam": "pass",
          "virus": "pass",
          "source": "string"
        },
        "reasons": ["string"],
        "computed_at": "2026-10-01T12:00:00.000Z"
      }
    }
  ]
}
```

## Update Thread in Pod

```http
PATCH /v0/pods/{pod_id}/threads/{thread_id}
```

Same as Update Thread (`PATCH /v0/threads/{thread_id}`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `message_update`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `thread_id` | `string` | required | ID of the thread. |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `add_labels` | `string \| string[]` |  | Label or labels to add to every message in the thread. |
| `remove_labels` | `string \| string[]` |  | Label or labels to remove from every message in the thread. Applied after add_labels. |

#### Response

**200** The updated thread, without messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                           |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                        |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                  |
| `402`  | Free plan: removing SPAM or QUARANTINE makes the message count toward the messages cap and storage from now on, and the organization is over one (limit\_key messages\_per\_month, messages\_per\_day or storage\_bytes). The labels are unchanged. Paid plans are never refused. |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register).                                            |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                              |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                                                              |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                          |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X PATCH "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "add_labels": "string"
}'
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
}
```

## Delete Thread in Pod

```http
DELETE /v0/pods/{pod_id}/threads/{thread_id}
```

Same as Delete Thread (`DELETE /v0/threads/{thread_id}`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_delete`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `thread_id` | `string` | required | ID of the thread. |

#### Response

**200** Deleted (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `ok` | `true` | required |  |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X DELETE "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "ok": true
}
```

## List Threads

```http
GET /v0/threads
```

Threads, most recently active first. Pass `q` to keep only threads with a message matching the words.

Threads with a spam, blocked, trash, quarantined or unauthenticated message are hidden unless you pass the matching `include_*` flag or ask for the label in `labels`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only items with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `include_spam` | `boolean` |  | Include spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |
| `q` | `string` |  | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. |

#### Response

**200** A page of threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/threads" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Search Threads

```http
GET /v0/threads/search
```

Threads with a message whose subject, sender, recipients, body or attachment file names contain every word of `q`, ordered by thread activity (not ranked by relevance). Scans the newest 500 matching messages; the response has `truncated: true` when it hit that cap.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `q` | `string` | required | Full-text filter: threads with a message whose subject, sender, recipients, body or attachment file names contain the words. Scans the newest 500 matching messages. (min length 1) |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `before` | `string (date-time)` |  | Only items with a timestamp before this time. |
| `after` | `string (date-time)` |  | Only items with a timestamp after this time. |
| `ascending` | `boolean` |  | Sort oldest first. |
| `labels` | `string[]` |  | Only threads with any of these labels. Asking for SPAM, BLOCKED, TRASH, QUARANTINE or UNAUTHENTICATED includes them. |
| `include_spam` | `boolean` |  | Include threads with spam (needs label_spam_read; ignored without it). |
| `include_blocked` | `boolean` |  | Include threads with blocked mail (needs label_blocked_read; ignored without it). |
| `include_unauthenticated` | `boolean` |  | Include threads with unauthenticated mail (needs label_unauthenticated_read; ignored without it). |
| `include_trash` | `boolean` |  | Include threads with trash (needs label_trash_read; ignored without it). |
| `include_quarantine` | `boolean` |  | Include threads that have a quarantined message. |

#### Response

**200** A page of matching threads (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `threads` | `ThreadItem[]` | required | Matching threads, ordered by timestamp, newest first unless ascending is set. |
| `truncated` | `true` |  | Present when q matched more than 500 messages: only the threads of the newest 500 matching messages were considered. |

Each item of `threads` is a [ThreadItem](https://machinemail.dev/docs/reference/api/objects#threaditem):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/threads/search?q=invoice" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "threads": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "received_timestamp": "2026-10-01T12:00:00.000Z",
      "sent_timestamp": "2026-10-01T12:00:00.000Z",
      "senders": ["customer@example.com"],
      "recipients": ["agent@machinemail.dev"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "message_count": 1,
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
    }
  ],
  "truncated": true
}
```

## Get Thread

```http
GET /v0/threads/{thread_id}
```

The thread with its messages, which carry their bodies and trust reports. Messages come in pages of `limit` (default and maximum 500): the first page has the newest messages, oldest first within the page; follow `next_page_token` for older ones.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_read`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `thread_id` | `string` | required | ID of the thread. |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of messages to return. Default and maximum 500. |
| `page_token` | `string` |  | next_page_token from the previous response: the next, older page of messages. |

#### Response

**200** The thread with a page of its messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `count` | `integer` | required | Number of messages in this page. |
| `limit` | `integer` |  | Maximum number of messages requested for this page. |
| `next_page_token` | `string` |  | Token for the next, older page of messages. Absent when this page completes the thread. |
| `messages` | `Message[]` | required | Messages in this page, oldest first. The first page has the newest messages; follow next_page_token for older ones. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

Each item of `messages` is a [Message](https://machinemail.dev/docs/reference/api/objects#message):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `message_id` | `string` | required | ID of the message. |
| `labels` | `string[]` | required | Labels of the message. |
| `timestamp` | `string (date-time)` | required | Time at which the message was sent or received. |
| `from` | `string` | required | Address of the sender. In format username@domain.com or Display Name <username@domain.com>. |
| `to` | `string[]` | required | Addresses of the recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `cc` | `string[]` |  | Addresses of the CC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `bcc` | `string[]` |  | Addresses of the BCC recipients. In format username@domain.com or Display Name <username@domain.com>. |
| `subject` | `string` |  | Subject of the message. |
| `preview` | `string` |  | Text preview of the message. |
| `attachments` | `Attachment[]` |  | Attachments of the message. |
| `in_reply_to` | `string` |  | ID of the message being replied to. |
| `references` | `string[]` |  | IDs of previous messages in the thread. |
| `headers` | `Record<string, string>` |  | Headers of the message. |
| `authentication_results` | `MessageAuthenticationResults` |  | SPF, DKIM and DMARC verdicts for a received message, each with the domain it is about, as recorded when the message reached MachineMail: by the receiving mail provider (for example Amazon SES), or by the Authentication-Results header of the server that delivered it to MachineMail. A mechanism that was not evaluated is absent. The whole field is absent on sent messages, on messages injected through POST /inbound/raw (their headers are not believed), when nothing was evaluated, and on messages received before the field was introduced. As in AgentMail, only verdicts that speak for the sender are kept. spf is present only when spf_domain is aligned with the sender (shares an organizational domain with the From domain, or with the Sender domain when From spans several domains): an unaligned SPF result is withheld whatever its value. dkim and dmarc are withheld when From spans more than one domain or could not be parsed. Unlike the trust report (trust.authentication), which collapses each result to the values MachineMail scores on and adds aligned, these are the uncollapsed results: permerror and temperror stay as they are. dkim_domain is the signing domain as written: compare it with the From domain before treating a DKIM pass as proof of the sender, and use trust.level (or the UNAUTHENTICATED label) to decide whether to act on the message. Do not reject mail on dmarc: fail alone: forwarding breaks it. |
| `size` | `integer` | required | Size of the message in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the message was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the message was created. |
| `pod_id` | `string` | required | ID of the pod. |
| `direction` | `"inbound" \| "outbound"` | required | Whether the message was received or sent. |
| `delivery_status` | `"sent" \| "delivered" \| "bounced" \| "complained"` |  | Outbound only: last delivery state reported by the transport. |
| `trust_level` | `"verified" \| "known" \| "unknown" \| "suspicious" \| "quarantined"` |  | Inbound only: trust.level, for list badges. |
| `reply_to` | `string[]` |  | Reply-to addresses. In format username@domain.com or Display Name <username@domain.com>. |
| `text` | `string` |  | Plain text body. Untrusted: agents should read trust.content.text or extracted_text. |
| `html` | `string` |  | HTML body. Untrusted. |
| `extracted_text` | `string` |  | New text content, without quoted replies. |
| `extracted_html` | `string` |  | New HTML content, without quoted replies. |
| `received_via` | `"raw"` |  | raw when the message was injected with POST /inbound/raw (authentication not verified). |
| `held_at` | `string (date-time)` |  | When the message arrived, for inbound mail that was held because the organization reached a Free plan limit. created_at is then when it was delivered (released). |
| `trust` | `TrustReport` |  | Trust report of an inbound message. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "messages": [
    {
      "inbox_id": "agent@machinemail.dev",
      "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
      "message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "labels": ["UNREAD"],
      "timestamp": "2026-10-01T12:00:00.000Z",
      "from": "Support Agent <agent@machinemail.dev>",
      "to": ["customer@example.com"],
      "cc": ["team@example.com"],
      "bcc": ["audit@example.com"],
      "subject": "Order #1042",
      "preview": "Thanks for the quick reply.",
      "attachments": [
        {
          "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
          "filename": "invoice.pdf",
          "size": 2048,
          "content_type": "application/pdf",
          "content_disposition": "string",
          "content_id": "string"
        }
      ],
      "in_reply_to": "msg_01k6x3a1g00000d0m6pt2y5f7h",
      "references": ["string"],
      "headers": {
        "key": "value"
      },
      "authentication_results": {
        "spf": "pass",
        "dkim": "pass",
        "dmarc": "pass",
        "spf_domain": "string",
        "dkim_domain": "string",
        "dmarc_domain": "string",
        "arc_sealer": "string"
      },
      "size": 2048,
      "updated_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "direction": "inbound",
      "delivery_status": "sent",
      "trust_level": "verified",
      "reply_to": ["agent@machinemail.dev"],
      "text": "Thanks for the quick reply.",
      "html": "<p>Thanks for the quick reply.</p>",
      "extracted_text": "Thanks for the quick reply.",
      "extracted_html": "<p>Thanks for the quick reply.</p>",
      "received_via": "raw",
      "held_at": "2026-10-01T12:00:00.000Z",
      "trust": {
        "level": "verified",
        "score": 0.8,
        "basis": "trusted_domain",
        "authentication": {
          "spf": "pass",
          "dkim": "pass",
          "dmarc": "pass",
          "aligned": false
        },
        "sender": {
          "address": "agent@machinemail.dev",
          "domain": "example.com",
          "prior_correspondence": false,
          "on_allow_list": false,
          "on_block_list": false,
          "free_mail_provider": false,
          "trusted_domain": {
            "id": "string",
            "domain": "example.com",
            "scope": "org"
          },
          "trusted_domain_shadowed_by": "block"
        },
        "content": {
          "text": "Thanks for the quick reply.",
          "html": "<p>Thanks for the quick reply.</p>",
          "segments": [{}],
          "hidden_text_found": false,
          "zero_width_found": false,
          "tracking_pixels": 1,
          "agent_instructions_detected": false,
          "warnings": ["string"]
        },
        "links": [
          {
            "href": "string",
            "text": "Thanks for the quick reply.",
            "final_url": "https://example.com",
            "domain": "example.com",
            "display_mismatch": false,
            "shortener": false,
            "suspicious": false,
            "reasons": []
          }
        ],
        "attachments": [
          {
            "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
            "filename": "invoice.pdf",
            "content_type": "application/pdf",
            "risk": "low",
            "reasons": [],
            "extracted_text": "Thanks for the quick reply."
          }
        ],
        "classification": {
          "is_spam": 0.8,
          "is_phishing": 0.8,
          "contains_instructions_to_agent": 0.8,
          "requests_credentials_or_payment": 0.8,
          "urgency": 0.8,
          "category": "transactional",
          "language": "string",
          "backend": "string",
          "input_hash": "string",
          "cached": false
        },
        "provider_verdicts": {
          "spam": "pass",
          "virus": "pass",
          "source": "string"
        },
        "reasons": ["string"],
        "computed_at": "2026-10-01T12:00:00.000Z"
      }
    }
  ]
}
```

## Update Thread

```http
PATCH /v0/threads/{thread_id}
```

Adds and removes labels on every message in the thread (up to 500 messages); the thread labels are their union. Labels are free strings, system labels included. Returns the updated thread without messages.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `message_update`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `thread_id` | `string` | required | ID of the thread. |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `add_labels` | `string \| string[]` |  | Label or labels to add to every message in the thread. |
| `remove_labels` | `string \| string[]` |  | Label or labels to remove from every message in the thread. Applied after add_labels. |

#### Response

**200** The updated thread, without messages (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `thread_id` | `string` | required | ID of the thread. |
| `labels` | `string[]` | required | Labels of the thread: the union of the labels of its messages. |
| `timestamp` | `string (date-time)` | required | Timestamp of the last sent or received message. |
| `received_timestamp` | `string (date-time)` |  | Timestamp of the last received message. |
| `sent_timestamp` | `string (date-time)` |  | Timestamp of the last sent message. |
| `senders` | `string[]` | required | Senders in the thread. In format username@domain.com. |
| `recipients` | `string[]` | required | Recipients (to and cc) in the thread. In format username@domain.com. |
| `subject` | `string` |  | Subject of the thread. |
| `preview` | `string` |  | Text preview of the last message in the thread. |
| `attachments` | `Attachment[]` |  | Attachments in the thread. |
| `last_message_id` | `string` | required | ID of the last message in the thread. |
| `message_count` | `integer` | required | Number of messages in the thread. |
| `size` | `integer` | required | Size of the thread in bytes. |
| `updated_at` | `string (date-time)` | required | Time at which the thread was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the thread was created. |
| `pod_id` | `string` | required | ID of the pod. |

Each item of `attachments` is a [Attachment](https://machinemail.dev/docs/reference/api/objects#attachment):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `attachment_id` | `string` | required | ID of the attachment. |
| `filename` | `string` |  | Filename of the attachment. |
| `size` | `integer` | required | Size of the attachment in bytes. |
| `content_type` | `string` |  | Content type of the attachment. |
| `content_disposition` | `string` |  | Content disposition of the attachment (inline or attachment). |
| `content_id` | `string` |  | Content ID of the attachment. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                           |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                        |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                  |
| `402`  | Free plan: removing SPAM or QUARANTINE makes the message count toward the messages cap and storage from now on, and the organization is over one (limit\_key messages\_per\_month, messages\_per\_day or storage\_bytes). The labels are unchanged. Paid plans are never refused. |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register).                                            |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                              |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                                                              |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                          |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X PATCH "https://api.machinemail.dev/v0/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "add_labels": "string"
}'
```

```json title="Response 200"
{
  "inbox_id": "agent@machinemail.dev",
  "thread_id": "thd_01k6x3a1fz0000c9k5ns1x4e6g",
  "labels": ["UNREAD"],
  "timestamp": "2026-10-01T12:00:00.000Z",
  "received_timestamp": "2026-10-01T12:00:00.000Z",
  "sent_timestamp": "2026-10-01T12:00:00.000Z",
  "senders": ["customer@example.com"],
  "recipients": ["agent@machinemail.dev"],
  "subject": "Order #1042",
  "preview": "Thanks for the quick reply.",
  "attachments": [
    {
      "attachment_id": "att_01k6x3h7qv0000m7v3xb9f2p4r",
      "filename": "invoice.pdf",
      "size": 2048,
      "content_type": "application/pdf",
      "content_disposition": "string",
      "content_id": "string"
    }
  ],
  "last_message_id": "msg_01k6x3a1g00000d0m6pt2y5f7h",
  "message_count": 1,
  "size": 2048,
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f"
}
```

## Delete Thread

```http
DELETE /v0/threads/{thread_id}
```

Permanently deletes the thread and its messages (up to 500).

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `thread_delete`.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `thread_id` | `string` | required | ID of the thread. |

#### Response

**200** Deleted (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `ok` | `true` | required |  |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](https://machinemail.dev/docs/reference/errors).

#### Example

```bash title="Request"
curl -X DELETE "https://api.machinemail.dev/v0/threads/thd_01k6x3a1fz0000c9k5ns1x4e6g" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "ok": true
}
```
