Mailboxes API reference

A mailbox is the addressable unit an agent sends from and receives into. Each one carries its own address plus a stack of per-mailbox policies: what the scanner enforces, how PII is delivered, whether attachments are exposed, and how sends and inbound senders are contained. This page is the per-operation reference for the /v1/mailboxes resource.

The machine-readable spec for every field and shape below is served at /docs/openapi.json.

How to read this page

Each operation lists its method + path, auth (as a contract, not a deployment fact), the request/response shape, the error codes worth knowing, and a classification tag:

TagMeaning
read-onlyReturns state; changes nothing.
mutatingChanges stored mailbox state.
secret-revealingReturns a value that is shown once and cannot be re-fetched.

None of the mailbox operations send email or route a message into a review queue directly, so send-triggering and human-review-possible do not appear here — but several policy writes below configure how future sends are gated. Those forward links point at the owning contract pages.

Auth vocabulary. "Bearer" is an API key (admin or mailbox-scoped agent role); "session" is a dashboard cookie. Where an operation says "admin + session", an agent-role key is rejected with 403 INSUFFICIENT_SCOPE. Full remediation for every code is in the error catalog.

Path params accept name or UUID. Every :id below accepts either the mailbox's UUID or its name. Unknown names return 404 NOT_FOUND in the same shape as unknown UUIDs. A UUID-shaped token is always read as an ID, never a name.

POST /v1/mailboxes — create a mailbox

Classification: mutating · Auth: admin key or session (agent keys → 403 INSUFFICIENT_SCOPE).

Creates a new mailbox and returns its sending address.

{ "name": "support-bot" }

On a self-hosted (bring-your-own) sending domain, also send self_hosted_imap_folder; the API probes the folder synchronously before insert, and it must be unique per domain.

Response (201):

{
  "id": "uuid",
  "name": "support-bot",
  "address": "[email protected]"
}

Errors: 400 INVALID_NAME (name matches the UUID shape), 409 (name already in use), and — self-hosted only — 400 IMAP_FOLDER_REQUIRED / IMAP_FOLDER_NOT_FOUND / SELF_HOSTED_PROBE_FAILED, 409 FOLDER_ALREADY_CLAIMED. See the error catalog.

GET /v1/mailboxes — list mailboxes

Classification: read-only · Auth: any account-scoped key or session. An agent key sees only its bound mailboxes.

Returns { "mailboxes": [ ... ] }. Each entry carries the mailbox identity plus the policy projection: status, scanner_policy, pii_mode, the effective attachment_exposure_mode / attachment_allowed_file_families, recipient_policy_mode, sender_policy_mode, hitl_mode, allow_thread_replies, the derived agent_send_policy / restricted_by pair, an outbound_attachments enablement block, and the instruction_trust_mode / instruction_trust_strict_recipient flags. The list and single-GET projections are byte-for-byte in parity, so you can read every mailbox's policy state in one call.

Session-only fields. The attachment / outbound-attachment consent bookkeepingcurrent_disclaimer_version, the *_accepted_at / *_accepted_version acceptance stamps, attachment_reauth_at, attachment_policy_version, image_raw_download_confirmed, current_image_risk_version, legacy_wildcard_active, the legacy attachment_access_enabled flag, and the matching sub-fields of outbound_attachments — is returned only to a dashboard session. It drives the re-acceptance banner, and the routes that record acceptance require a human session plus re-authentication, so an API-key (Bearer) caller cannot act on it. These fields are therefore omitted from every API-key response, and the outbound_attachments block is reduced to just its enabled flag. An agent reads the effective attachment_exposure_mode instead.

GET /v1/mailboxes/:id — mailbox detail

Classification: read-only · Auth: admin key, session, or a mailbox-bound agent key. An agent key that is not bound to this mailbox sees 404 NOT_FOUND (the resource is resolved then masked, so existence is not leaked).

Returns the full mailbox object — the same projection as a list entry. Example (API-key shape; a dashboard session additionally receives the session-only consent fields described above):

