Skip to contentSkip to content
machinemail

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.

MethodPathSummary
GET/v0/inboxes/{inbox_id}/trusted-domainsList Trusted Domains in Inbox
POST/v0/inboxes/{inbox_id}/trusted-domainsCreate Trusted Domain in Inbox
POST/v0/inboxes/{inbox_id}/trusted-domains/previewPreview Trusted Domain in Inbox
GET/v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}Get Trusted Domain in Inbox
PATCH/v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}Update Trusted Domain in Inbox
DELETE/v0/inboxes/{inbox_id}/trusted-domains/{trusted_domain_id}Delete Trusted Domain in Inbox
GET/v0/pods/{pod_id}/trusted-domainsList Trusted Domains in Pod
POST/v0/pods/{pod_id}/trusted-domainsCreate Trusted Domain in Pod
POST/v0/pods/{pod_id}/trusted-domains/previewPreview Trusted Domain in Pod
GET/v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}Get Trusted Domain in Pod
PATCH/v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}Update Trusted Domain in Pod
DELETE/v0/pods/{pod_id}/trusted-domains/{trusted_domain_id}Delete Trusted Domain in Pod
GET/v0/trusted-domainsList Trusted Domains
POST/v0/trusted-domainsCreate Trusted Domain
POST/v0/trusted-domains/previewPreview Trusted Domain
GET/v0/trusted-domains/{trusted_domain_id}Get Trusted Domain
PATCH/v0/trusted-domains/{trusted_domain_id}Update Trusted Domain
DELETE/v0/trusted-domains/{trusted_domain_id}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

inbox_idstringRequired
ID of the inbox.

Query parameters

limitinteger
Maximum number of items to return. Default 50, maximum 500; larger values are capped.
page_tokenstring
next_page_token from the previous page.
ascendingboolean
Oldest first.

Response

200 A page of trusted domains (application/json)

countintegerRequired
Number of items returned.
limitinteger
Maximum number of items per page. Default 50, maximum 500; larger values are capped.
next_page_tokenstring
Page token for pagination. Absent on the last page.
trusted_domainsTrustedDomain[]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:

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

Request
curl "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
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

inbox_idstringRequired
ID of the inbox.

Request body

Content type: application/json.

domainstringRequired
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_subdomainsboolean
Also trust mail signed by subdomains. Default false. Not allowed for an address.
reasonstring
Why you trust it, for people reading the list. Never included in a message’s trust report. (max length 500)
expires_atstring (date-time)
When the entry stops applying. Must be in the future. Omit for never.
confirmboolean
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_idstring
Idempotency key: a create with a client_id already used in the organization returns that entry (200).

Response

200 The existing entry for this domain or client_id (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

201 Created (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
402The 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.
403The 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.
404Not found. Also returned for resources outside the credential scope.
409client_id is already used at another level.
422trusted_domain_not_allowed (with error.reason) or confirmation_required (resend with confirm: true).
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"
    }
  ]
}

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

inbox_idstringRequired
ID of the inbox.

Request body

Content type: application/json.

domainstringRequired
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_subdomainsboolean
As for create.

Response

200 The preview (application/json)

domainstring | nullRequired
The entry as it would be stored (ASCII form). null when it is not a domain or address.
domain_unicodestring | nullRequired
The same in Unicode form.
entry_typeobjectRequired
allowedbooleanRequired
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) | nullRequired
Why it cannot be trusted. null when it can.
messagestring | nullRequired
The refusal or confirmation, in one sentence. null when there is neither.
confirmation_requiredbooleanRequired
A platform where anyone can write the content, or a relay address: a create needs confirm: true.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes, as a create would report them.
would_releaseobject | nullRequired
Not computed yet: always null.
suggestionsstring[] | nullRequired
Narrower entries to consider (an exact address). Not computed yet: always null.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"]
}

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

inbox_idstringRequired
ID of the inbox.
trusted_domain_idstringRequired
ID of the entry (td_...).

Response

200 The entry (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.

Errors

StatusMeaning
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

Request
curl "https://api.machinemail.dev/v0/inboxes/agent@machinemail.dev/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
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

inbox_idstringRequired
ID of the inbox.
trusted_domain_idstringRequired
ID of the entry (td_...).

Request body

Content type: application/json.

include_subdomainsboolean
Also trust mail signed by subdomains. Not allowed for an address.
confirmboolean
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.
reasonstring | null
New reason; null clears it. (max length 500)
expires_atstring (date-time) | null
New expiry, in the future; null removes it.

Response

200 The updated entry (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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.
404Not found. Also returned for resources outside the credential scope.
422Turning on include_subdomains: trusted_domain_not_allowed (with error.reason) or confirmation_required (resend with confirm: true).
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"
    }
  ]
}

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

