Mint a temporary inbox

Search Documentation

Search across all developer documentation

inboxes

Mint a temporary inbox

POST /v1/inboxes
Auth required api.inboxes.manage

Allocates a short-lived receiving address and returns it immediately. Give the address to whoever is forwarding the mail, then long-poll poll_url. When the inbox expires the address stops resolving and everything stored against it — parsed messages, the raw original, every staged attachment — is deleted.

A body is optional. POST /v1/inboxes with no body at all mints a private, unrestricted inbox that lives for ten minutes, which is the shortest useful call here.

The expiry is final. ttl is 60 to 3600 seconds and cannot be extended afterwards: PATCH does not accept it, and there is no renew. If you need longer, mint another inbox.

Restricted inboxes are not metered. Pass allowed_senders containing only addresses you have verified (see POST /v1/verified-senders) or @yourdomain.com for a domain your org owns, and the inbox becomes restricted: true: mail from anybody else is dropped and counted in dropped_count, never bounced. A restricted inbox cannot receive a stranger's signup confirmation, so it is exempt from the mint quota and the per-credential cap. An entry that is neither verified nor a domain you own is a 422 naming the field, not a silently dropped entry.

Metering, when the inbox is NOT restricted. An org that has connected its own AWS account gets 30 mints an hour and 200 a day; one that has not gets 3 a day. There is also a cap on live inboxes: 5 per credential (unrestricted only) and 25 per org (always). A refusal is a 429 whose reason says which limit, so a client can tell "wait an hour" from "verify a sender" from "an administrator switched this off".

Ownership and visibility. The inbox belongs to the credential that minted it — an API key or an OAuth client, or the person behind a user-delegated token — and is private unless you say otherwise. Another member of the same org reading a private inbox gets 404, not 403: a 403 would confirm the id exists.

Every mint also returns a pull URL. pull_url is an address on the fetch host that reads THIS INBOX'S MAIL WITH NO CREDENTIAL AT ALL — no API key, no token, no session. That is what it is for: mint an inbox for one of your own users and give them the link, and they never need a SendOps account, because you hold the only credential. It is read-only — it reads status and messages and can neither delete the inbox nor change it — but whoever holds it reads the mail, so treat it as the secret it is and put it somewhere only that user can see. Revoking the token is how you take that back: pull_token.id names it, and DELETE /v1/inboxes/{id}/pull-tokens/{token_id} cuts it off without touching the inbox. POST /v1/inboxes/{id}/pull-tokens issues more, up to ten live at a time.

pull_url is returned once, here. Only the token's hash is stored, so GET /v1/inboxes/{id} and the list carry neither pull_url nor pull_token — there is nothing for them to return. Lost it? Issue another and revoke the old one.

account_ref is your key, not ours. Pass whichever id your own product uses for the user this inbox is for. SendOps stores it, counts caps per value, and lets you filter GET /v1/inboxes?account_ref= by it — and interprets it in no other way. Optional here; REQUIRED for an org in Platform mode, whose caps are counted per account_ref rather than per credential.

PII. The address is real and the mail it receives is real. See api.inboxes.view for what the messages route returns.

Request body

Content type: application/json

ttl integer optional

Lifetime in SECONDS. The ceiling is an hour on purpose: the promise is "and then it is gone", and a promise measured in hours invites the address to be used as a mailbox, which is a different product with a different privacy story.

Constraints: 60–3600

Default: 600

label string optional

A human note. Never leaves your org, matched against nothing.

Constraints: length 0–80

visibility string enum optional

One of: private, org

Default: private

allowed_senders array<string> optional

Restrict the inbox to these senders. Each entry is either an address YOU have verified (POST /v1/verified-senders) or @example.com for a domain your org owns. An entry that is neither is a 422 naming it, not a silently dropped entry — an allow-list that lost an entry would be an inbox that silently stopped being restricted.

account_ref string optional

YOUR key for whichever of your users this inbox is for. SendOps stores it, counts caps per value and filters GET /v1/inboxes?account_ref= by it, and interprets it in no other way — it is deliberately not an address or an id SendOps could resolve, so do not put your user's email here. Charset A–Z a–z 0–9 . _ : @ / -, 1 to 128 characters, no whitespace. Stored and compared EXACTLY: case is preserved and nothing is trimmed. Optional for an ordinary org. REQUIRED for one in Platform mode, whose live-inbox cap is counted per account_ref rather than per credential — without it there would be one undifferentiated pool and any one of your users could exhaust the rest.

Constraints: length 0–128

Responses

Errors follow the RFC 7807 problem format — see the error reference.