{
  "id": "uuid",
  "name": "support-bot",
  "address": "[email protected]",
  "status": "active",
  "scanner_policy": null,
  "pii_mode": "passthrough",
  "attachment_exposure_mode": "metadata_only",
  "attachment_allowed_file_families": [],
  "outbound_attachments": { "enabled": false },
  "default_subaddress_mode": "reply_to",
  "recipient_policy_mode": "blocklist",
  "sender_policy_mode": "blocklist",
  "instruction_trust_mode": "disabled",
  "instruction_trust_strict_recipient": true,
  "created_at": "2026-04-02T12:00:00Z"
}

attachment_exposure_mode and attachment_allowed_file_families are the effective values from a read-time projection, not raw column values.

PATCH /v1/mailboxes/:id — update mailbox policy

Classification: mutating · Auth: admin key or session only. An agent key → 403 INSUFFICIENT_SCOPE, so only a human session or an admin key can loosen a policy.

Partial-update the mailbox's scanner policy, PII delivery mode, review-queue mode, per-detector PII config, thread-reply behavior, and agent-send containment. Every body field is optional — send only what you want to change; omitted fields are preserved.

{
  "scanner_policy": {
    "language_mode": "allow_all_languages",
    "disabled_scanners": ["pii", "secrets"],
    "disabled_proxy_criteria": ["profanity"],
    "outbound_pii_policy": { "phone_number": "allow_with_warning" }
  },
  "pii_mode": "redacted",
  "hitl_mode": "all_outbound",
  "allow_thread_replies": true,
  "agent_send_policy": "restricted"
}

scanner_policy

Accepts three distinct shapes:

  • An object — shallow-merged at the top level: present keys overwrite, absent keys are preserved. Clear an array key by sending it as [] (e.g. disabled_scanners: [] re-enables all scanners). A per-key null inside the object is rejected 400.
  • {} — a no-op merge; the stored policy is preserved.
  • null — reset the whole policy to platform defaults. A later GET returns scanner_policy: null. This is distinct from {} (preserve) and from omitting the field entirely (column no-op).

Sub-fields:

  • language_modeenglish_only (default), allow_all_languages, or disabled. english_only is a best-effort whole-document heuristic that quarantines mail detected as non-English; short or mixed-language messages can be misclassified either way, so it is not a containment guarantee — the regex and LLM scanners are the defense against malicious content in any language.
  • disabled_scanners — local scanners you can turn off: prompt-injection, attachment-policy, mime-mismatch, pii, secrets, url-reputation.
  • disabled_proxy_criteria — LLM criteria you can turn off: prompt_injection, jailbreak, function_call_risk, profanity, confidentiality_leak, unauthorized_liability.
  • outbound_pii_policy — per-type outbound PII send-safety map for ssn, credit_card, phone_number, with actions allow, allow_with_warning, review, quarantine, block. Omitted types use platform defaults (ssn / credit_cardquarantine, phone_numberallow_with_warning). Stricter actions are accepted on every tier; relaxing an action below default, newly disabling the pii scanner, or newly setting review requires paid entitlement (see limits). An action of review routes matching sends into the review queue — see the message lifecycle.
  • outbound_review_policy.approval_noteoptional (default) or required_for_sensitive_pii (require an approver note before releasing SSN / credit-card review holds).

Mandatory outbound criteria. Including any of toxicity, hate_speech, violence, harassment, self_harm, or sexual_content in disabled_proxy_criteria returns 422. These stay enforced on the shared sending domain and are not in the inbound profile. The platform trust check recipient-check is likewise not part of scanner policy.

pii_mode

passthrough (default) or redacted. Controls how PII detected in inbound bodies is delivered to the agent:

  • passthrough — reads return body as a plaintext display projection, capped at the shared body delivery limit.
  • redacted — reads return body.content with each detected PII span replaced by a <TYPE> tag (e.g. <EMAIL_ADDRESS>, <PERSON>, <PHONE_NUMBER>). Detected types are the scrub-worthy subset: PERSON, EMAIL_ADDRESS, PHONE_NUMBER, US_SSN, CREDIT_CARD, US_BANK_NUMBER, US_DRIVER_LICENSE, US_ITIN, US_PASSPORT, UK_NHS, IBAN_CODE, MEDICAL_LICENSE, CRYPTO. URLs, dates, and locations are not redacted. redacted requires paid entitlement — a sandbox account gets 403 TIER_LIMIT. If a message's PII spans failed to persist at scan time, redacted fails closed with body.content = null rather than leaking.

