.md

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... (or Authorization: Bearer sb...)
  • Errors: { "error": string }. 401 is always Invalid API key.
  • to is always a JSON array. A string is a 400.
  • Do not call POST /api or /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:write edits campaigns, campaigns:send starts them. audience:write adds and edits people, audience:delete removes them. automations:write builds, automations:manage turns 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-tool in /openapi.json). tools/list shows only what the grant allows.

Minimal send

POST /api/v1/emails
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

GoalScopeRoute
Send transactional mailemail:sendPOST /api/v1/emails
Read inboxinbox:readGET /api/v1/conversations?root=
Reply in-threadinbox:replyPOST /api/v1/messages/{id}/reply
Check the daily sending allowancecampaigns:readGET /api/v1/campaigns/allowance
See how a campaign will go outcampaigns:readGET /api/v1/campaigns/{id}/plan
Start a campaigncampaigns:sendPOST /api/v1/campaigns/{id}/start
Pause or resume a campaigncampaigns:sendPOST /api/v1/campaigns/{id}/pause or /resume
See who a campaign reachedcampaigns:readGET /api/v1/campaigns/{id}/recipients
Add a contactaudience:writePOST /api/v1/contacts
Record that someone unsubscribedaudience:writePATCH /api/v1/contacts/{email}/subscriptions
Send a marketing email to one personemail:sendPOST /api/v1/emails with topicId
Set the address marketing email needssettings:writePUT /api/v1/email-settings
Remove contactsaudience:deleteDELETE /api/v1/contacts/{email}?root= or POST /api/v1/contacts/bulk
Export contactsaudience:exportPOST /api/v1/contact-exports
Tell an automation something happenedautomations:writePOST /api/v1/events
Turn an automation onautomations:managePOST /api/v1/automations/{id}/start
Create a templatetemplates:writePOST /api/v1/templates
Check a domain's setupdomains:readGET /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 to on the reply route.
  • Pass cursor for lists. Do not assume one page is complete. limit max is 50.

Campaign rules

  • Before starting or scheduling a campaign, read GET /api/v1/campaigns/{id}/plan.
  • If needsChoice is 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. If canFinish is 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 PAUSED with pausedBy: "SENDBUNNY", tell the human the pausedReason before 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: true only when the person signed up again themselves through a form. A 409 on a yes means only they can give it: tell the human, do not retry.
  • POST /api/v1/emails without topicId is for receipts and other transactional mail. Anything promotional needs a topicId.
  • A 422 with code UNSUBSCRIBED, TOPIC_OFF or WAITING_TO_CONFIRM means the person said no or has not confirmed. Do not retry, and do not resend it without the topic.
  • A 422 FOOTER_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.