{"openapi":"3.1.0","info":{"title":"Maildrop API","version":"1.0.0","description":"Inbound email stored in Cloudflare D1, with larger attachments in KV. Authenticate with an API key or the admin token. Browser clients can call the API cross-origin; authentication is bearer-only, with no cookies. All timestamps are ISO 8601 UTC."},"servers":[{"url":"https://maildrop.example.workers.dev","description":"Replace with your deployed Worker URL"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Mailboxes"},{"name":"Messages"},{"name":"Keys"},{"name":"System"}],"paths":{"/api/v1/whoami":{"get":{"tags":["System"],"summary":"Describe the credential used for this request","responses":{"200":{"description":"Credential details"}}}},"/api/v1/stats":{"get":{"tags":["System"],"summary":"Instance storage statistics and configured limits","responses":{"200":{"description":"Storage statistics"}}}},"/api/v1/mailboxes":{"get":{"tags":["Mailboxes"],"summary":"List mailboxes","responses":{"200":{"description":"Mailboxes visible to the credential","content":{"application/json":{"schema":{"type":"object","properties":{"mailboxes":{"type":"array","items":{"$ref":"#/components/schemas/Mailbox"}}}}}}}}},"post":{"tags":["Mailboxes"],"summary":"Create a mailbox","description":"Requires the mailboxes scope. An address without a domain gets DEFAULT_DOMAIN when configured.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMailboxRequest"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxResponse"}}}},"409":{"description":"Address already exists"}}}},"/api/v1/mailboxes/{address}":{"parameters":[{"$ref":"#/components/parameters/Address"}],"get":{"tags":["Mailboxes"],"summary":"Read a mailbox and its usage","responses":{"200":{"description":"Mailbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Mailboxes"],"summary":"Update settings, quotas, or retention","description":"Requires the mailboxes scope. Lowering a quota on a trim_oldest mailbox trims it immediately.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMailboxRequest"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Mailboxes"],"summary":"Delete a mailbox and all its messages","responses":{"200":{"description":"Deleted"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/mailboxes/{address}/messages":{"parameters":[{"$ref":"#/components/parameters/Address"}],"get":{"tags":["Messages"],"summary":"List messages in a mailbox","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Since"},{"$ref":"#/components/parameters/Before"},{"$ref":"#/components/parameters/Search"},{"$ref":"#/components/parameters/Unread"},{"$ref":"#/components/parameters/Starred"},{"$ref":"#/components/parameters/HasAttachments"},{"name":"order","in":"query","schema":{"type":"string","enum":["desc","asc"],"default":"desc"}}],"responses":{"200":{"description":"Page of messages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageList"}}}}}}},"/api/v1/mailboxes/{address}/stats":{"parameters":[{"$ref":"#/components/parameters/Address"}],"get":{"tags":["Mailboxes"],"summary":"Mailbox usage and attachment storage split","responses":{"200":{"description":"Statistics"}}}},"/api/v1/mailboxes/{address}/trim":{"parameters":[{"$ref":"#/components/parameters/Address"}],"post":{"tags":["Mailboxes"],"summary":"Delete oldest messages until within quota","description":"Requires the write scope. Deletes up to 2,000 messages per call; within_quota reports whether another call is needed.","responses":{"200":{"description":"Trimmed","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer"},"within_quota":{"type":"boolean"},"mailbox":{"$ref":"#/components/schemas/Mailbox"}}}}}}}}},"/api/v1/mailboxes/{address}/purge":{"parameters":[{"$ref":"#/components/parameters/Address"}],"post":{"tags":["Mailboxes"],"summary":"Delete messages by age or all at once","description":"Requires the write scope. Deletes up to 5,000 messages per call; has_more and remaining say whether to call again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurgeRequest"}}}},"responses":{"200":{"description":"Purged","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer"},"remaining":{"type":"integer"},"has_more":{"type":"boolean"},"mailbox":{"$ref":"#/components/schemas/Mailbox"}}}}}}}}},"/api/v1/messages":{"get":{"tags":["Messages"],"summary":"List messages across one mailbox","description":"Equivalent to /mailboxes/{address}/messages. The mailbox query parameter is required.","parameters":[{"name":"mailbox","in":"query","required":true,"schema":{"type":"string"},"description":"Mailbox address"},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Search"}],"responses":{"200":{"description":"Page of messages"}}}},"/api/v1/messages/{id}":{"parameters":[{"$ref":"#/components/parameters/MessageId"}],"get":{"tags":["Messages"],"summary":"Read a message with bodies, headers, and attachment metadata","responses":{"200":{"description":"Message","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Messages"],"summary":"Set read or starred flags","description":"Requires the write scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageRequest"}}}},"responses":{"200":{"description":"Updated"}}},"delete":{"tags":["Messages"],"summary":"Delete one message and its attachments","description":"Requires the write scope.","responses":{"200":{"description":"Deleted"}}}},"/api/v1/messages/{id}/raw":{"parameters":[{"$ref":"#/components/parameters/MessageId"}],"get":{"tags":["Messages"],"summary":"Download the original RFC 822 source","description":"Only available when the source fits under RAW_INLINE_MAX.","responses":{"200":{"description":"Raw MIME message","content":{"message/rfc822":{}}},"404":{"description":"Source was not retained"}}}},"/api/v1/attachments/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["Messages"],"summary":"Download attachment bytes","responses":{"200":{"description":"Attachment bytes"},"410":{"description":"Attachment content was not stored or no longer exists"}}}},"/api/v1/keys":{"get":{"tags":["Keys"],"summary":"List API keys","description":"Requires the keys scope.","parameters":[{"name":"include_revoked","in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Keys without token values"}}},"post":{"tags":["Keys"],"summary":"Create an API key","description":"Requires the keys scope. The token is returned once and never again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}}},"responses":{"201":{"description":"Created key and token"}}}},"/api/v1/keys/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"delete":{"tags":["Keys"],"summary":"Revoke an API key","responses":{"200":{"description":"Revoked key"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/audit":{"get":{"tags":["Keys"],"summary":"Recent administrative actions","description":"Requires the keys scope.","responses":{"200":{"description":"Audit entries"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"An API key (mk_...) with the required scopes, or ADMIN_TOKEN for full access. X-API-Key is also accepted."}},"parameters":{"Address":{"name":"address","in":"path","required":true,"schema":{"type":"string"},"description":"Mailbox address, URL-encoded"},"MessageId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Message id, for example msg_01h..."},"Limit":{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},"Cursor":{"name":"cursor","in":"query","schema":{"type":"string"},"description":"next_cursor from the previous page"},"Since":{"name":"since","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only messages received at or after this time"},"Before":{"name":"before","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only messages received before this time"},"Search":{"name":"q","in":"query","schema":{"type":"string","maxLength":200},"description":"Case-insensitive substring match over subject, sender, preview, and text body"},"Unread":{"name":"unread","in":"query","schema":{"type":"boolean"}},"Starred":{"name":"starred","in":"query","schema":{"type":"boolean"}},"HasAttachments":{"name":"has_attachments","in":"query","schema":{"type":"boolean"}}},"responses":{"NotFound":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"]}}},"Mailbox":{"type":"object","properties":{"id":{"type":"string"},"address":{"type":"string","format":"email"},"display_name":{"type":["string","null"]},"enabled":{"type":"boolean"},"quota_bytes":{"type":"integer","description":"0 means unlimited"},"quota_messages":{"type":"integer","description":"0 means unlimited"},"overflow":{"type":"string","enum":["trim_oldest","reject_new"],"description":"trim_oldest deletes oldest mail; reject_new throws when the mailbox is at quota so the sender may retry."},"retention_days":{"type":"integer","description":"0 inherits the Worker default; retention is stored but not scheduled automatically"},"used_bytes":{"type":"integer"},"used_messages":{"type":"integer"},"last_received_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"MailboxResponse":{"type":"object","properties":{"mailbox":{"$ref":"#/components/schemas/Mailbox"}}},"CreateMailboxRequest":{"type":"object","required":["address"],"properties":{"address":{"type":"string"},"display_name":{"type":"string"},"quota_bytes":{"type":"integer","minimum":0},"quota_messages":{"type":"integer","minimum":0},"overflow":{"type":"string","enum":["trim_oldest","reject_new"],"default":"trim_oldest","description":"trim_oldest deletes oldest mail; reject_new throws when the mailbox is at quota so the sender may retry."},"retention_days":{"type":"integer","minimum":0,"maximum":3650}}},"UpdateMailboxRequest":{"type":"object","properties":{"display_name":{"type":["string","null"]},"enabled":{"type":"boolean"},"quota_bytes":{"type":"integer","minimum":0},"quota_messages":{"type":"integer","minimum":0},"overflow":{"type":"string","enum":["trim_oldest","reject_new"],"description":"trim_oldest deletes oldest mail; reject_new throws when the mailbox is at quota so the sender may retry."},"retention_days":{"type":"integer","minimum":0,"maximum":3650}}},"AddressObject":{"type":"object","properties":{"address":{"type":"string"},"name":{"type":"string"}}},"MessageSummary":{"type":"object","properties":{"id":{"type":"string"},"mailbox":{"type":"string","format":"email"},"subject":{"type":"string"},"from":{"$ref":"#/components/schemas/AddressObject"},"envelope_from":{"type":["string","null"]},"to":{"type":"array","items":{"$ref":"#/components/schemas/AddressObject"}},"cc":{"type":"array","items":{"$ref":"#/components/schemas/AddressObject"}},"reply_to":{"type":"array","items":{"$ref":"#/components/schemas/AddressObject"}},"received_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"},"message_id":{"type":["string","null"]},"in_reply_to":{"type":["string","null"]},"references":{"type":["string","null"]},"preview":{"type":"string"},"size_bytes":{"type":"integer"},"stored_bytes":{"type":"integer"},"has_attachments":{"type":"boolean"},"attachment_count":{"type":"integer"},"is_read":{"type":"boolean"},"is_starred":{"type":"boolean"},"raw_available":{"type":"boolean"},"parse_error":{"type":["string","null"]}}},"Message":{"allOf":[{"$ref":"#/components/schemas/MessageSummary"},{"type":"object","properties":{"text":{"type":"string"},"html":{"type":"string","description":"Untrusted HTML. Sanitize before rendering."},"body_truncated":{"type":"boolean"},"headers":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"}},"raw_url":{"type":"string"}}}]},"MessageResponse":{"type":"object","properties":{"message":{"$ref":"#/components/schemas/Message"}}},"MessageList":{"type":"object","properties":{"mailbox":{"$ref":"#/components/schemas/Mailbox"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageSummary"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}},"Attachment":{"type":"object","properties":{"id":{"type":"string"},"message_id":{"type":"string"},"filename":{"type":["string","null"]},"mime_type":{"type":"string"},"size_bytes":{"type":"integer"},"content_id":{"type":["string","null"]},"disposition":{"type":["string","null"]},"is_inline":{"type":"boolean"},"storage":{"type":"string","enum":["d1","kv","unavailable"]},"available":{"type":"boolean"},"url":{"type":"string"},"sha256":{"type":["string","null"]}}},"UpdateMessageRequest":{"type":"object","properties":{"is_read":{"type":"boolean"},"is_starred":{"type":"boolean"}}},"PurgeRequest":{"type":"object","properties":{"all":{"type":"boolean"},"older_than_days":{"type":"integer","minimum":1},"before":{"type":"string","format":"date-time"}},"description":"Provide all, or older_than_days, or before."},"CreateKeyRequest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":100},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","mailboxes","keys"]},"default":["read","write"],"description":"Defaults to read and write so an agent can clean up its own mail; pass [\"read\"] for read-only."},"mailbox_scope":{"type":["array","null"],"items":{"type":"string"},"description":"Optional allowlist of mailbox addresses; null means all mailboxes"}}}}}}