Maildrop — agent guide ====================== Maildrop receives inbound email for registered addresses and serves it as JSON. There is no IMAP, SMTP, or outbound sending. Base URL: your deployed Worker origin (for example https://maildrop.example.workers.dev). OpenAPI: GET /openapi.json. Dashboard: GET /admin. Authentication -------------- Send either header on every API request: Authorization: Bearer mk_... X-API-Key: mk_... ADMIN_TOKEN also works as a full-access bearer token. API keys are created in the dashboard or with POST /api/v1/keys and are shown only once. Browser clients can call /api/v1 from any origin; CORS is enabled, but auth is bearer-only and no cookies are used. Scopes (additive): read list mailboxes and messages, read bodies, download attachments/raw write change flags, delete messages, trim, purge mailboxes create, update, and delete mailboxes keys list, create, and revoke API keys, read the audit log A key can also be restricted to specific mailbox addresses. A restricted key gets 403 mailbox_forbidden outside its list. Conventions ----------- - Timestamps are ISO 8601 UTC strings. - Sizes are bytes. - Lists are newest first by default. Paginate with limit (1..200, default 50) and the opaque next_cursor. Stop when has_more is false. - Errors are {"error":{"code","message","details"?}} with 4xx/5xx status. - Addresses are lowercased. Creating a mailbox does not create the Cloudflare Email Routing rule; mail that reaches the Worker without a mailbox is rejected. - Inbound messages larger than MAX_MESSAGE_BYTES are rejected. Mailbox quotas either trim the oldest mail or throw when full, per mailbox. - Message text/html are capped at MAX_BODY_BYTES each; body_truncated says so. Raw RFC 822 is retained only below RAW_INLINE_MAX; check raw_available first. - Attachments are stored in D1 up to INLINE_ATTACHMENT_MAX bytes, otherwise KV. KV write failures fail delivery; some senders retry the resulting 5xx, but retries are not guaranteed. Storage is "unavailable" only when an attachment cannot fit in KV. - HTML bodies are untrusted. Sanitize before rendering. - Search (?q=) is a case-insensitive substring match over subject, sender, preview, and text body. There is no full-text index. Typical flow ------------ 1. Register an address (needs the mailboxes scope or ADMIN_TOKEN): curl -sS -X POST "$MAILDROP_URL/api/v1/mailboxes" -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"address":"notes@mail.example.com","quota_bytes":52428800,"overflow":"trim_oldest"}' 2. Poll for mail (needs read): curl -sS "$MAILDROP_URL/api/v1/mailboxes/notes%40mail.example.com/messages?limit=20&unread=true" -H "Authorization: Bearer $API_KEY" Add &since=2026-10-27T12:00:00Z to fetch only newer mail. The response has messages[] summaries plus has_more and next_cursor. 3. Read one message (bodies, headers, attachment metadata): curl -sS "$MAILDROP_URL/api/v1/messages/$MESSAGE_ID" -H "Authorization: Bearer $API_KEY" 4. Download an attachment or the raw source: curl -sS -L "$MAILDROP_URL/api/v1/attachments/$ATTACHMENT_ID" -H "Authorization: Bearer $API_KEY" -o file.bin curl -sS "$MAILDROP_URL/api/v1/messages/$MESSAGE_ID/raw" -H "Authorization: Bearer $API_KEY" -o message.eml 5. Mark handled mail as read (needs write): curl -sS -X PATCH "$MAILDROP_URL/api/v1/messages/$MESSAGE_ID" -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" -d '{"is_read":true}' 6. Delete mail you have processed (needs write): curl -sS -X DELETE "$MAILDROP_URL/api/v1/messages/$MESSAGE_ID" -H "Authorization: Bearer $API_KEY" Storage control --------------- GET /api/v1/stats instance totals + limits GET /api/v1/mailboxes/{address}/stats per-mailbox usage POST /api/v1/mailboxes/{address}/trim enforce quota now POST /api/v1/mailboxes/{address}/purge {"all":true} or {"older_than_days":30} PATCH /api/v1/mailboxes/{address} quotas, overflow, retention Quota fields are quota_bytes and quota_messages; 0 means unlimited. overflow is "trim_oldest" (default) or "reject_new", which throws when the mailbox is at quota so the sender may retry. retention_days is stored for future use, but no automatic cleanup is scheduled; use purge for immediate deletion. Trim deletes up to 2,000 messages per call and returns within_quota. Purge deletes up to 5,000 per call and returns remaining and has_more; call it again while has_more is true. Issuing a scoped key -------------------- curl -sS -X POST "$MAILDROP_URL/api/v1/keys" -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"name":"inbox agent","scopes":["read","write"],"mailbox_scope":["notes@mail.example.com"]}' Read the token from the response once and store it outside this transcript. New keys default to read and write; pass "scopes":["read"] for read-only. Endpoint summary ---------------- GET /api/v1/whoami GET /api/v1/stats GET /api/v1/mailboxes POST /api/v1/mailboxes GET /api/v1/mailboxes/{address} PATCH /api/v1/mailboxes/{address} DELETE /api/v1/mailboxes/{address} GET /api/v1/mailboxes/{address}/messages GET /api/v1/mailboxes/{address}/stats POST /api/v1/mailboxes/{address}/trim POST /api/v1/mailboxes/{address}/purge GET /api/v1/messages?mailbox={address} GET /api/v1/messages/{id} PATCH /api/v1/messages/{id} DELETE /api/v1/messages/{id} GET /api/v1/messages/{id}/raw GET /api/v1/attachments/{id} GET /api/v1/keys POST /api/v1/keys DELETE /api/v1/keys/{id} GET /api/v1/audit