---
title: "Trusted Domains"
description: "Domains and addresses the organization, a pod or an inbox trusts: mail they genuinely sign is never quarantined or withheld from agents. Preview an entry first."
canonical: "https://machinemail.dev/docs/reference/api/trusted-domains"
---

# Trusted Domains

> Domains and addresses the organization, a pod or an inbox trusts: mail they genuinely sign is never quarantined or withheld from agents. Preview an entry first.

Source: https://machinemail.dev/docs/reference/api/trusted-domains

| Method   | Path                                                         | Summary                                                               |
| -------- | ------------------------------------------------------------ | --------------------------------------------------------------------- |
| `GET`    | `/v0/inboxes/{inbox_id}/trusted-domains`                     | [List Trusted Domains in Inbox](#list-trusted-domains-in-inbox)       |
| `POST`   | `/v0/inboxes/{inbox_id}/trusted-domains`                     | [Create Trusted Domain in Inbox](#create-trusted-domain-in-inbox)     |
| `POST`   | `/v0/inboxes/{inbox_id}/trusted-domains/preview`             | [Preview Trusted Domain in Inbox](#preview-trusted-domain-in-inbox)   |
| `GET`    | `/v0/inboxes/{inbox_id}/trusted-domains/suggestions`         | [Suggest Trusted Domains in Inbox](#suggest-trusted-domains-in-inbox) |
| `GET`    | `/v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}` | [Get Trusted Domain in Inbox](#get-trusted-domain-in-inbox)           |
| `PATCH`  | `/v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}` | [Update Trusted Domain in Inbox](#update-trusted-domain-in-inbox)     |
| `DELETE` | `/v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}` | [Delete Trusted Domain in Inbox](#delete-trusted-domain-in-inbox)     |
| `GET`    | `/v0/pods/{pod_id}/trusted-domains`                          | [List Trusted Domains in Pod](#list-trusted-domains-in-pod)           |
| `POST`   | `/v0/pods/{pod_id}/trusted-domains`                          | [Create Trusted Domain in Pod](#create-trusted-domain-in-pod)         |
| `POST`   | `/v0/pods/{pod_id}/trusted-domains/preview`                  | [Preview Trusted Domain in Pod](#preview-trusted-domain-in-pod)       |
| `GET`    | `/v0/pods/{pod_id}/trusted-domains/suggestions`              | [Suggest Trusted Domains in Pod](#suggest-trusted-domains-in-pod)     |
| `GET`    | `/v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}`      | [Get Trusted Domain in Pod](#get-trusted-domain-in-pod)               |
| `PATCH`  | `/v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}`      | [Update Trusted Domain in Pod](#update-trusted-domain-in-pod)         |
| `DELETE` | `/v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}`      | [Delete Trusted Domain in Pod](#delete-trusted-domain-in-pod)         |
| `GET`    | `/v0/trusted-domains`                                        | [List Trusted Domains](#list-trusted-domains)                         |
| `POST`   | `/v0/trusted-domains`                                        | [Create Trusted Domain](#create-trusted-domain)                       |
| `POST`   | `/v0/trusted-domains/preview`                                | [Preview Trusted Domain](#preview-trusted-domain)                     |
| `GET`    | `/v0/trusted-domains/suggestions`                            | [Suggest Trusted Domains](#suggest-trusted-domains)                   |
| `GET`    | `/v0/trusted-domains/{trusted_domain_id}`                    | [Get Trusted Domain](#get-trusted-domain)                             |
| `PATCH`  | `/v0/trusted-domains/{trusted_domain_id}`                    | [Update Trusted Domain](#update-trusted-domain)                       |
| `DELETE` | `/v0/trusted-domains/{trusted_domain_id}`                    | [Delete Trusted Domain](#delete-trusted-domain)                       |

## List Trusted Domains in Inbox

```http
GET /v0/inboxes/{inbox_id}/trusted-domains
```

Same as List Trusted Domains (`GET /v0/trusted-domains`), limited to the inbox.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |

#### 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` |  | Oldest first. |

#### Response

**200** A page of trusted domains (`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. |
| `trusted_domains` | `TrustedDomain[]` | required | Entries stored at exactly this level (organization, pod or inbox), newest first unless ascending is set. |

Each item of `trusted_domains` is a [TrustedDomain](https://machinemail.dev/docs/reference/api/objects#trusteddomain):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |

#### 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/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "trusted_domains": [
    {
      "trusted_domain_id": "string",
      "entry_type": "domain",
      "domain": "example.com",
      "domain_unicode": "string",
      "include_subdomains": false,
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "reason": "string",
      "expires_at": "2026-10-01T12:00:00.000Z",
      "created_by": {
        "type": "user",
        "id": "string"
      },
      "match_count": 1,
      "last_matched_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "updated_at": "2026-10-01T12:00:00.000Z"
    }
  ]
}
```

## Create Trusted Domain in Inbox

```http
POST /v0/inboxes/{inbox_id}/trusted-domains
```

Same as Create Trusted Domain (`POST /v0/trusted-domains`), limited to the inbox.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | A domain (example.com) or an exact address (invoices@example.com). Unicode names are accepted and stored in ASCII (punycode) form. (min length 1, max length 320) |
| `include_subdomains` | `boolean` |  | Also trust mail signed by subdomains. Default false. Not allowed for an address. |
| `reason` | `string` |  | Why you trust it, for people reading the list. Never included in a message’s trust report. (max length 500) |
| `expires_at` | `string (date-time)` |  | When the entry stops applying. Must be in the future. Omit for never. |
| `confirm` | `boolean` |  | Required (true) for platforms where anyone can write the content (github.com, google.com, slack.com and similar, or one under the domain with include_subdomains) and for one address on a group or marketplace relay (team@googlegroups.com): without it the answer is 422 confirmation_required. |
| `client_id` | `string` |  | Idempotency key: a create with a client_id already used in the organization returns that entry (200). |
| `apply_to_existing` | `boolean` |  | Also release mail held over the last 30 days that the new entry now trusts: mail held for sender or authentication-history reasons that carries the domain’s own DKIM signature. Never mail held for malware, instructions to agents or phishing, mail a person quarantined, or mail a block or allow-only scope stops. Released mail emits message.released, not message.received. Default false. |

#### Response

**200** The existing entry for this domain or client\_id (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

**201** Created (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                                                                                  |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                                                                               |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                                                                         |
| `402`  | The organization already has as many trusted domains as its plan allows (limit\_key: "trusted\_domains"): 10 on Free, 500 on paid plans, none for unclaimed sandbox agents. Remove one first.                                                                                                                                            |
| `403`  | The credential lacks trusted\_domain\_create, or is not an organization API key or a dashboard session. Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get 403, whatever their permissions, so an agent cannot be talked into trusting a domain. |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                                                                                     |
| `409`  | client\_id is already used at another level.                                                                                                                                                                                                                                                                                             |
| `422`  | trusted\_domain\_not\_allowed (with error.reason) or confirmation\_required (resend with confirm: true).                                                                                                                                                                                                                                 |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                                                                                 |

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

#### Example

```bash title="Request"
curl -X POST "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "example.com",
  "reason": "string"
}'
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z",
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "released": {
    "count": 1
  }
}
```

## Preview Trusted Domain in Inbox

```http
POST /v0/inboxes/{inbox_id}/trusted-domains/preview
```

Same as Preview Trusted Domain (`POST /v0/trusted-domains/preview`), limited to the inbox.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | A domain (example.com) or an exact address (invoices@example.com). Unicode names are accepted and stored in ASCII (punycode) form. (min length 1, max length 320) |
| `include_subdomains` | `boolean` |  | As for create. |

#### Response

**200** The preview (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string \| null` | required | The entry as it would be stored (ASCII form). null when it is not a domain or address. |
| `domain_unicode` | `string \| null` | required | The same in Unicode form. |
| `entry_type` | `object` | required |  |
| `allowed` | `boolean` | required | Whether a create would be accepted (with confirm: true when confirmation_required), plan limit aside. |
| `refusal` | `("free_mail" \| "privacy_relay" \| "shared_platform" \| "public_suffix" \| "disposable" \| "invalid" \| "confusable_idn" \| null) \| null` | required | Why it cannot be trusted. null when it can. |
| `message` | `string \| null` | required | The refusal or confirmation, in one sentence. null when there is neither. |
| `confirmation_required` | `boolean` | required | A platform where anyone can write the content, or a relay address: a create needs confirm: true. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes, as a create would report them. |
| `would_release` | `object \| null` | required | Held messages of the last 30 days that apply_to_existing would release at this level. null when the entry is not allowed. |
| `suggestions` | `string[]` | required | Narrower entries to consider instead: exact addresses that have written from this domain, most frequent first, at most 5. Empty when there are none. |
| `evidence` | `Evidence` | required | What MachineMail has seen of the domain, for warnings before trusting it. All false, null or 0 when the entry is not a domain or address. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

`evidence` is a [Evidence](https://machinemail.dev/docs/reference/api/objects#evidence):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `dkim_seen` | `boolean` | required | Mail of the last 90 days at this level carried its own DKIM signature, verified by MachineMail. When false, trust applies only to mail it signs. |
| `first_contact` | `boolean` | required | No mail from it has reached this level in the last 90 days. |
| `lookalike_of` | `string \| null` | required | A well-known brand domain or an existing trusted entry this name imitates (confusable characters or a near match), else null. |
| `recent_auth_failures` | `integer` | required | Messages from it in the last 30 days whose DKIM or DMARC check failed, or that failed a trusted entry’s check. |

#### 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 -X POST "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/trusted-domains/preview" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "example.com"
}'
```

```json title="Response 200"
{
  "domain": "example.com",
  "domain_unicode": "string",
  "entry_type": {},
  "allowed": false,
  "refusal": "free_mail",
  "message": "string",
  "confirmation_required": false,
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "would_release": {
    "count": 1
  },
  "suggestions": ["string"],
  "evidence": {
    "dkim_seen": false,
    "first_contact": false,
    "lookalike_of": "string",
    "recent_auth_failures": 1
  }
}
```

## Suggest Trusted Domains in Inbox

```http
GET /v0/inboxes/{inbox_id}/trusted-domains/suggestions
```

Same as Suggest Trusted Domains (`GET /v0/trusted-domains/suggestions`), limited to the inbox.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Most suggestions to return. Default 10, maximum 50; larger values are capped. |

#### Response

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

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of suggestions returned. |
| `suggestions` | `Suggestion[]` | required | Most messages first. |

Each item of `suggestions` is a [Suggestion](https://machinemail.dev/docs/reference/api/objects#suggestion):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, as a create would take it (ASCII form). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `message_count` | `integer` | required | Messages exchanged with it in the last 90 days: received with its own verified DKIM signature, plus sent to it. |
| `last_seen_at` | `string (date-time)` | required | When the last of those messages was received or sent. |
| `dkim_seen` | `boolean` | required | Mail from it carried its own DKIM signature that our server verified. |

#### 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/trusted-domains/suggestions" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "suggestions": [
    {
      "domain": "example.com",
      "entry_type": "domain",
      "message_count": 1,
      "last_seen_at": "2026-10-01T12:00:00.000Z",
      "dkim_seen": false
    }
  ]
}
```

## Get Trusted Domain in Inbox

```http
GET /v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}
```

Same as Get Trusted Domain (`GET /v0/trusted-domains/{trusted_domain_id}`), limited to the inbox.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Response

**200** The entry (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |

#### 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.                                                                                                                                                                   |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z"
}
```

## Update Trusted Domain in Inbox

```http
PATCH /v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}
```

Same as Update Trusted Domain (`PATCH /v0/trusted-domains/{trusted_domain_id}`), limited to the inbox.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `include_subdomains` | `boolean` |  | Also trust mail signed by subdomains. Not allowed for an address. |
| `confirm` | `boolean` |  | Required (true) to turn on include_subdomains when a platform where anyone can write the content lies under the domain: without it the answer is 422 confirmation_required. |
| `reason` | `string \| null` |  | New reason; null clears it. (max length 500) |
| `expires_at` | `string (date-time) \| null` |  | New expiry, in the future; null removes it. |

#### Response

**200** The updated entry (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

#### 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 trusted\_domain\_update, or is not an organization API key or a dashboard session. Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get 403, whatever their permissions, so an agent cannot be talked into trusting a domain. |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                                                                                     |
| `422`  | Turning on include\_subdomains: trusted\_domain\_not\_allowed (with error.reason) or confirmation\_required (resend with confirm: true).                                                                                                                                                                                                 |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "string"
}'
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z",
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "released": {
    "count": 1
  }
}
```

## Delete Trusted Domain in Inbox

```http
DELETE /v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}
```

Same as Delete Trusted Domain (`DELETE /v0/trusted-domains/{trusted_domain_id}`), limited to the inbox.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `inbox_id` | `string` | required | ID of the inbox. |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Response

**204** Deleted

#### 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.                                                                                                                                                                   |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

## List Trusted Domains in Pod

```http
GET /v0/pods/{pod_id}/trusted-domains
```

Same as List Trusted Domains (`GET /v0/trusted-domains`), limited to the pod.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `trusted_domain_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` |  | Oldest first. |

#### Response

**200** A page of trusted domains (`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. |
| `trusted_domains` | `TrustedDomain[]` | required | Entries stored at exactly this level (organization, pod or inbox), newest first unless ascending is set. |

Each item of `trusted_domains` is a [TrustedDomain](https://machinemail.dev/docs/reference/api/objects#trusteddomain):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |

#### 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/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "trusted_domains": [
    {
      "trusted_domain_id": "string",
      "entry_type": "domain",
      "domain": "example.com",
      "domain_unicode": "string",
      "include_subdomains": false,
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "reason": "string",
      "expires_at": "2026-10-01T12:00:00.000Z",
      "created_by": {
        "type": "user",
        "id": "string"
      },
      "match_count": 1,
      "last_matched_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "updated_at": "2026-10-01T12:00:00.000Z"
    }
  ]
}
```

## Create Trusted Domain in Pod

```http
POST /v0/pods/{pod_id}/trusted-domains
```

Same as Create Trusted Domain (`POST /v0/trusted-domains`), limited to the pod.

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

#### Path parameters

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

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | A domain (example.com) or an exact address (invoices@example.com). Unicode names are accepted and stored in ASCII (punycode) form. (min length 1, max length 320) |
| `include_subdomains` | `boolean` |  | Also trust mail signed by subdomains. Default false. Not allowed for an address. |
| `reason` | `string` |  | Why you trust it, for people reading the list. Never included in a message’s trust report. (max length 500) |
| `expires_at` | `string (date-time)` |  | When the entry stops applying. Must be in the future. Omit for never. |
| `confirm` | `boolean` |  | Required (true) for platforms where anyone can write the content (github.com, google.com, slack.com and similar, or one under the domain with include_subdomains) and for one address on a group or marketplace relay (team@googlegroups.com): without it the answer is 422 confirmation_required. |
| `client_id` | `string` |  | Idempotency key: a create with a client_id already used in the organization returns that entry (200). |
| `apply_to_existing` | `boolean` |  | Also release mail held over the last 30 days that the new entry now trusts: mail held for sender or authentication-history reasons that carries the domain’s own DKIM signature. Never mail held for malware, instructions to agents or phishing, mail a person quarantined, or mail a block or allow-only scope stops. Released mail emits message.released, not message.received. Default false. |

#### Response

**200** The existing entry for this domain or client\_id (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

**201** Created (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                                                                                  |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                                                                               |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                                                                         |
| `402`  | The organization already has as many trusted domains as its plan allows (limit\_key: "trusted\_domains"): 10 on Free, 500 on paid plans, none for unclaimed sandbox agents. Remove one first.                                                                                                                                            |
| `403`  | The credential lacks trusted\_domain\_create, or is not an organization API key or a dashboard session. Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get 403, whatever their permissions, so an agent cannot be talked into trusting a domain. |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                                                                                     |
| `409`  | client\_id is already used at another level.                                                                                                                                                                                                                                                                                             |
| `422`  | trusted\_domain\_not\_allowed (with error.reason) or confirmation\_required (resend with confirm: true).                                                                                                                                                                                                                                 |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                                                                                 |

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

#### Example

```bash title="Request"
curl -X POST "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "example.com",
  "reason": "string"
}'
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z",
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "released": {
    "count": 1
  }
}
```

## Preview Trusted Domain in Pod

```http
POST /v0/pods/{pod_id}/trusted-domains/preview
```

Same as Preview Trusted Domain (`POST /v0/trusted-domains/preview`), limited to the pod.

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

#### Path parameters

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

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | A domain (example.com) or an exact address (invoices@example.com). Unicode names are accepted and stored in ASCII (punycode) form. (min length 1, max length 320) |
| `include_subdomains` | `boolean` |  | As for create. |

#### Response

**200** The preview (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string \| null` | required | The entry as it would be stored (ASCII form). null when it is not a domain or address. |
| `domain_unicode` | `string \| null` | required | The same in Unicode form. |
| `entry_type` | `object` | required |  |
| `allowed` | `boolean` | required | Whether a create would be accepted (with confirm: true when confirmation_required), plan limit aside. |
| `refusal` | `("free_mail" \| "privacy_relay" \| "shared_platform" \| "public_suffix" \| "disposable" \| "invalid" \| "confusable_idn" \| null) \| null` | required | Why it cannot be trusted. null when it can. |
| `message` | `string \| null` | required | The refusal or confirmation, in one sentence. null when there is neither. |
| `confirmation_required` | `boolean` | required | A platform where anyone can write the content, or a relay address: a create needs confirm: true. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes, as a create would report them. |
| `would_release` | `object \| null` | required | Held messages of the last 30 days that apply_to_existing would release at this level. null when the entry is not allowed. |
| `suggestions` | `string[]` | required | Narrower entries to consider instead: exact addresses that have written from this domain, most frequent first, at most 5. Empty when there are none. |
| `evidence` | `Evidence` | required | What MachineMail has seen of the domain, for warnings before trusting it. All false, null or 0 when the entry is not a domain or address. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

`evidence` is a [Evidence](https://machinemail.dev/docs/reference/api/objects#evidence):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `dkim_seen` | `boolean` | required | Mail of the last 90 days at this level carried its own DKIM signature, verified by MachineMail. When false, trust applies only to mail it signs. |
| `first_contact` | `boolean` | required | No mail from it has reached this level in the last 90 days. |
| `lookalike_of` | `string \| null` | required | A well-known brand domain or an existing trusted entry this name imitates (confusable characters or a near match), else null. |
| `recent_auth_failures` | `integer` | required | Messages from it in the last 30 days whose DKIM or DMARC check failed, or that failed a trusted entry’s check. |

#### 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 -X POST "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/trusted-domains/preview" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "example.com"
}'
```

```json title="Response 200"
{
  "domain": "example.com",
  "domain_unicode": "string",
  "entry_type": {},
  "allowed": false,
  "refusal": "free_mail",
  "message": "string",
  "confirmation_required": false,
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "would_release": {
    "count": 1
  },
  "suggestions": ["string"],
  "evidence": {
    "dkim_seen": false,
    "first_contact": false,
    "lookalike_of": "string",
    "recent_auth_failures": 1
  }
}
```

## Suggest Trusted Domains in Pod

```http
GET /v0/pods/{pod_id}/trusted-domains/suggestions
```

Same as Suggest Trusted Domains (`GET /v0/trusted-domains/suggestions`), limited to the pod.

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

#### Path parameters

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

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Most suggestions to return. Default 10, maximum 50; larger values are capped. |

#### Response

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

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of suggestions returned. |
| `suggestions` | `Suggestion[]` | required | Most messages first. |

Each item of `suggestions` is a [Suggestion](https://machinemail.dev/docs/reference/api/objects#suggestion):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, as a create would take it (ASCII form). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `message_count` | `integer` | required | Messages exchanged with it in the last 90 days: received with its own verified DKIM signature, plus sent to it. |
| `last_seen_at` | `string (date-time)` | required | When the last of those messages was received or sent. |
| `dkim_seen` | `boolean` | required | Mail from it carried its own DKIM signature that our server verified. |

#### 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/trusted-domains/suggestions" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "suggestions": [
    {
      "domain": "example.com",
      "entry_type": "domain",
      "message_count": 1,
      "last_seen_at": "2026-10-01T12:00:00.000Z",
      "dkim_seen": false
    }
  ]
}
```

## Get Trusted Domain in Pod

```http
GET /v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}
```

Same as Get Trusted Domain (`GET /v0/trusted-domains/{trusted_domain_id}`), limited to the pod.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Response

**200** The entry (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |

#### 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.                                                                                                                                                                   |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z"
}
```

## Update Trusted Domain in Pod

```http
PATCH /v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}
```

Same as Update Trusted Domain (`PATCH /v0/trusted-domains/{trusted_domain_id}`), limited to the pod.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `include_subdomains` | `boolean` |  | Also trust mail signed by subdomains. Not allowed for an address. |
| `confirm` | `boolean` |  | Required (true) to turn on include_subdomains when a platform where anyone can write the content lies under the domain: without it the answer is 422 confirmation_required. |
| `reason` | `string \| null` |  | New reason; null clears it. (max length 500) |
| `expires_at` | `string (date-time) \| null` |  | New expiry, in the future; null removes it. |

#### Response

**200** The updated entry (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

#### 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 trusted\_domain\_update, or is not an organization API key or a dashboard session. Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get 403, whatever their permissions, so an agent cannot be talked into trusting a domain. |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                                                                                     |
| `422`  | Turning on include\_subdomains: trusted\_domain\_not\_allowed (with error.reason) or confirmation\_required (resend with confirm: true).                                                                                                                                                                                                 |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "string"
}'
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z",
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "released": {
    "count": 1
  }
}
```

## Delete Trusted Domain in Pod

```http
DELETE /v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}
```

Same as Delete Trusted Domain (`DELETE /v0/trusted-domains/{trusted_domain_id}`), limited to the pod.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `pod_id` | `string` | required | ID of the pod. |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Response

**204** Deleted

#### 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.                                                                                                                                                                   |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

## List Trusted Domains

```http
GET /v0/trusted-domains
```

Entries stored at exactly this level: `/v0/trusted-domains` lists the organization entries, `/v0/pods/{pod_id}/trusted-domains` a pod’s, `/v0/inboxes/{inbox_id}/trusted-domains` an inbox’s. Mail to an inbox is judged against all three.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: `trusted_domain_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` |  | Oldest first. |

#### Response

**200** A page of trusted domains (`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. |
| `trusted_domains` | `TrustedDomain[]` | required | Entries stored at exactly this level (organization, pod or inbox), newest first unless ascending is set. |

Each item of `trusted_domains` is a [TrustedDomain](https://machinemail.dev/docs/reference/api/objects#trusteddomain):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |

#### 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/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "trusted_domains": [
    {
      "trusted_domain_id": "string",
      "entry_type": "domain",
      "domain": "example.com",
      "domain_unicode": "string",
      "include_subdomains": false,
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "reason": "string",
      "expires_at": "2026-10-01T12:00:00.000Z",
      "created_by": {
        "type": "user",
        "id": "string"
      },
      "match_count": 1,
      "last_matched_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z",
      "updated_at": "2026-10-01T12:00:00.000Z"
    }
  ]
}
```

## Create Trusted Domain

```http
POST /v0/trusted-domains
```

Trust a domain (`example.com`) or one address (`invoices@example.com`) at this level. Domains are stored in ASCII (punycode) form; responses carry both forms. Adding an entry that exists at this level (the same domain, or the same `client_id`) returns it unchanged with `200`.

Trust takes effect only on mail that carries a DKIM signature of the trusted domain itself (`d=` equal to the domain, or a subdomain with `include_subdomains`); a block entry always wins over trust, and an allow-only scope still blocks every sender it does not allow.

Conflicts with block entries and allow-only scopes do not refuse the entry: they are returned as `warnings`.

With `apply_to_existing: true`, held mail of the last 30 days that the new entry vouches for is released (`released.count`): mail held for sender or authentication-history reasons that carries the domain’s own DKIM signature, never mail held for malware, instructions to agents or phishing, mail a person quarantined, or mail a block or allow-only scope stops. Each released message emits `message.released` (not `message.received`); a large backlog is released in the background after the response. An existing entry (`200`) releases nothing.

Refused with `422`: `trusted_domain_not_allowed` for names anyone can get mail from (`error.reason`: `free_mail`, `privacy_relay`, `shared_platform`, `public_suffix`, `disposable`, `invalid`, `confusable_idn`); `confirmation_required` for platforms where anyone can write the content (github.com, google.com, slack.com and similar, or one under the domain with `include_subdomains`) and for one address on a group or marketplace relay (`team@googlegroups.com`), until you resend with `confirm: true`. An address on a free mail provider (`someone@gmail.com`) can be trusted.

Refused with `402` `plan_limit_exceeded` (`limit_key: "trusted_domains"`) past the organization’s limit: 10 on Free, 500 on paid plans, none for unclaimed sandbox agents. Entries at every level count.

Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get `403`, whatever their permissions, so an agent cannot be talked into trusting a domain.

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

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | A domain (example.com) or an exact address (invoices@example.com). Unicode names are accepted and stored in ASCII (punycode) form. (min length 1, max length 320) |
| `include_subdomains` | `boolean` |  | Also trust mail signed by subdomains. Default false. Not allowed for an address. |
| `reason` | `string` |  | Why you trust it, for people reading the list. Never included in a message’s trust report. (max length 500) |
| `expires_at` | `string (date-time)` |  | When the entry stops applying. Must be in the future. Omit for never. |
| `confirm` | `boolean` |  | Required (true) for platforms where anyone can write the content (github.com, google.com, slack.com and similar, or one under the domain with include_subdomains) and for one address on a group or marketplace relay (team@googlegroups.com): without it the answer is 422 confirmation_required. |
| `client_id` | `string` |  | Idempotency key: a create with a client_id already used in the organization returns that entry (200). |
| `apply_to_existing` | `boolean` |  | Also release mail held over the last 30 days that the new entry now trusts: mail held for sender or authentication-history reasons that carries the domain’s own DKIM signature. Never mail held for malware, instructions to agents or phishing, mail a person quarantined, or mail a block or allow-only scope stops. Released mail emits message.released, not message.received. Default false. |

#### Response

**200** The existing entry for this domain or client\_id (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

**201** Created (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                                                                                  |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                                                                               |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                                                                         |
| `402`  | The organization already has as many trusted domains as its plan allows (limit\_key: "trusted\_domains"): 10 on Free, 500 on paid plans, none for unclaimed sandbox agents. Remove one first.                                                                                                                                            |
| `403`  | The credential lacks trusted\_domain\_create, or is not an organization API key or a dashboard session. Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get 403, whatever their permissions, so an agent cannot be talked into trusting a domain. |
| `409`  | client\_id is already used at another level.                                                                                                                                                                                                                                                                                             |
| `422`  | trusted\_domain\_not\_allowed (with error.reason) or confirmation\_required (resend with confirm: true).                                                                                                                                                                                                                                 |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                                                                                 |

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

#### Example

```bash title="Request"
curl -X POST "https://api.machinemail.dev/v0/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "example.com",
  "reason": "string"
}'
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z",
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "released": {
    "count": 1
  }
}
```

## Preview Trusted Domain

```http
POST /v0/trusted-domains/preview
```

What creating this entry at this level would do, without saving anything: whether it is allowed (and if not, why), whether it needs `confirm: true`, its warnings, the held mail of the last 30 days `apply_to_existing` would release, exact addresses to trust instead of a whole domain, and what MachineMail has seen of the domain (`evidence`: its own DKIM signature, first contact, a lookalike of a well-known brand or an existing trusted entry, recent authentication failures). For validating as someone types. Read from the most recent mail at this level.

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

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | A domain (example.com) or an exact address (invoices@example.com). Unicode names are accepted and stored in ASCII (punycode) form. (min length 1, max length 320) |
| `include_subdomains` | `boolean` |  | As for create. |

#### Response

**200** The preview (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string \| null` | required | The entry as it would be stored (ASCII form). null when it is not a domain or address. |
| `domain_unicode` | `string \| null` | required | The same in Unicode form. |
| `entry_type` | `object` | required |  |
| `allowed` | `boolean` | required | Whether a create would be accepted (with confirm: true when confirmation_required), plan limit aside. |
| `refusal` | `("free_mail" \| "privacy_relay" \| "shared_platform" \| "public_suffix" \| "disposable" \| "invalid" \| "confusable_idn" \| null) \| null` | required | Why it cannot be trusted. null when it can. |
| `message` | `string \| null` | required | The refusal or confirmation, in one sentence. null when there is neither. |
| `confirmation_required` | `boolean` | required | A platform where anyone can write the content, or a relay address: a create needs confirm: true. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes, as a create would report them. |
| `would_release` | `object \| null` | required | Held messages of the last 30 days that apply_to_existing would release at this level. null when the entry is not allowed. |
| `suggestions` | `string[]` | required | Narrower entries to consider instead: exact addresses that have written from this domain, most frequent first, at most 5. Empty when there are none. |
| `evidence` | `Evidence` | required | What MachineMail has seen of the domain, for warnings before trusting it. All false, null or 0 when the entry is not a domain or address. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

`evidence` is a [Evidence](https://machinemail.dev/docs/reference/api/objects#evidence):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `dkim_seen` | `boolean` | required | Mail of the last 90 days at this level carried its own DKIM signature, verified by MachineMail. When false, trust applies only to mail it signs. |
| `first_contact` | `boolean` | required | No mail from it has reached this level in the last 90 days. |
| `lookalike_of` | `string \| null` | required | A well-known brand domain or an existing trusted entry this name imitates (confusable characters or a near match), else null. |
| `recent_auth_failures` | `integer` | required | Messages from it in the last 30 days whose DKIM or DMARC check failed, or that failed a trusted entry’s check. |

#### 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 -X POST "https://api.machinemail.dev/v0/trusted-domains/preview" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "example.com"
}'
```

```json title="Response 200"
{
  "domain": "example.com",
  "domain_unicode": "string",
  "entry_type": {},
  "allowed": false,
  "refusal": "free_mail",
  "message": "string",
  "confirmation_required": false,
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "would_release": {
    "count": 1
  },
  "suggestions": ["string"],
  "evidence": {
    "dkim_seen": false,
    "first_contact": false,
    "lookalike_of": "string",
    "recent_auth_failures": 1
  }
}
```

## Suggest Trusted Domains

```http
GET /v0/trusted-domains/suggestions
```

Senders this level regularly corresponds with, most messages first: mail received in the last 90 days that carried the sender’s own DKIM signature (verified by MachineMail), and mail sent to them. Leaves out what is already trusted or blocked here. For free-mail providers and platforms where anyone can write the content, the exact address is suggested instead of the domain. Read from the most recent mail at this level.

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

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Most suggestions to return. Default 10, maximum 50; larger values are capped. |

#### Response

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

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of suggestions returned. |
| `suggestions` | `Suggestion[]` | required | Most messages first. |

Each item of `suggestions` is a [Suggestion](https://machinemail.dev/docs/reference/api/objects#suggestion):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, as a create would take it (ASCII form). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `message_count` | `integer` | required | Messages exchanged with it in the last 90 days: received with its own verified DKIM signature, plus sent to it. |
| `last_seen_at` | `string (date-time)` | required | When the last of those messages was received or sent. |
| `dkim_seen` | `boolean` | required | Mail from it carried its own DKIM signature that our server verified. |

#### 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/trusted-domains/suggestions" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "suggestions": [
    {
      "domain": "example.com",
      "entry_type": "domain",
      "message_count": 1,
      "last_seen_at": "2026-10-01T12:00:00.000Z",
      "dkim_seen": false
    }
  ]
}
```

## Get Trusted Domain

```http
GET /v0/trusted-domains/{trusted_domain_id}
```

An entry stored at this level (`404` for entries at another level).

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Response

**200** The entry (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |

#### 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.                                                                                                                                                                   |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z"
}
```

## Update Trusted Domain

```http
PATCH /v0/trusted-domains/{trusted_domain_id}
```

Change `include_subdomains`, `reason` or `expires_at`. The domain and level are fixed: delete and create to change them. Turning `include_subdomains` off rescores the mail from subdomains it marked trusted, as a delete does. Turning it on is checked like a create with it: refused (`422 trusted_domain_not_allowed`) when a privacy relay or MachineMail shared domain lies under the domain, and `422 confirmation_required` until you send `confirm: true` when a platform where anyone can write the content does. Returns the entry with its current `warnings`. Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get `403`, whatever their permissions, so an agent cannot be talked into trusting a domain.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `include_subdomains` | `boolean` |  | Also trust mail signed by subdomains. Not allowed for an address. |
| `confirm` | `boolean` |  | Required (true) to turn on include_subdomains when a platform where anyone can write the content lies under the domain: without it the answer is 422 confirmation_required. |
| `reason` | `string \| null` |  | New reason; null clears it. (max length 500) |
| `expires_at` | `string (date-time) \| null` |  | New expiry, in the future; null removes it. |

#### Response

**200** The updated entry (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |
| `entry_type` | `"domain" \| "email"` | required | domain: every address at the domain (and its subdomains with include_subdomains). email: exactly one address. |
| `domain` | `string` | required | The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form. |
| `domain_unicode` | `string` | required | The same, with the domain in its Unicode form. Equal to domain for ASCII names. |
| `include_subdomains` | `boolean` | required | Also trust mail signed by subdomains (billing.example.com for example.com). Always false for addresses. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string \| null` | required | The pod, for pod- and inbox-scoped entries. |
| `inbox_id` | `string \| null` | required | The inbox, for inbox-scoped entries. |
| `reason` | `string \| null` | required | Why it was added, for people reading the list. Never included in a message’s trust report. |
| `expires_at` | `string (date-time) \| null` | required | When the entry stops applying. null: never. |
| `created_by` | `object \| null` | required | Who added the entry. |
| `match_count` | `integer` | required | Messages this entry has marked trusted. |
| `last_matched_at` | `string (date-time) \| null` | required | When this entry last marked a message trusted. null: never. |
| `created_at` | `string (date-time)` | required | When the entry was added. |
| `updated_at` | `string (date-time)` | required | When the entry last changed. |
| `warnings` | `Warning[]` | required | Conflicts with block entries or allow-only scopes. Empty when there are none. |
| `released` | `object \| null` | required | Create with apply_to_existing: true: the held messages of the last 30 days this entry released (each emits message.released). null otherwise, and on update. |

Each item of `warnings` is a [Warning](https://machinemail.dev/docs/reference/api/objects#warning):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `"shadowed_by_block" \| "shadowed_by_allow_only"` | required | shadowed_by_block: a receive/block list entry matches some or all of the mail this entry trusts, and a block always wins over trust. shadowed_by_allow_only: the scope is allow-only (it has receive/allow entries) and none of them allows this sender, so the mail is still BLOCKED. |
| `scope` | `"org" \| "pod" \| "inbox"` | required | Where an entry applies: the whole organization, one pod, or one inbox. |
| `pod_id` | `string` |  | The pod of the list, for pod and inbox scopes. |
| `inbox_id` | `string` |  | The inbox of the list, for the inbox scope. |
| `entry` | `object` |  | shadowed_by_block only: the block entry. Left out for credentials without list_entry_read. |
| `message` | `string` | required | What the warning means, in one sentence. |

#### 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 trusted\_domain\_update, or is not an organization API key or a dashboard session. Only an organization API key or a dashboard session can add or change an entry: pod and inbox keys, OAuth apps and agent access tokens get 403, whatever their permissions, so an agent cannot be talked into trusting a domain. |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                                                                                                                     |
| `422`  | Turning on include\_subdomains: trusted\_domain\_not\_allowed (with error.reason) or confirmation\_required (resend with confirm: true).                                                                                                                                                                                                 |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "string"
}'
```

```json title="Response 200"
{
  "trusted_domain_id": "string",
  "entry_type": "domain",
  "domain": "example.com",
  "domain_unicode": "string",
  "include_subdomains": false,
  "scope": "org",
  "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
  "inbox_id": "agent@machinemail.dev",
  "reason": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_by": {
    "type": "user",
    "id": "string"
  },
  "match_count": 1,
  "last_matched_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-01T12:00:00.000Z",
  "warnings": [
    {
      "code": "shadowed_by_block",
      "scope": "org",
      "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
      "inbox_id": "agent@machinemail.dev",
      "entry": {
        "entry": "customer@example.com",
        "entry_type": "email"
      },
      "message": "string"
    }
  ],
  "released": {
    "count": 1
  }
}
```

## Delete Trusted Domain

```http
DELETE /v0/trusted-domains/{trusted_domain_id}
```

Mail from the domain is judged normally again: every message the entry marked trusted gets back the level and labels it would have without it (a large set is finished in the background after the response). Any credential with `trusted_domain_delete` that can see the entry may remove it.

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

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `trusted_domain_id` | `string` | required | ID of the entry (td_...). |

#### Response

**204** Deleted

#### 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.                                                                                                                                                                   |
| `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/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```
