For AI agents
Use this page as a skill. The source of truth is /openapi.json. Full prose is in /llms-full.txt. Do not invent endpoints or scopes.
Last updated
Facts
- Self-hosted. Base URL is
https://<cloudfront>/api/v1. - Auth:
x-api-key: sb...(orAuthorization: Bearer sb...) - Errors:
{ "error": string }. 401 is alwaysInvalid API key. tois always a JSON array. A string is a 400.- Do not call
POST /apior/agent/*. - Do not mint keys. Do not invite users. Do not call Updates.
- If you get
Key lacks <scope> scope, stop and ask the human to add that scope. - Scopes follow the dashboard's roles. Drafting is not sending:
campaigns:writeedits campaigns,campaigns:sendstarts them.audience:writeadds and edits people,audience:deleteremoves them.automations:writebuilds,automations:manageturns on. Ask for the smaller one first. - A 403 that says the key must be allowed for every domain (suppression list, shared templates, settings) means the human has to widen the key; do not look for another route.
- Over MCP, every route has a tool with the same scope (
x-mcp-toolin /openapi.json).tools/listshows only what the grant allows.
Minimal send
POST /api/v1/emails
x-api-key: sb...
Content-Type: application/json
{"from":"hello@domain.com","to":["user@example.com"],"subject":"Hi","text":"Hi"}200: { "messageId": "...", "id": "..." }. Confirm with GET /api/v1/emails/{id}.
Choosing routes
| Goal | Scope | Route |
|---|---|---|
| Send transactional mail | email:send | POST /api/v1/emails |
| Read inbox | inbox:read | GET /api/v1/conversations?root= |
| Reply in-thread | inbox:reply | POST /api/v1/messages/{id}/reply |
| Check the daily sending allowance | campaigns:read | GET /api/v1/campaigns/allowance |
| See how a campaign will go out | campaigns:read | GET /api/v1/campaigns/{id}/plan |
| Start a campaign | campaigns:send | POST /api/v1/campaigns/{id}/start |
| Pause or resume a campaign | campaigns:send | POST /api/v1/campaigns/{id}/pause or /resume |
| See who a campaign reached | campaigns:read | GET /api/v1/campaigns/{id}/recipients |
| Add a contact | audience:write | POST /api/v1/contacts |
| Record that someone unsubscribed | audience:write | PATCH /api/v1/contacts/{email}/subscriptions |
| Send a marketing email to one person | email:send | POST /api/v1/emails with topicId |
| Set the address marketing email needs | settings:write | PUT /api/v1/email-settings |
| Remove contacts | audience:delete | DELETE /api/v1/contacts/{email}?root= or POST /api/v1/contacts/bulk |
| Export contacts | audience:export | POST /api/v1/contact-exports |
| Tell an automation something happened | automations:write | POST /api/v1/events |
| Turn an automation on | automations:manage | POST /api/v1/automations/{id}/start |
| Create a template | templates:write | POST /api/v1/templates |
| Check a domain's setup | domains:read | GET /api/v1/domains/{root}/onboarding |
Inbox rules
- A key limited to specific addresses gets 404 (not 403) for other message ids.
- Reply recipients are server-derived. Do not send
toon the reply route. - Pass
cursorfor lists. Do not assume one page is complete.limitmax is 50.
Campaign rules
- Before starting or scheduling a campaign, read
GET /api/v1/campaigns/{id}/plan. - If
needsChoiceis true, it does not fit in one day. Ask the human: send it over several days, send to fewer people, or raise the Amazon sending limit first. Never choose for them. IfcanFinishis false, it cannot start as it is (no allowance left for campaigns, or Gradual Send settings that would take over a year): tell the human. - Only when the human chose several days, start it with
{"spreadOverDays": true}. Send it on every start or schedule; an earlier choice does not count. Without it, the start is refused with a 400 that gives the reason. - Changing a scheduled campaign's audience, time, or Gradual Send settings is checked again. If the edit is refused because it no longer fits in one day, tell the human.
waitingFor: "DAILY_LIMIT"or"GRADUAL_NEXT_DAY"is normal. The campaign carries on by itself. Do not cancel or restart it.- If a campaign is
PAUSEDwithpausedBy: "SENDBUNNY", tell the human thepausedReasonbefore you resume it. - To delete a campaign that is queuing, sending, or paused, cancel it first.
Subscription rules
- Never turn someone's no back into a yes. Send
signedUpAgain: trueonly when the person signed up again themselves through a form. A409on a yes means only they can give it: tell the human, do not retry. POST /api/v1/emailswithouttopicIdis for receipts and other transactional mail. Anything promotional needs atopicId.- A
422withcodeUNSUBSCRIBED,TOPIC_OFForWAITING_TO_CONFIRMmeans the person said no or has not confirmed. Do not retry, and do not resend it without the topic. - A
422FOOTER_MISSING, or a campaign that will not start for the same reason, needs the domain's company name and postal address. Ask the human for them; never make them up.