Advanced per-detector config (pii_redaction_config) lets a paid account choose which detectors redact and how a span renders (replace_with_type, partial_mask, hash_replace). It is a paid feature that only takes effect while pii_mode is redacted; null means "platform default — every detector redacts with a <TYPE> placeholder". On a downgrade to a non-feature tier the saved config is retained but ignored on reads (privacy-safe — a downgraded account never regains raw PII delivery). Entitlement details are in limits.

hitl_mode

disabled (default) or all_outbound. When all_outbound, every clean outbound send routes to a review state instead of going on the wire, and blocks until released. Setting all_outbound requires paid entitlement (403 TIER_LIMIT otherwise); setting disabled is always allowed as a downgrade escape hatch. See the message lifecycle for the resulting states and the send-gate contract for where this sits in the decision tree.

allow_thread_replies

Boolean. In an allowlist-mode mailbox this permits an outbound send to a non-allowlisted recipient only when that recipient is a visible inbound participant of the same thread (computed at send time; no standing allowlist row is written). Inert in blocklist mode. Fail-closed: an unset value disables the bypass. This is a send-gate control — the full decision tree lives at send gates.

agent_send_policy

restricted or open — the single front-door control for agent-send containment. When containment is enforced for your mailbox, sends that originate from an agent-role key are held to the recipient allowlist even while the mailbox is otherwise in blocklist mode, so an exfiltration-style send to a brand-new recipient is contained; human/admin/session sends keep the mailbox's native mode. It is a derived control and is mutually exclusive in one request with the raw recipient_policy_mode / agent_send_containment fields (400 otherwise). Setting open on an allowlist mailbox flips it to blocklist and clears agent containment; it does not open human sends (they were never allowlist-restricted), so it requires no consent. confirm_open_human_sends is deprecated and ignored (kept for back-compat). Responses carry the derived pair agent_send_policy + restricted_by (mailbox_allowlist | agent_containment | null). The interaction with the recipient allowlist is documented at send gates.

Response (200): the full mailbox object with the updated policy. Errors: 403 TIER_LIMIT (a requested value needs paid entitlement), 422 (invalid or mandatory criteria, or an unknown/mis-targeted PII detector), 400 (schema-shape rejections). Full remediation: error catalog.

DELETE /v1/mailboxes/:id — deactivate a mailbox

Classification: mutating · Auth: admin key or session (agent keys → 403 INSUFFICIENT_SCOPE).

Soft-deletes the mailbox: it stops accepting new mail and its bindings are removed from any keys. Returns { "status": "deleted" }. Deleted mailboxes drop out of every binding read path, so they never linger in a key's mailbox_ids.

POST /v1/mailboxes/:id/attachment-access — inbound attachment exposure

Classification: mutating · Auth: admin key or session (agent keys → 403 INSUFFICIENT_SCOPE). Enabling or widening approved raw downloads is session + fresh re-auth only — a Bearer key hits 403 REAUTH_REQUIRES_SESSION before any state change.

Sets the mailbox's three-mode exposure model with a versioned disclaimer acceptance:

modeWhat an agent gets
metadata_only (default)Filename, size, MIME, AV verdict. No content, no bytes.
derived_contentMetadata plus a 20,000-character inline text preview for text/plain, text/csv, PDF, and Office Open XML documents, via the message attachment preview endpoint. Visible text only. Paid entitlement (Starter+).
raw_download_selected_typesMetadata, previews, plus raw byte download for selected families (pdf, text, csv, optional image). Session-cookie auth + TOTP/password re-auth + Pro+ entitlement; raw image needs a separate image-risk acknowledgement.
{
  "mode": "derived_content",
  "allowed_file_families": ["pdf", "text", "csv"],
  "accept_disclaimer_version": "v2026-07"
}

allowed_file_families (closed enum pdf | text | csv | image) is required for raw_download_selected_types and ignored for the other modes; a '*' wildcard is rejected 400 INVALID_FILE_FAMILY_WILDCARD. Adding image requires the current image-risk acknowledgement.