inbox_idstringRequired
ID of the inbox.
trusted_domain_idstringRequired
ID of the entry (td_...).

Response

204 Deleted

Errors

StatusMeaning
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

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

pod_idstringRequired
ID of the pod.

Query parameters

limitinteger
Maximum number of items to return. Default 50, maximum 500; larger values are capped.
page_tokenstring
next_page_token from the previous page.
ascendingboolean
Oldest first.

Response

200 A page of trusted domains (application/json)

countintegerRequired
Number of items returned.
limitinteger
Maximum number of items per page. Default 50, maximum 500; larger values are capped.
next_page_tokenstring
Page token for pagination. Absent on the last page.
trusted_domainsTrustedDomain[]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:

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

Request
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
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

pod_idstringRequired
ID of the pod.

Request body

Content type: application/json.

domainstringRequired
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_subdomainsboolean
Also trust mail signed by subdomains. Default false. Not allowed for an address.
reasonstring
Why you trust it, for people reading the list. Never included in a message’s trust report. (max length 500)
expires_atstring (date-time)
When the entry stops applying. Must be in the future. Omit for never.
confirmboolean
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_idstring
Idempotency key: a create with a client_id already used in the organization returns that entry (200).

Response

200 The existing entry for this domain or client_id (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

201 Created (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
402The 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.
403The 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.
404Not found. Also returned for resources outside the credential scope.
409client_id is already used at another level.
422trusted_domain_not_allowed (with error.reason) or confirmation_required (resend with confirm: true).
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"
    }
  ]
}

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

pod_idstringRequired
ID of the pod.

Request body

Content type: application/json.

domainstringRequired
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_subdomainsboolean
As for create.

Response

200 The preview (application/json)

domainstring | nullRequired
The entry as it would be stored (ASCII form). null when it is not a domain or address.
domain_unicodestring | nullRequired
The same in Unicode form.
entry_typeobjectRequired
allowedbooleanRequired
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) | nullRequired
Why it cannot be trusted. null when it can.
messagestring | nullRequired
The refusal or confirmation, in one sentence. null when there is neither.
confirmation_requiredbooleanRequired
A platform where anyone can write the content, or a relay address: a create needs confirm: true.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes, as a create would report them.
would_releaseobject | nullRequired
Not computed yet: always null.
suggestionsstring[] | nullRequired
Narrower entries to consider (an exact address). Not computed yet: always null.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"]
}

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

pod_idstringRequired
ID of the pod.
trusted_domain_idstringRequired
ID of the entry (td_...).

Response

200 The entry (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.

Errors

StatusMeaning
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

Request
curl "https://api.machinemail.dev/v0/pods/pod_01k6x2m9qd0001b8j4mr0w3d5f/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
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

pod_idstringRequired
ID of the pod.
trusted_domain_idstringRequired
ID of the entry (td_...).

Request body

Content type: application/json.

include_subdomainsboolean
Also trust mail signed by subdomains. Not allowed for an address.
confirmboolean
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.
reasonstring | null
New reason; null clears it. (max length 500)
expires_atstring (date-time) | null
New expiry, in the future; null removes it.

Response

200 The updated entry (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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.
404Not found. Also returned for resources outside the credential scope.
422Turning on include_subdomains: trusted_domain_not_allowed (with error.reason) or confirmation_required (resend with confirm: true).
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"
    }
  ]
}

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

pod_idstringRequired
ID of the pod.
trusted_domain_idstringRequired
ID of the entry (td_...).

Response

204 Deleted

Errors

StatusMeaning
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

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

limitinteger
Maximum number of items to return. Default 50, maximum 500; larger values are capped.
page_tokenstring
next_page_token from the previous page.
ascendingboolean
Oldest first.

Response

200 A page of trusted domains (application/json)

countintegerRequired
Number of items returned.
limitinteger
Maximum number of items per page. Default 50, maximum 500; larger values are capped.
next_page_tokenstring
Page token for pagination. Absent on the last page.
trusted_domainsTrustedDomain[]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:

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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).
500Unexpected server error.

Error bodies use the error envelope.

Example

Request
curl "https://api.machinemail.dev/v0/trusted-domains" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
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.

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.