201 The inbox, including the address to hand out and the URL to poll application/json
id string<uuid> required
address string required

The mailbox to give to whoever is forwarding the mail. Ten opaque characters at the temporary-inbox host. PII in the ordinary sense once it is used: mail sent here is real mail.

expires_at string<date-time> required

When the address stops resolving and everything stored against it is deleted. Not extendable.

poll_url string<uri> required

The absolute URL of this inbox's long-poll route, on the origin you reached us on. Never assemble it yourself.

pull_url string<uri> optional

On the mint response only. A URL on the fetch host that reads this inbox's mail WITH NO CREDENTIAL — give it to the end user this inbox is for. Read-only, and whoever holds it reads the mail. Absent from GET /v1/inboxes/{id} and from the list, because only the token's hash is stored and there is nothing to return. Lost it? POST /v1/inboxes/{id}/pull-tokens issues another.

pull_token object optional

On the mint response only. The metadata of the pull URL above, so you can revoke it later by id without having kept the URL.

restricted boolean required

True when allowed_senders was non-empty and every entry was verified at mint time. A restricted inbox drops mail from anybody else and is exempt from the mint quota and the per-credential cap.

restriction_source string enum | null required

WHO vouched for allowed_senders, and it is the difference between a claim SendOps checked and one it took on trust. verified — the minting user held a confirmed claim on every entry, or the org owns the domain. platform — the org asserted the list under Platform mode and SendOps did not verify it. Null when the inbox is not restricted, because then there is no list to vouch for.

One of: verified, platform, null

account_ref string | null required

The partition key sent at mint, echoed back exactly as sent — SendOps interprets it in no way. Null when the mint did not carry one. Filter the list by it with ?account_ref=.

Constraints: length 0–128

visibility string enum required

private — only the credential that minted it. org — any colleague holding inboxes.use can read the mail. Others get 404, never 403.

One of: private, org

label string required

A human note, 80 characters at most. Matched against nothing.

status string enum required

expired — the cleanup job retired it; the messages route answers 410. deleted — the owner purged it early; the messages route answers 404.

One of: active, expired, deleted

allowed_senders array<string> required

Frozen at mint. Each entry is a full address the minting user had verified, or @example.com for a domain the org owns. Revoking a verified sender later does not change this list. Empty means the inbox accepts mail from anyone, which is what makes it metered.

message_count integer required

Messages delivered to this inbox.

dropped_count integer required

Messages turned away — a sender not on allowed_senders, or a virus verdict. Nothing is bounced, so a sender is never told. A rising dropped_count with a flat message_count is the signature of a forward from the wrong address.

created_at string<date-time> required
401 Missing, malformed, or unknown API key application/problem+json
403 Either the credential lacks the required scope (code: invalid_scope), or it is bound to the test environment and this operation is irreversible (code: test_environment_forbidden). Branch on code: the first is fixed by granting the scope, the second only by using a live credential. See the "Live and test credentials" section of the API description. application/problem+json
422 A query parameter, path value or body field failed validation. The body is a validation_failed Problem. When the refusal is about a reference — a segment key, template slug, topic or attribute name the organization does not have — it additionally carries validation_code, field, line/column, missing, candidates and next_step, so a client can correct the call without a second round of guessing. See ValidationProblem. application/problem+json
429 A mint limit refused the request. Nothing was created. The reason extension says WHICH limit, and the values need different reactions: - quota_hourly — the org's hourly mint allowance. Clears itself; honour Retry-After. - quota_daily — the org's daily allowance. An org that has not connected its own AWS account gets three unrestricted mints a day. Waiting until tomorrow works; VERIFYING A SENDER and passing it in allowed_senders works now, because a restricted inbox is not metered. - cap_user — this credential already holds the maximum live unrestricted inboxes. Delete one, wait, or mint a restricted one. Standard orgs only. - cap_account_ref — the Platform-mode twin of cap_user: THIS account_ref already holds the maximum live unrestricted inboxes, counted per partition key rather than per credential, so every other account_ref in the org is unaffected. Delete one of that user's inboxes, wait for one to expire, or mint a restricted one. Platform- profile orgs only. - cap_org — the org already holds the maximum live inboxes. Applies to restricted inboxes too. - org_disabled — an operator or the abuse breaker switched temporary inboxes off for this org. NO Retry-After IS SENT, deliberately: retrying will not help and a nominal value would be a lie that produced a hot-retry loop. Talk to an administrator. Inboxes already minted keep receiving until they expire. application/problem+json
500 Unexpected server-side failure. The code is internal_error. The request_id field can be quoted to SendOps support to investigate. application/problem+json