On derived_content, extracted preview text is scanned before it can be served; a preview whose extracted-text scan is rejected returns 403 ATTACHMENT_PREVIEW_BLOCKED. On raw_download_selected_types, an agent downloads raw bytes for an approved family from GET /v1/messages/:id/attachments/:idx — those bytes are AV-clean and policy-clean but are not scanned for hidden instructions, so the agent must treat them as untrusted.

Re-auth applies only when enabling or widening approved downloads. Narrowing to a subset of already-approved families, or downgrading to metadata_only / derived_content, does not require re-auth and is a valid admin-key path.

Errors: 400 family/disclaimer/re-auth validation (e.g. INVALID_FILE_FAMILY, EMPTY_ALLOWED_FILE_FAMILIES, DISCLAIMER_VERSION_MISMATCH, REAUTH_INVALID_TOTP), 403 INSUFFICIENT_SCOPE / REAUTH_REQUIRES_SESSION / TIER_LIMIT / ATTACHMENT_ACCESS_INELIGIBLE, 404 NOT_FOUND, 429 RATE_LIMITED (re-auth brute-force lockout). The legacy { "enable": true } shape is deprecated (400 LEGACY_ENABLE_DEPRECATED); { "enable": false } remains as a kill-switch to metadata_only. Full remediation: error catalog.

POST /v1/mailboxes/:id/outbound-attachments — enable outbound attachments

Classification: mutating · Auth: session + fresh re-auth only to enable — a Bearer key gets 403 REAUTH_REQUIRES_SESSION. Disabling is admin-only. Pro+ entitlement.

Turns on the mailbox's ability to send attachments, with a versioned disclaimer acceptance. A human account owner enables it once (with TOTP/password re-auth); after that, API keys can stage and send attachment handles normally.

{
  "enabled": true,
  "accept_disclaimer_version": "v2026-07",
  "accept_image_risk_version": "v2026-05",
  "reauth_token": "<session re-auth token>"
}

The response echoes outbound_attachments_enabled and the accepted-version timestamps (image fields null when the image-risk version was not supplied).

Errors: 403 REAUTH_REQUIRES_SESSION (Bearer used) / OUTBOUND_ATTACHMENTS_INELIGIBLE (below Pro+), 400 re-auth/disclaimer validation, 404 NOT_FOUND. The staging-and-send lifecycle for the handles themselves — upload, scan, consume-once — is the attachments contract.

PATCH /v1/mailboxes/:id/sender-policy — flip the inbound firewall mode

Classification: mutating · Auth: admin or mailbox-bound agent key (the customer is being protected from senders, so both roles can tighten).

Flips the mailbox's inbound firewall between blocklist and allowlist.

{ "mode": "allowlist", "force_empty": false }

force_empty is required when flipping to allowlist while the inbound allowlist is empty; without it the flip returns 409 SENDER_POLICY_FLIP_EMPTY_ALLOWLIST. Passing true acknowledges that every incoming sender will be firewall_blocked until entries are added.

Response (200):

{
  "mailbox_id": "...",
  "sender_policy_mode": "allowlist",
  "previous_mode": "blocklist",
  "changed_at": "2026-04-25T20:35:06Z"
}

A mailbox.sender_policy_changed webhook fires only on real flips (no-op flips are suppressed) — see the webhook catalog.

Instruction-trust operations

Trusted instruction sources are an opt-in, default-off read-path relaxation: once an operator configures them, a configured agent key reading a verified inbound message from a trusted sender receives narrower guidance in agent_safety_context. The per-field trust taxonomy — what the relaxed agent_safety_context and its instruction_trust basis contain — is owned by the security model.

Auth here is asymmetric by design. Every operation that loosens trust (granting a source, enabling the mailbox mode, turning the strict-recipient guard off) requires a dashboard session with fresh re-auth and rejects Bearer keys with 403 REAUTH_REQUIRES_SESSION. An agent cannot enable, grant, or loosen instruction trust for itself. Only tightening (revoke, disable, turn the guard on) is unprivileged and agent-callable, so an agent can respond fast to an incident.

GET /v1/mailboxes/:id/trusted-sources — list trusted sources

Classification: read-only · Auth: session or any mailbox-scoped agent key.