domainstringRequired
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_subdomainsboolean
Also trust mail signed by subdomains. Default false. Not allowed for an address.
reasonstring
Why you trust it, for people reading the list. Never included in a message’s trust report. (max length 500)
expires_atstring (date-time)
When the entry stops applying. Must be in the future. Omit for never.
confirmboolean
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_idstring
Idempotency key: a create with a client_id already used in the organization returns that entry (200).

Response

200 The existing entry for this domain or client_id (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

201 Created (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
402The 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.
403The 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.
409client_id is already used at another level.
422trusted_domain_not_allowed (with error.reason) or confirmation_required (resend with confirm: true).
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"
    }
  ]
}

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, and its warnings. For validating as someone types. would_release and suggestions are not computed yet and are always null.

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

Request body

Content type: application/json.

domainstringRequired
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_subdomainsboolean
As for create.

Response

200 The preview (application/json)

domainstring | nullRequired
The entry as it would be stored (ASCII form). null when it is not a domain or address.
domain_unicodestring | nullRequired
The same in Unicode form.
entry_typeobjectRequired
allowedbooleanRequired
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) | nullRequired
Why it cannot be trusted. null when it can.
messagestring | nullRequired
The refusal or confirmation, in one sentence. null when there is neither.
confirmation_requiredbooleanRequired
A platform where anyone can write the content, or a relay address: a create needs confirm: true.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes, as a create would report them.
would_releaseobject | nullRequired
Not computed yet: always null.
suggestionsstring[] | nullRequired
Narrower entries to consider (an exact address). Not computed yet: always null.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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).
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"]
}

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

trusted_domain_idstringRequired
ID of the entry (td_...).

Response

200 The entry (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.

Errors

StatusMeaning
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

Request
curl "https://api.machinemail.dev/v0/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
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 on include_subdomains 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

trusted_domain_idstringRequired
ID of the entry (td_...).

Request body

Content type: application/json.

include_subdomainsboolean
Also trust mail signed by subdomains. Not allowed for an address.
confirmboolean
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.
reasonstring | null
New reason; null clears it. (max length 500)
expires_atstring (date-time) | null
New expiry, in the future; null removes it.

Response

200 The updated entry (application/json)

trusted_domain_idstringRequired
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.
domainstringRequired
The domain, or for entry_type: "email" the address, lowercased, with the domain in its ASCII (punycode) form.
domain_unicodestringRequired
The same, with the domain in its Unicode form. Equal to domain for ASCII names.
include_subdomainsbooleanRequired
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_idstring | nullRequired
The pod, for pod- and inbox-scoped entries.
inbox_idstring | nullRequired
The inbox, for inbox-scoped entries.
reasonstring | nullRequired
Why it was added, for people reading the list. Never included in a message’s trust report.
expires_atstring (date-time) | nullRequired
When the entry stops applying. null: never.
created_byobject | nullRequired
Who added the entry.
match_countintegerRequired
Messages this entry has marked trusted.
last_matched_atstring (date-time) | nullRequired
When this entry last marked a message trusted. null: never.
created_atstring (date-time)Required
When the entry was added.
updated_atstring (date-time)Required
When the entry last changed.
warningsWarning[]Required
Conflicts with block entries or allow-only scopes. Empty when there are none.

Each item of warnings is a Warning:

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_idstring
The pod of the list, for pod and inbox scopes.
inbox_idstring
The inbox of the list, for the inbox scope.
entryobject
shadowed_by_block only: the block entry. Left out for credentials without list_entry_read.
messagestringRequired
What the warning means, in one sentence.

Errors

StatusMeaning
400Invalid request: error.type is validation and error.param names the field.
401Missing, invalid, expired or revoked credential.
403The 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.
404Not found. Also returned for resources outside the credential scope.
422Turning on include_subdomains: trusted_domain_not_allowed (with error.reason) or confirmation_required (resend with confirm: true).
500Unexpected server error.

Error bodies use the error envelope.

Example

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"
}'
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"
    }
  ]
}

Delete Trusted Domain

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

Mail from the domain is judged normally again from now on. 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

trusted_domain_idstringRequired
ID of the entry (td_...).

Response

204 Deleted

Errors

StatusMeaning
401Missing, invalid, expired or revoked credential.
403The 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).
404Not found. Also returned for resources outside the credential scope.
500Unexpected server error.

Error bodies use the error envelope.

Example

Request
curl -X DELETE "https://api.machinemail.dev/v0/trusted-domains/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"