Limits
Inbox, address, TTL, message, attachment, batch, sandbox and retention limits.
These are the limits the API enforces today. Values marked “deployment default” are configuration on the hosted service at api.machinemail.dev and may change.
Inboxes and addresses
| Limit | Value |
|---|---|
| Number of inboxes | No limit on your own domains. Inbox count is not a quota. |
Inboxes on the shared domain (machinemail.dev) | 1000 per organization (deployment default). Past that, creating one returns 403 Inbox limit reached for <domain> (1000 per organization). Add your own domain for more. |
| Username | 1 to 64 characters: a-z, 0-9, ., _, +, -; must start and end with a letter or digit. Input is trimmed and lowercased. |
| Username when omitted | Random, 12 characters. |
| Address uniqueness | Global. An address taken by any organization returns 409 Inbox with email <email> already exists. |
| Domain on create | Defaults to the shared domain. Any other domain must belong to your organization (404 Domain not found otherwise). |
| Metadata | String values. No size or key-count limit is enforced. |
Addresses on the shared domain are first come, first served across all organizations. Mail to an unknown address on the shared domain is never turned into a new inbox.
Ephemeral inboxes
| Setting | Value |
|---|---|
ttl_seconds | Any positive number of seconds. There is no maximum. |
ephemeral: true without ttl_seconds | TTL of 3600 seconds (one hour). |
| Expiry | expires_at = created_at + ttl_seconds. Every inbound message moves it to now + ttl_seconds (never earlier). |
| Changing it | PATCH with a new ttl_seconds restarts the clock from now. ttl_seconds: null or ephemeral: false makes the inbox permanent. |
| Deletion | An expired inbox is deleted with its messages, threads, drafts and files within about a minute (the reaper runs every 60 seconds), and inbox.expired is emitted. |
Catch-all domains
A custom domain with catch_all: true creates an inbox the first time mail arrives for an unknown address, when all of these hold:
- the domain is
VERIFIED(deployment default) andinbound_enabledis true; - the local part is a valid username.
The new inbox has created_by: "catch_all". If the domain sets catch_all_ttl_seconds (any positive number), the inbox is ephemeral with that TTL; otherwise it is permanent. inbox.created is emitted.
Messages and attachments
| Limit | Value |
|---|---|
| Recipients per message | At least one of to, cc, bcc, and at most 50 (deployment default; MachineMail can change it, and the error states the cap in force) across all three, each address counted once (case-insensitive). An unclaimed agent sandbox inbox has a lower cap. More returns 400 Too many recipients: <n> across to, cc and bcc. A message can have at most 50 recipients; send separate messages instead. (param: to). Applies to sends, replies, reply-all, forwards, drafts (checked on create, update and send) and scheduled drafts (over the cap at send time: send_status: failed). An entry holding an address list (a@x.com, B <b@y.com>) counts, and is checked against suppressions and send lists, as each address in it. Each address must look like name@host. |
Attachment by url | Fetched by MachineMail: public http(s) only, no redirects, at most 25 MB, 20 second timeout. |
Attachment by base64 content | No separate limit. |
Raw message injection (POST /inbound/raw) | 40 MB. |
Preview (preview field) | First 200 characters. |
Attachment download_url | Expires one hour after the request (unless the storage backend signs its own expiry). |
| Suppressed recipients | Sending to an address in suppressions returns 403. Permanent bounces and complaints add addresses automatically. |
| Paused inbox | Cannot send (403) or receive (mail is not stored; message.rejected is emitted). |
Batches, lists and search
| Limit | Value |
|---|---|
Page size (limit) | Default 50, maximum 500. See Pagination. |
batch-get, batch-update | 100 message IDs per request; extra IDs are dropped. |
| Thread detail | Up to 500 messages per thread in GET /v0/threads/{thread_id}. Thread label updates touch up to 500 messages. |
| Thread search | Scans the newest 500 matching messages (truncated: true when more). |
Inbox search (GET /v0/inboxes/search) | Scans the newest 500 inboxes. |
| Allow and block lists | Up to 500 entries per direction are applied to mail (the first 500 in entry order). |
| Metrics | period 1 to 86400 seconds; default range the last 30 days; start must be before end; at most 100,000 events counted per query. |
Export (GET /v0/inboxes/{inbox_id}/export) | No limit: streams every message as mbox or jsonl. |
Webhooks and events
| Limit | Value |
|---|---|
| Webhooks per organization | No limit. Events are matched against the first 500 webhooks of an organization. |
| Webhook URL | Public http(s); private and loopback addresses are refused. |
| Delivery timeout | 10 seconds. |
| Retries | 1 m, 5 m, 30 m, 2 h, 12 h, then daily, for up to 5 days. See Webhook events. |
| Stored response body | First 1024 bytes. |
SSE resume (Last-Event-ID) | Up to 500 missed events. |
| SSE keepalive | : ping after 15 seconds without events. |
| WebSocket idle timeout | 120 seconds without traffic. Send {"type":"ping"} to keep it open. |
MCP wait_for_message | Up to 60 seconds per call (default 30). |
Domains
| Limit | Value |
|---|---|
| Domains per organization | No limit. |
| Domain name | A valid hostname, 3 to 253 characters. |
| Uniqueness | A domain can belong to one organization only (409 Domain <domain> is already registered). The shared domain cannot be added (409 <domain> is a shared domain). |
| DNS checks | PENDING and INVALID domains are re-checked in the background at most every 5 minutes, for 7 days after the domain was added. After that, call POST /v0/domains/{domain_id}/verify. Domain Connect discovery is cached for 6 hours (10 minutes after a provider error). |
Pods
- The default pod cannot be deleted.
- A pod with inboxes cannot be deleted (
409 Pod still has inboxes; delete them first).
Agent sandbox
An agent that registers itself anonymously through auth.md gets, until a person claims it:
| Limit | Value (deployment defaults) |
|---|---|
| Inboxes | One sandbox inbox on the shared domain, agent-<12 characters>@machinemail.dev |
| Sandbox inbox lifetime | 24 hours |
| Messages | 10 per rolling 24 hours |
| Different recipients | 10 per rolling 24 hours (writing again to an address already counted is free) |
| Recipients per message | 5 (to, cc and bcc together; never more than the cap for every message) |
| Scopes | inboxes.read, messages.read, messages.send, messages.write on that inbox only |
| Claim window | 24 hours |
| Access token lifetime | 1 hour |
| Registrations | 3 per rolling hour and 10 per rolling 24 hours per IPv4 address or IPv6 /64, 100 per hour across the service |
The message, recipient and registration numbers are platform limits: MachineMail can change them at any time without notice, for example to stop abuse. The current values are in auth.md and in every error a cap returns. Every send counts when it is made: deleting sent messages or threads does not give any back. Claiming the agent lifts the sandbox caps. See Rate limits.
Credentials
| Item | Lifetime |
|---|---|
| API key | Until revoked, or until its expires_at |
| Dashboard session | 30 days |
| Sign-in link | 15 minutes, single use |
First API key display (GET /auth/first-key) | 10 minutes after first sign-in, once |
Retention
Messages, raw MIME, attachments, events and webhook deliveries are kept until you delete them (or the inbox expires). There is no automatic retention window today.