Returns { "mailbox_id": ..., "sources": [ ... ] }. Each source carries grain (always "address" in v1), value, value_ascii (punycode/ASCII rendering — a transparency surface for homoglyph domains, not the match key), expires_at, revoked_at, a derived status (active | revoked | expired), and an eligibility_snapshot.

POST /v1/mailboxes/:id/trusted-sources — grant a trusted source (loosening)

Classification: mutating · Auth: session + fresh re-auth only (Bearer → 403 REAUTH_REQUIRES_SESSION).

Grants one verified sender address as a trusted instruction source. v1 is address-grain only — a domain grain is rejected 400 INSTRUCTION_TRUST_DOMAIN_GRAIN_UNSUPPORTED. Provide exactly one of value (the typed address) or message_id (a verified inbound message the server derives the address from). Both modes require prior evidence of receipt: a verified inbound message from that exact address must already exist, or the grant fails 422 TRUSTED_SOURCE_NO_VERIFIED_HISTORY / TRUSTED_SOURCE_ANCHOR_NOT_ELIGIBLE.

{
  "value": "[email protected]",
  "reauth_token": "...",
  "residual_ack_version": "v2026-06-a",
  "address_attestation": true,
  "expires_in_days": 90
}

expires_in_days defaults to 90 and must be an integer 1–365 (no infinite grants). The gate order is session re-auth → an accountability floor (an active billing entitlement or an exemption; failing it returns 403 INSTRUCTION_TRUST_ACCOUNTABILITY_REQUIRED, a non-monetary check with no upgrade link) → the current residual-risk acknowledgement (400 INSTRUCTION_TRUST_RESIDUAL_ACK_REQUIRED) → address_attestation: true (400 INSTRUCTION_TRUST_ADDRESS_ATTESTATION_REQUIRED) → the evidence-of-receipt check.

Response (201): the created source object. Granting an address already actively trusted returns 409 TRUSTED_SOURCE_ALREADY_EXISTS; granting a previously revoked or expired entry transparently revives the same row. Fires the trusted_source.granted webhook.

DELETE /v1/mailboxes/:id/trusted-sources/:sourceId — revoke (tightening)

Classification: mutating · Auth: unprivileged — any mailbox-authorized caller, including an agent key.

Soft-revokes a trusted source (stamps revoked_at; the row and its ledger lineage are retained). Idempotent — revoking an already-revoked source returns the same shape with already_revoked: true and does not re-fire the webhook. Returns 404 TRUSTED_SOURCE_NOT_FOUND if the source does not exist. Fires the trusted_source.revoked webhook.

POST /v1/mailboxes/:id/instruction-trust — mailbox master switch

Classification: mutating · Auth: enabling is loosening — session + fresh re-auth only, plus the accountability floor, the current residual-risk acknowledgement, and an explicit strict_recipient choice. Disabling is unprivileged and agent-callable.

Enable:

{ "enabled": true, "reauth_token": "...", "residual_ack_version": "v2026-06-a", "strict_recipient": true }

Disable: { "enabled": false } (does not touch the stored strict_recipient value).

Errors: 400 VALIDATION_ERROR, 404 NOT_FOUND, the re-auth ladder, 403 INSTRUCTION_TRUST_ACCOUNTABILITY_REQUIRED, 400 INSTRUCTION_TRUST_RESIDUAL_ACK_REQUIRED, 400 INSTRUCTION_TRUST_STRICT_CHOICE_REQUIRED. See the error catalog.

POST /v1/mailboxes/:id/instruction-trust/strict-recipient — send-path narrowing

Classification: mutating · Auth: turning it on (true) is tightening — unprivileged and agent-callable. Turning it off (false) re-widens the send surface and requires session + fresh re-auth.

When the mailbox's trust mode is enabled, strict_recipient: true suppresses the thread-scoped reply bypass for agent-origin sends — an agent send is then admitted only via an explicit allowlist entry, never inferred from thread history. It defaults true (contained at rest) and is inert unless the trust mode is enabled. The interaction with the allowlist bypass is documented at send gates.

{ "strict_recipient": false, "reauth_token": "..." }

(reauth_token is only required to turn it off.)


Per-key instruction-trust capability is a key operation, not a mailbox one — enabling it likewise requires a session with fresh re-auth and only applies to agent-role keys; see the security model for how the mailbox mode, the key capability, and a grant combine to relax a read.