{"openapi":"3.1.0","info":{"title":"BotInbox API","version":"0.1.0","description":"Email inboxes for AI agents, and the Email API for apps. Keys are account-, pod-, inbox- or domain-scoped, with full or sending permission. API keys, domains, pods (list/create), and webhooks require an account-scoped full-access key; narrower keys receive 403 insufficient_scope. Sending keys and domain-scoped keys work only with the Email API (and sending keys also with inbox sends). Webhook URLs must be public https."},"servers":[{"url":"https://botinbox.dev"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"bi_"}},"parameters":{"inboxId":{"name":"inboxId","in":"path","required":true,"schema":{"type":"string"}},"messageId":{"name":"messageId","in":"path","required":true,"schema":{"type":"string"}},"threadId":{"name":"threadId","in":"path","required":true,"schema":{"type":"string"}},"domainId":{"name":"domainId","in":"path","required":true,"schema":{"type":"string"}},"podId":{"name":"podId","in":"path","required":true,"schema":{"type":"string"}},"keyId":{"name":"keyId","in":"path","required":true,"schema":{"type":"string"}},"webhookId":{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"}},"emailId":{"name":"emailId","in":"path","required":true,"schema":{"type":"string"}},"idempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":256},"description":"Makes a retry safe for 24 hours: the same key with the same body replays the first response (Idempotent-Replayed: true) and sends nothing; the same key with a different body returns 409 invalid_idempotent_request."}},"schemas":{"Email":{"type":"object","required":["from","to","subject"],"properties":{"from":{"type":"string","description":"Any address on a verified domain of your organization, optionally with a name: Acme <noreply@acme.com>"},"to":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"cc":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"bcc":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"reply_to":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"subject":{"type":"string","maxLength":998},"html":{"type":"string"},"text":{"type":"string","description":"text or html is required"},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"X-* headers, List-Unsubscribe and List-Unsubscribe-Post only"},"attachments":{"type":"array","description":"Up to 25 MB each and 40 MB in total after base64 encoding. Not allowed in batch sends.","items":{"type":"object","required":["filename","content"],"properties":{"filename":{"type":"string"},"content":{"type":"string","format":"byte","description":"Base64-encoded file"},"content_type":{"type":"string"}}}},"tags":{"type":"array","maxItems":10,"items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,256}$"},"value":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,256}$"}}}}},"description":"At most 50 recipients across to, cc and bcc."},"EmailStatus":{"type":"object","properties":{"object":{"type":"string","enum":["email"]},"id":{"type":"string"},"status":{"type":"string","enum":["queued","scheduled","sending","sent","delivery_delayed","delivered","bounced","complained","failed","suppressed","canceled"]},"from":{"type":"string"},"to":{"type":"array","items":{"type":"string"}},"cc":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"reply_to":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"},"tags":{"type":"array","items":{"type":"object"}},"attachments":{"type":"array","items":{"type":"object"}},"batch_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"},"delivered_at":{"type":["string","null"],"format":"date-time"},"failed_at":{"type":["string","null"],"format":"date-time"},"last_event_at":{"type":["string","null"],"format":"date-time"},"open_count":{"type":"integer"},"click_count":{"type":"integer"},"suppressed":{"type":"array","items":{"type":"string"},"description":"Recipients dropped because they are suppressed"},"error_code":{"type":["string","null"]},"error":{"type":["string","null"]}}},"EmailEvent":{"type":"object","properties":{"object":{"type":"string","enum":["email_event"]},"id":{"type":"string"},"type":{"type":"string","enum":["email.sent","email.delivered","email.delivery_delayed","email.bounced","email.complained","email.failed","email.suppressed","email.opened","email.clicked"]},"recipient":{"type":["string","null"]},"bounce":{"type":"object","properties":{"type":{"type":"string","enum":["hard","soft"]},"sub_type":{"type":["string","null"]}}},"link":{"type":"string"},"occurred_at":{"type":"string","format":"date-time"}}},"Suppression":{"type":"object","properties":{"object":{"type":"string","enum":["suppression"]},"id":{"type":"string"},"email":{"type":"string"},"reason":{"type":"string"},"source":{"type":["string","null"],"enum":["bounce","complaint","manual","unsubscribe","import",null]},"domain_id":{"type":["string","null"]},"expires_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"}}},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"active":{"type":"boolean"},"events":{"type":"array","items":{"type":"string"},"description":"Event types delivered; empty = every event"},"domain_id":{"type":["string","null"]},"description":{"type":["string","null"]},"disabled_at":{"type":["string","null"],"format":"date-time","description":"Set when the endpoint was turned off after failing persistently"},"disabled_reason":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}}},"SentimentInput":{"type":"object","required":["sentiment"],"properties":{"sentiment":{"type":"string","enum":["positive","neutral","negative"]},"score":{"type":"number","minimum":-1,"maximum":1,"description":"Must agree with sentiment: positive ≥ 0, negative ≤ 0"},"note":{"type":"string","maxLength":500}}},"MessageSentiment":{"type":"object","properties":{"message_id":{"type":"string"},"thread_id":{"type":"string"},"direction":{"type":"string","enum":["inbound","outbound"]},"sentiment":{"type":["string","null"],"enum":["positive","neutral","negative",null]},"score":{"type":["number","null"]},"note":{"type":["string","null"]},"source":{"type":["string","null"],"enum":["agent","human",null]},"set_by":{"type":["string","null"],"description":"API key id (agent) or user id (person)"},"set_at":{"type":["string","null"],"format":"date-time"}}},"OutcomeInput":{"type":"object","required":["outcome"],"properties":{"outcome":{"type":"string","enum":["open","resolved","unresolved","escalated"]},"success":{"type":"boolean","description":"Default: true for resolved, false for unresolved and escalated. Not allowed with open."},"reason":{"type":"string","maxLength":500},"confidence":{"type":"number","minimum":0,"maximum":1}}},"HandoffInput":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":1,"maxLength":500},"priority":{"type":"string","enum":["low","normal","high","urgent"],"default":"normal"},"category":{"type":"string","enum":["customer_request","frustrated","billing","security","legal","uncertain","other"],"default":"other"}}},"ThreadInsights":{"type":"object","properties":{"thread_id":{"type":"string"},"inbox_id":{"type":"string"},"outcome":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved","unresolved","escalated"]},"success":{"type":["boolean","null"]},"reason":{"type":["string","null"]},"confidence":{"type":["number","null"]},"source":{"type":["string","null"],"enum":["agent","human",null]},"set_by":{"type":["string","null"]},"set_at":{"type":["string","null"],"format":"date-time"}}},"handoff":{"type":"object","properties":{"status":{"type":"string","enum":["none","requested","claimed","resolved"]},"reason":{"type":["string","null"]},"category":{"type":["string","null"]},"priority":{"type":["string","null"],"enum":["low","normal","high","urgent",null]},"requested_at":{"type":["string","null"],"format":"date-time"},"requested_by":{"type":["string","null"],"description":"API key id"},"claimed_by":{"type":["string","null"],"description":"User id"},"claimed_at":{"type":["string","null"],"format":"date-time"},"resolved_at":{"type":["string","null"],"format":"date-time"},"agent_replies_held":{"type":"boolean","description":"Agent sends on this thread return 409 thread_in_handoff"}}},"already_open":{"type":"boolean","description":"POST handoff only: a handoff was already open (nothing changed, nobody notified)"}}},"ConversationInsights":{"type":"object","properties":{"object":{"type":"string","enum":["conversation_insights"]},"inbox_count":{"type":"integer"},"range":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"days":{"type":"integer"}}},"sentiment":{"type":"object","description":"days[]: { day, inbound: {positive, neutral, negative}, outbound: {...} }; totals.inbound / totals.outbound add total and average_score"},"outcomes":{"type":"object","properties":{"open":{"type":"integer"},"resolved":{"type":"integer"},"unresolved":{"type":"integer"},"escalated":{"type":"integer"},"closed":{"type":"integer"},"successful":{"type":"integer"},"success_ratio":{"type":["number","null"],"description":"successful ÷ closed; null when nothing closed"}}},"handoffs":{"type":"object","properties":{"requested":{"type":"integer"},"open":{"type":"integer"},"resolved":{"type":"integer"},"conversations":{"type":"integer","description":"Threads with activity in the range"},"rate":{"type":["number","null"],"description":"requested ÷ conversations"},"median_seconds_to_claim":{"type":["number","null"]},"median_seconds_to_resolve":{"type":["number","null"]},"by_priority":{"type":"object"}}}}}}},"paths":{"/health":{"get":{"security":[],"summary":"Liveness","responses":{"200":{"description":"OK"}}}},"/v0/me":{"get":{"summary":"Account usage and caps","responses":{"200":{"description":"Usage"}}}},"/v0/inboxes":{"get":{"summary":"List inboxes","responses":{"200":{"description":"Inboxes"}}},"post":{"summary":"Create an inbox","responses":{"201":{"description":"Created"}}}},"/v0/inboxes/{inboxId}/messages":{"get":{"summary":"List messages","parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"unread","in":"query","schema":{"type":"boolean"}},{"name":"limit","in":"query","schema":{"type":"integer"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"label","in":"query","schema":{"type":"string"},"description":"Label name or ID"},{"name":"from","in":"query","schema":{"type":"string"},"description":"Sender address contains (case-insensitive)"},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"},"description":"Received at or after"}],"responses":{"200":{"description":"Messages"}}},"post":{"summary":"Send a message from the inbox (optional bcc, reply_to, headers, attachments, tags; optional Idempotency-Key)","parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"responses":{"201":{"description":"Sent"}}}},"/v0/inboxes/{inboxId}":{"parameters":[{"$ref":"#/components/parameters/inboxId"}],"get":{"summary":"Get an inbox","responses":{"200":{"description":"Inbox"}}}},"/v0/inboxes/{inboxId}/messages/{messageId}":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/messageId"}],"get":{"summary":"Get a message","responses":{"200":{"description":"Message"}}},"patch":{"summary":"Update read state or labels","responses":{"200":{"description":"Updated"}}}},"/v0/inboxes/{inboxId}/drafts":{"parameters":[{"$ref":"#/components/parameters/inboxId"}],"get":{"summary":"List drafts","responses":{"200":{"description":"Drafts"}}},"post":{"summary":"Create a draft","responses":{"201":{"description":"Draft created"}}}},"/v0/inboxes/{inboxId}/drafts/{draftId}":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"Get a draft","responses":{"200":{"description":"Draft"}}},"patch":{"summary":"Update a draft","responses":{"200":{"description":"Updated draft"}}},"delete":{"summary":"Delete a draft","responses":{"204":{"description":"Deleted"}}}},"/v0/inboxes/{inboxId}/drafts/{draftId}/send":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"name":"draftId","in":"path","required":true,"schema":{"type":"string"}}],"post":{"summary":"Send a draft","responses":{"201":{"description":"Message sent"}}}},"/v0/inboxes/{inboxId}/labels":{"parameters":[{"$ref":"#/components/parameters/inboxId"}],"get":{"summary":"List labels","responses":{"200":{"description":"Labels"}}},"post":{"summary":"Create a label","responses":{"201":{"description":"Label created"}}}},"/v0/inboxes/{inboxId}/labels/{labelId}":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"name":"labelId","in":"path","required":true,"schema":{"type":"string"}}],"patch":{"summary":"Update a label","responses":{"200":{"description":"Updated label"}}},"delete":{"summary":"Delete a label","responses":{"204":{"description":"Deleted"}}}},"/v0/inboxes/{inboxId}/rules":{"parameters":[{"$ref":"#/components/parameters/inboxId"}],"get":{"summary":"List sender rules and policy","responses":{"200":{"description":"Rules"}}},"post":{"summary":"Create a sender rule or update policy","responses":{"201":{"description":"Created"}}}},"/v0/inboxes/{inboxId}/rules/{ruleId}":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"name":"ruleId","in":"path","required":true,"schema":{"type":"string"}}],"delete":{"summary":"Delete a sender rule","responses":{"204":{"description":"Deleted"}}}},"/v0/inboxes/{inboxId}/messages/{messageId}/attachments/{attachmentId}":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/messageId"},{"name":"attachmentId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"Download an attachment","responses":{"200":{"description":"The file, or its metadata with download: null when the original file is unavailable"}}}},"/v0/inboxes/{inboxId}/threads":{"parameters":[{"$ref":"#/components/parameters/inboxId"}],"get":{"summary":"List threads","responses":{"200":{"description":"Threads"}}}},"/v0/inboxes/{inboxId}/threads/{threadId}":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/threadId"}],"get":{"summary":"Get a thread","responses":{"200":{"description":"Thread"}}},"patch":{"summary":"Mark a thread read or unread","responses":{"200":{"description":"Updated thread"}}}},"/v0/inboxes/{inboxId}/messages/{messageId}/sentiment":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/messageId"}],"put":{"summary":"Set a message's sentiment (conversation insights; full-access key)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SentimentInput"}}}},"responses":{"200":{"description":"Sentiment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageSentiment"}}}},"400":{"description":"validation_error"},"403":{"description":"insufficient_scope (sending-only or domain key)"},"404":{"description":"Message not found in the key's scope"}}}},"/v0/inboxes/{inboxId}/threads/{threadId}/outcome":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/threadId"}],"put":{"summary":"Set a conversation's outcome (emits thread.outcome_set)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutcomeInput"}}}},"responses":{"200":{"description":"Thread insights","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadInsights"}}}},"400":{"description":"validation_error"},"403":{"description":"insufficient_scope"},"404":{"description":"Thread not found"}}}},"/v0/inboxes/{inboxId}/threads/{threadId}/insights":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/threadId"}],"get":{"summary":"A thread's outcome, handoff, per-message sentiment and change history","responses":{"200":{"description":"ThreadInsights plus subject, messages[] (MessageSentiment) and history[]"},"404":{"description":"Thread not found"}}}},"/v0/inboxes/{inboxId}/threads/{threadId}/handoff":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/threadId"}],"post":{"summary":"Request a human handoff (notifies Owners and Admins, emits thread.handoff_requested; optional Idempotency-Key)","description":"While the handoff is open and the organization holds agent replies (the default), agent sends replying on this thread (POST messages with reply_to_message_id, draft sends, MCP and CLI) return 409 thread_in_handoff. Dashboard replies are not held. Repeating the request while a handoff is open returns 200 with already_open: true.","parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoffInput"}}}},"responses":{"200":{"description":"A handoff was already open (already_open: true)"},"201":{"description":"Handoff requested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadInsights"}}}},"400":{"description":"validation_error"},"403":{"description":"insufficient_scope"},"404":{"description":"Thread not found"}}},"delete":{"summary":"Cancel your handoff request before a person claims it (ends the reply hold)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Thread insights"},"409":{"description":"handoff_claimed or no_open_handoff"}}}},"/v0/inboxes/{inboxId}/threads/{threadId}/handoff/claim":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/threadId"}],"post":{"summary":"Claim an open handoff (account-scoped full-access key; usually done in the dashboard)","responses":{"200":{"description":"Thread insights"},"403":{"description":"insufficient_scope (inbox and pod keys can't claim)"},"409":{"description":"handoff_claimed or no_open_handoff"}}}},"/v0/inboxes/{inboxId}/threads/{threadId}/handoff/resolve":{"parameters":[{"$ref":"#/components/parameters/inboxId"},{"$ref":"#/components/parameters/threadId"}],"post":{"summary":"Resolve a handoff, optionally with the outcome (account-scoped key; ends the reply hold; emits thread.handoff_resolved)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"$ref":"#/components/schemas/OutcomeInput"},"note":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Thread insights"},"403":{"description":"insufficient_scope"},"409":{"description":"no_open_handoff"}}}},"/v0/insights":{"get":{"summary":"Conversation insights aggregates over the inboxes the key reaches","parameters":[{"name":"from","in":"query","schema":{"type":"string"},"description":"YYYY-MM-DD or ISO 8601 (default: 13 days before to)"},{"name":"to","in":"query","schema":{"type":"string"},"description":"YYYY-MM-DD (whole day) or ISO 8601 (default: now). At most 92 days after from"},{"name":"inbox_id","in":"query","schema":{"type":"string"}},{"name":"pod_id","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Aggregates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationInsights"}}}},"400":{"description":"validation_error (bad range)"},"404":{"description":"Inbox or pod not found"}}}},"/v0/emails":{"post":{"summary":"Send an email from any address on a verified domain (account- or domain-scoped key, full or sending permission)","parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Email"}}}},"responses":{"200":{"description":"Idempotent replay of an earlier request (Idempotent-Replayed: true)"},"201":{"description":"Queued for delivery","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"em_01j9z3k8q4t7w2x5v6b8n0m1c3"},"status":{"type":"string","enum":["queued","suppressed"]},"suppressed":{"type":"array","items":{"type":"string"},"description":"Recipients dropped because they are on the suppression list (email.suppressed is sent)"}}}}}},"400":{"description":"validation_error or invalid_idempotency_key"},"403":{"description":"insufficient_scope, domain_paused, sandbox_restricted, account_suspended, account_limited, trust_limit, recipient_limit"},"409":{"description":"invalid_idempotent_request or concurrent_idempotent_requests"},"422":{"description":"domain_not_verified"},"429":{"description":"rate_limited, trust_limit or plan_limit (honour Retry-After when present)"},"503":{"description":"sending_paused_globally"}}},"get":{"summary":"List emails, newest first (sending keys: only emails they sent; domain keys: their domain)","parameters":[{"name":"status","in":"query","schema":{"type":"string"},"description":"One status or a comma-separated list"},{"name":"domain_id","in":"query","schema":{"type":"string"}},{"name":"tag","in":"query","schema":{"type":"string"},"description":"name:value"},{"name":"to","in":"query","schema":{"type":"string"},"description":"Recipient address"},{"name":"api_key_id","in":"query","schema":{"type":"string"}},{"name":"created_after","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"created_before","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"next_cursor from the previous page"}],"responses":{"200":{"description":"A page of emails (no bodies)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailStatus"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"400":{"description":"validation_error"}}}},"/v0/emails/batch":{"post":{"summary":"Send up to 100 emails in one call, validated all or nothing (no attachments)","parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/Email"}}}}},"responses":{"200":{"description":"All emails queued; data[] is in request order","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"}}}}}}}}},"400":{"description":"validation_error with the index of the first invalid email"},"403":{"description":"As POST /v0/emails, with index when one email is refused"},"409":{"description":"invalid_idempotent_request or concurrent_idempotent_requests"},"422":{"description":"domain_not_verified, with index"},"429":{"description":"rate_limited, trust_limit or plan_limit"}}}},"/v0/emails/{emailId}":{"parameters":[{"$ref":"#/components/parameters/emailId"}],"get":{"summary":"Get an email's status (sending keys: only emails they sent)","responses":{"200":{"description":"Email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailStatus"}}}},"404":{"description":"not_found"}}}},"/v0/emails/{emailId}/events":{"parameters":[{"$ref":"#/components/parameters/emailId"}],"get":{"summary":"An email's delivery timeline, oldest first (sending keys: only emails they sent)","responses":{"200":{"description":"Events","content":{"application/json":{"schema":{"type":"object","properties":{"email_id":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/EmailEvent"}}}}}}},"404":{"description":"not_found"}}}},"/v0/suppressions":{"get":{"summary":"List the organization's suppressions (account-scoped full-access key; Owner or Admin)","parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"source","in":"query","schema":{"type":"string","enum":["bounce","complaint","manual","unsubscribe","import"]}},{"name":"domain_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Suppressions","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Suppression"}},"next_cursor":{"type":["string","null"]}}}}}},"403":{"description":"insufficient_scope (sending, domain, pod and inbox keys) or role_forbidden"}}},"post":{"summary":"Add suppressions (JSON: email or emails[], reason, domain_id) or import a CSV (text/csv, up to 100,000 addresses)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"emails":{"type":"array","items":{"type":"string"},"maxItems":1000},"reason":{"type":"string"},"domain_id":{"type":"string"}}}},"text/csv":{"schema":{"type":"string"}}}},"responses":{"201":{"description":"Added (total, added, existing; invalid[] for imports)"},"400":{"description":"validation_error"},"403":{"description":"insufficient_scope or role_forbidden"}}}},"/v0/suppressions/{suppressionId}":{"parameters":[{"name":"suppressionId","in":"path","required":true,"schema":{"type":"string"},"description":"Suppression id or email address"}],"delete":{"summary":"Remove a suppression","responses":{"204":{"description":"Removed"},"404":{"description":"not_found"}}}},"/v0/domains":{"get":{"summary":"List domains (account key)","responses":{"200":{"description":"Domains"}}},"post":{"summary":"Add a domain (account key; paid plans). Returns the DNS records to publish.","description":"Body: name, optional mode (send_only default | inbound_subdomain | full_mx) and inbound_label (default \"in\"). send_only asks for no MX record; inbound_subdomain also adds <inbound_label>.<name> with receiving on (returned as inbound_domain); full_mx receives at the name and replaces its MX records.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"mode":{"type":"string","enum":["send_only","inbound_subdomain","full_mx"]},"inbound_label":{"type":"string"}}}}}},"responses":{"201":{"description":"Created"},"403":{"description":"plan_required"},"429":{"description":"domain_limit_reached"}}}},"/v0/domains/{domainId}":{"parameters":[{"$ref":"#/components/parameters/domainId"}],"get":{"summary":"Get a domain","responses":{"200":{"description":"Domain"}}},"patch":{"summary":"Change the domain's mode: send_only (stops receiving; refused while inboxes use it) or full_mx (receive at the name)","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["mode"],"properties":{"mode":{"type":"string","enum":["send_only","full_mx"]}}}}}},"responses":{"200":{"description":"Updated domain with new records"},"409":{"description":"domain_in_use or mode_unsupported"}}},"delete":{"summary":"Delete a domain (stops sending and receiving for it)","parameters":[{"name":"force","in":"query","schema":{"type":"boolean"},"description":"Detach inboxes that still use the domain"}],"responses":{"200":{"description":"Deleted"},"409":{"description":"domain_in_use"},"502":{"description":"provider_error"}}}},"/v0/domains/{domainId}/verify":{"parameters":[{"$ref":"#/components/parameters/domainId"}],"post":{"summary":"Verify the domain's DNS records (response includes per-record checks)","responses":{"200":{"description":"Verified"},"409":{"description":"Not verified"}}}},"/v0/providers/smtp":{"get":{"summary":"Get the bring-your-own SMTP config (account key; no secrets)","responses":{"200":{"description":"Config or null"}}},"put":{"summary":"Save a BYO SMTP server (Team+)","responses":{"200":{"description":"Saved"},"400":{"description":"Invalid config or host"},"403":{"description":"plan_required or insufficient_scope"}}},"delete":{"summary":"Delete the BYO SMTP config","responses":{"204":{"description":"Deleted"}}}},"/v0/providers/smtp/test":{"post":{"summary":"Test the saved SMTP server (connect + auth, no mail sent)","responses":{"200":{"description":"OK"},"422":{"description":"Connection failed"}}}},"/v0/pods":{"get":{"summary":"List pods (account key)","responses":{"200":{"description":"Pods"}}},"post":{"summary":"Create a pod (account key)","responses":{"201":{"description":"Created"}}}},"/v0/pods/{podId}":{"parameters":[{"$ref":"#/components/parameters/podId"}],"get":{"summary":"Get a pod","responses":{"200":{"description":"Pod"}}}},"/v0/api-keys":{"get":{"summary":"List API keys (account key)","responses":{"200":{"description":"API keys (never the secret)"}}},"post":{"summary":"Create an API key (account key). Optional inbox_id, pod_id or domain_id scope, and permission: full (default) or sending.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"inbox_id":{"type":"string"},"pod_id":{"type":"string"},"domain_id":{"type":"string","description":"Email API: the key may only send from this domain"},"permission":{"type":"string","enum":["full","sending"],"default":"full"}}}}}},"responses":{"201":{"description":"Created; api_key is shown once"}}}},"/v0/api-keys/{keyId}":{"parameters":[{"$ref":"#/components/parameters/keyId"}],"delete":{"summary":"Revoke an API key","responses":{"204":{"description":"Revoked"}}}},"/v0/webhooks":{"get":{"summary":"List webhooks (account key)","responses":{"200":{"description":"Webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}},"403":{"description":"Insufficient scope"}}},"post":{"summary":"Create a webhook (account key; public https URL only)","description":"Event types: message.received, email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained, email.failed, email.suppressed, email.opened, email.clicked, thread.handoff_requested, thread.handoff_resolved, thread.outcome_set. Deliveries are signed: x-botinbox-signature-v2 = sha256=HMAC-SHA256(secret, \"<x-botinbox-timestamp>.<raw body>\"); the body-only x-botinbox-signature is kept for existing receivers. Retries: immediately, 5s, 5m, 30m, 2h, 5h, 10h, 10h.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"domain_id":{"type":"string"},"description":{"type":"string"}}}}}},"responses":{"201":{"description":"Created; the secret is returned once"},"400":{"description":"Invalid or non-public URL, or unknown event type"},"403":{"description":"Insufficient scope"}}}},"/v0/webhooks/{webhookId}":{"parameters":[{"$ref":"#/components/parameters/webhookId"}],"get":{"summary":"Get a webhook","responses":{"200":{"description":"Webhook","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}}}},"patch":{"summary":"Update url, events, domain_id or description, or turn the webhook on or off (active)","responses":{"200":{"description":"Updated webhook"},"400":{"description":"validation_error"},"404":{"description":"Not found"}}},"delete":{"summary":"Delete a webhook","responses":{"204":{"description":"Deleted"}}}},"/v0/webhooks/{webhookId}/deliveries":{"parameters":[{"$ref":"#/components/parameters/webhookId"}],"get":{"summary":"Delivery log, newest first (status: pending | delivered | failed)","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["pending","delivered","failed"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Deliveries"}}}},"/v0/webhooks/{webhookId}/replay":{"parameters":[{"$ref":"#/components/parameters/webhookId"}],"post":{"summary":"Send failed deliveries again (delivery_ids, or since; default: the last 7 days)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"delivery_ids":{"type":"array","items":{"type":"string"}},"since":{"type":"string","format":"date-time"}}}}}},"responses":{"202":{"description":"{ replayed }"},"409":{"description":"webhook_disabled: turn the webhook back on first"}}}},"/v0/events":{"get":{"summary":"Stream SSE mail events","parameters":[{"name":"token","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"text/event-stream"}}}},"/v0/openapi.json":{"get":{"security":[],"summary":"This document","responses":{"200":{"description":"OpenAPI 3.1 document"}}}},"/mcp":{"post":{"summary":"Remote MCP server (Streamable HTTP, JSON-RPC 2.0)","description":"Model Context Protocol endpoint for AI clients. Send one JSON-RPC message per POST with Authorization: Bearer bi_...; responses are application/json (notifications return 202). Supports MCP 2026-07-28 and initialize-based 2024-11-05..2025-11-25 clients. Tools mirror this API and respect key scope. GET and DELETE return 405.","responses":{"200":{"description":"JSON-RPC response"},"202":{"description":"Notification accepted"},"401":{"description":"Missing or invalid API key"}}}}},"x-error-codes":{"account_suspended":"403 — the account is suspended; every API key is refused","account_limited":"403 — sending is paused for the account","trust_limit":"403/429 — new-account warm-up limit (limit, max, trust_level in the body)","recipient_limit":"403 — more than 50 recipients (to + cc + bcc) in one message","plan_limit":"429 — the plan's daily or monthly send limit (limit: sends_per_day | sends_per_month)","insufficient_scope":"403 — the key's scope or permission doesn't allow this endpoint (e.g. a sending key listing inboxes, or a domain key sending from another domain)","domain_not_verified":"422 — the From domain isn't a verified sending domain in the organization","domain_paused":"403 — sending from the domain is paused until its DKIM record is restored","sandbox_restricted":"403 — the sandbox sender can only email verified members of the organization","validation_error":"400 — the request body is invalid (issues[] lists each problem; index for batch sends)","invalid_idempotency_key":"400 — the Idempotency-Key header must be 1–256 characters","invalid_idempotent_request":"409 — the Idempotency-Key was already used with a different request","concurrent_idempotent_requests":"409 — a request with this Idempotency-Key is still in progress","sending_paused_globally":"503 — sending is temporarily paused; retry later","recipients_suppressed":"422 — inbox sends only: recipients on the suppression list (suppressed[] in the body). Email API sends drop suppressed recipients instead","webhook_disabled":"409 — the webhook is turned off; PATCH active: true before replaying","role_forbidden":"403 — the key creator's role doesn't allow this (e.g. suppressions need Owner or Admin)","rate_limited":"429 — request or send rate limit; honour Retry-After","provider_error":"502 — delivery failed temporarily; retry later","thread_in_handoff":"409 — the conversation was handed off to a person and agent replies are on hold until the handoff is resolved or cancelled","handoff_claimed":"409 — a person already claimed the handoff (it can't be cancelled or claimed by someone else)","no_open_handoff":"409 — the conversation has no open handoff"}}