APIReference

The MySendbox API.

One REST API for both products. Send transactional email from your app with Dispatch, and work with your AI agent's mailboxes: read mail, approve or reject what the agent wants to do, and write drafts.

01

Overview

  • Base URL: https://api.mysendbox.com. Every route is under /v1.
  • Requests and responses are JSON. Send Content-Type: application/json with a body.
  • Times are Unix timestamps in milliseconds, such as createdAt.
  • The email and template routes need Dispatch (the Email product) turned on for your team. The mailbox routes are for Agents.
  • Using an agent such as Claude, ChatGPT or Cursor? Connect it over MCP instead: Bring your own agent.
02

Endpoints

Emails and templatesDispatch
POST    /v1/emails
POST    /v1/emails/batch
GET     /v1/emails
GET     /v1/emails/{id}
POST    /v1/emails/{id}/cancel
GET     /v1/templates
GET     /v1/templates/{id or name}
POST    /v1/templates
PATCH   /v1/templates/{id or name}
DELETE  /v1/templates/{id or name}
MailboxesAgents
GET     /v1/mailboxes
GET     /v1/mailboxes/{id}/storage
GET     /v1/mailboxes/{id}/messages
GET     /v1/mailboxes/{id}/messages/{messageId}
POST    /v1/mailboxes/{id}/messages/{messageId}/agent
GET     /v1/mailboxes/{id}/actions
GET     /v1/mailboxes/{id}/actions/{actionId}
POST    /v1/mailboxes/{id}/actions/{actionId}/approve
POST    /v1/mailboxes/{id}/actions/{actionId}/reject
POST    /v1/mailboxes/{id}/drafts
03

Authentication

A team owner creates API keys in the dashboard at app.mysendbox.com, under Integrations → API keys. The key is shown once. Send it as a bearer token on every request:

Header
Authorization: Bearer msb_live_…
  • No key, or a malformed header: 401 with missing API key.
  • An unknown or revoked key: 401 with invalid API key.
  • Keys belong to your team. A mailbox that isn't yours returns 404, the same as one that doesn't exist.

For AI agents: sign up with one call

An agent can create its own workspace with no account and no email. It gets a mailbox at once (an address on our agent domain), an API key with every scope, and an activation link. Receiving works straight away; sending starts once a person opens the link and starts the 7-day trial ($1, card required). The key and the link are shown once.

Requestcurl
curl -X POST https://api.mysendbox.com/v1/signup \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme booking agent", "mailbox": "bookings", "accept_terms": true }'
Response201
{
  "workspace": { "id": "tnt_01k8…", "name": "Acme booking agent", "status": "inactive" },
  "mailbox": { "id": "mbx_01k8…", "address": "bookings-k3m9qx@…" },
  "api_key": "msb_live_…",
  "activate_url": "https://app.mysendbox.com/activate#act_…",
  "next": "Receiving works now. Give activate_url to a person …"
}
  • name — 2 to 80 characters. mailbox — optional label, 2 to 20 lowercase letters, digits or hyphens; a random suffix is added. accept_terms — must be true: the terms and the acceptable use policy.
  • The activation link lets a person start the plan, buy credits, issue a new API key if the agent lost its own, and take ownership with an email sign-in. Whoever has it can do those things: hand it only to the person who'll pay.
  • GET /v1/account (any key) — status (inactive, trialing, active …), can_send, credits and mailboxes. POST /v1/account/activation-link — a new link; earlier ones stop working.
  • The address only replies in conversations someone else started. Your own domain and Dispatch need an owner (take ownership first).
  • Limits: 3 sign-ups a day per network. A workspace nobody paid for or claimed within 7 days is switched off: its key is revoked and its mailbox removed.
04

Scopes

Each key has scopes. Give it only the ones it needs. A request outside them gets 403 with this key lacks the … scope.

  • emails:send — send and cancel email.
  • emails:read — read the email log and templates.
  • templates:write — create, update and delete templates.
  • messages:read — list mailboxes and read their messages.
  • agent:run — run the agent on a message.
  • actions:read — read agent actions.
  • actions:write — approve and reject agent actions.
  • drafts:write — create drafts.
  • messages:write and replies:send — used by the MCP server to tidy mailboxes and send replies. No REST route uses them.
05

Errors

Errors return a status code and a JSON body with one message:

Error422
{ "error": "subject is required" }
  • 400 — the body isn't JSON, or a field is missing or malformed.
  • 401 — no API key, or it's invalid or revoked.
  • 402 — your team has no plan (no plan), or not enough credits for these emails with automatic top-up off (not enough credits). Each email uses 1 credit.
  • 403 — the key lacks a scope, or Dispatch isn't turned on for your team.
  • 404 — the route, email, template, mailbox, message or action doesn't exist.
  • 409 — a conflict: an Idempotency-Key reused with a different request, a template name that's taken, an email that's no longer scheduled, or an action that's no longer pending.
  • 422 — the email or template doesn't pass validation, such as an unverified From domain.
  • 500 — something failed on our side: { "error": "internal error" }.
06

Idempotency

Send an Idempotency-Key header with POST /v1/emails or POST /v1/emails/batch so a retry never sends twice. Use something tied to the event, such as receipt-A-2291.

  • 1 to 256 printable ASCII characters, no spaces. Anything else is a 400.
  • Keys are kept for 24 hours.
  • The same key with the same body returns the same email IDs, and nothing new is sent.
  • The same key with a different body is a 409.
  • Single sends and batches keep separate keys.
07

Send an email

POST /v1/emails · scope emails:send

Accepts one email for sending and returns its ID with 202. Sending happens right after; follow it with Get an email. The From address must be on a domain you've added and verified, and the domain must be active.

  • from string, required — billing@acme.example or Acme <billing@acme.example>. Display names up to 100 characters.
  • to string or array — recipients. At least one of to, cc or bcc is required, and at most 50 in total.
  • cc, bcc string or array, optional
  • reply_to string or array, optional — also accepted as replyTo.
  • subject string, required without a template — up to 998 characters.
  • html, text string — the body. One is required without a template. Up to 1,000,000 characters each.
  • template string or object, optional — a template ID or name, or { "id": "…", "variables": { … } } (name works in place of id). See Templates.
  • variables object, optional — template variables, when template is a string.
  • attachments array, optional — up to 20, 10 MB in total. Each is { "filename", "content", "content_type" }: content is base64, and content_type is optional (it's taken from the file extension when left out).
  • headers object, optional — up to 20. Only X-* headers, List-Unsubscribe and List-Unsubscribe-Post. Values up to 1,000 characters.
  • tags object or array, optional — up to 10, as { "order": "A-2291" } or [{ "name": "order", "value": "A-2291" }]. Letters, digits, _ and - only.
  • scheduled_at string, optional — an ISO 8601 date-time up to 72 hours ahead. A time in the past sends now. Also accepted as scheduledAt.
  • tracking boolean, optional — link click tracking for this email. Left out, the sending domain's setting applies (off unless you turn it on under Domains). See Link click tracking.
Requestcurl
curl https://api.mysendbox.com/v1/emails \
  -H "Authorization: Bearer msb_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: receipt-A-2291" \
  -d '{
    "from": "Acme <billing@acme.example>",
    "to": ["jane@example.com"],
    "subject": "Your receipt #A-2291",
    "html": "<p>Thanks for your payment.</p>",
    "tags": { "order": "A-2291" }
  }'
Response202
{ "id": "em_01k8r2m4qd7x9c3vtw5n6hbj0e" }

The API is for transactional email. A request with more than 10 identical emails (same subject and body) is refused as a broadcast, with 422.

Link click tracking

Off by default. With it on, each http or https link in the HTML part goes through a redirect on your tracking domain (such as links.acme.example, set up under Domains), so you can see which links were clicked: in the email's clicks, in the dashboard, and as the email.clicked webhook event (once per link, on the first click by a person). Opens aren't tracked.

  • Left as written: the text part, mailto: and tel: links, the unsubscribe link, and links that look like sign-in, password reset or verification links. To leave any other link alone, add data-msb-notrack to it: <a href="…" data-msb-notrack> (the attribute is removed before sending).
  • Send sign-in links, password resets and codes with "tracking": false to be sure they're never rewritten.
  • Mail filters such as Microsoft Defender open links before people do. Those visits are kept with suspectedBot: true, aren't counted and don't fire the webhook.
  • Clicks are kept 90 days; tracked links keep redirecting for a year. No IP addresses are stored.
08

Send a batch

POST /v1/emails/batch · scope emails:send

Accepts 1 to 100 emails in one request. The body is a JSON array of emails with the same fields as Send an email, except that batch emails can't have attachments or scheduled_at. If any email fails validation, none are sent. IDs come back in the order you sent them.

Requestcurl
curl https://api.mysendbox.com/v1/emails/batch \
  -H "Authorization: Bearer msb_live_…" \
  -H "Content-Type: application/json" \
  -d '[
    { "from": "Acme <billing@acme.example>", "to": ["jane@example.com"],
      "template": { "id": "receipt", "variables": { "name": "Jane", "total": "$42.00" } } },
    { "from": "Acme <billing@acme.example>", "to": ["sam@example.com"],
      "template": { "id": "receipt", "variables": { "name": "Sam", "total": "$18.50" } } }
  ]'
Response202
{
  "data": [
    { "id": "em_01k8r2m4qd7x9c3vtw5n6hbj0e" },
    { "id": "em_01k8r2m4qe2b8f6kz1y3a7d5gs" }
  ]
}
09

List emails

GET /v1/emails · scope emails:read

Your email log, newest first. Query parameters, all optional:

  • status — one of scheduled, queued, sending, sent, delivered, bounced, complained, failed, suppressed, canceled.
  • to — only emails to this address (in to, cc or bcc).
  • before — an email ID. Returns emails older than it: pass the last ID of a page to get the next.
  • limit — 1 to 100. Default 50.
Requestcurl
curl "https://api.mysendbox.com/v1/emails?status=bounced&limit=20" \
  -H "Authorization: Bearer msb_live_…"
Response200, trimmed
{
  "data": [
    {
      "id": "em_01k8r2m4qd7x9c3vtw5n6hbj0e",
      "status": "bounced",
      "from": { "address": "billing@acme.example", "name": "Acme" },
      "to": ["jane@example.com"],
      "subject": "Your receipt #A-2291",
      "createdAt": 1793280000000,
      …
    }
  ]
}
10

Get an email

GET /v1/emails/{id} · scope emails:read

One email with its delivery events, oldest first. Add ?body=1 to include what was sent: body with html, text, attachments and headers.

  • status — where the email is. suppressed means its recipients are on your suppression list, which hard bounces and complaints add to.
  • events[].type — sent, delivery, delivery_delay, soft_bounce, bounce, complaint, reject, deferred, suppressed, failed or click (the first click on a link by a person, with its url).
  • clicks — tracked link clicks, oldest first: index (the link's position among the tracked links), url, at, agent (browser, mail_app, scanner or other) and suspectedBot.
  • tracking — true or false when the request set it, otherwise null.
  • error — the reason for a failure, suppression or bounce, or null.
  • templateId — the template it was rendered from, or null.
Requestcurl
curl https://api.mysendbox.com/v1/emails/em_01k8r2m4qd7x9c3vtw5n6hbj0e \
  -H "Authorization: Bearer msb_live_…"
Response200, trimmed
{
  "id": "em_01k8r2m4qd7x9c3vtw5n6hbj0e",
  "status": "delivered",
  "from": { "address": "billing@acme.example", "name": "Acme" },
  "to": ["jane@example.com"],
  "cc": [],
  "bcc": [],
  "replyTo": [],
  "subject": "Your receipt #A-2291",
  "tags": { "order": "A-2291" },
  "templateId": null,
  "createdAt": 1793280000000,
  "scheduledAt": null,
  "sentAt": 1793280001200,
  "tracking": null,
  "error": null,
  "events": [
    { "type": "sent", "at": 1793280001200, "detail": null },
    { "type": "delivery", "at": 1793280003900, "detail": null },
    { "type": "click", "at": 1793280912000, "detail": { "url": "https://acme.example/orders/A-2291", "index": 0 } }
  ],
  "clicks": [
    { "index": 0, "url": "https://acme.example/orders/A-2291", "at": 1793280004100, "agent": "scanner", "suspectedBot": true },
    { "index": 0, "url": "https://acme.example/orders/A-2291", "at": 1793280912000, "agent": "browser", "suspectedBot": false }
  ]
}
11

Cancel a scheduled email

POST /v1/emails/{id}/cancel · scope emails:send

Cancels an email sent with scheduled_at before its time comes, and returns it with status canceled. An email that isn't scheduled any more is a 409.

Requestcurl
curl -X POST https://api.mysendbox.com/v1/emails/em_01k8r2m4qd7x9c3vtw5n6hbj0e/cancel \
  -H "Authorization: Bearer msb_live_…"
Response200, trimmed
{
  "id": "em_01k8r2m4qd7x9c3vtw5n6hbj0e",
  "status": "canceled",
  "scheduledAt": 1793300400000,
  …
}
12

Templates

A template holds a subject and an HTML and/or text body with {{name}} placeholders. Send with template and the variables fill them in.

  • Nested values work with dots: {{order.total}}.
  • Values are HTML-escaped in the HTML body.
  • A missing variable is an error (422), never a blank.
  • subject, html or text in the send request win over the template's.
  • Templates are addressed by ID (tpl_…) or by name.
13

List templates

GET /v1/templates · scope emails:read

All your templates, by name.

Requestcurl
curl https://api.mysendbox.com/v1/templates \
  -H "Authorization: Bearer msb_live_…"
Response200, trimmed
{
  "data": [
    {
      "id": "tpl_01k8qz7c2m5v9x3b6n4d8f1h0j",
      "name": "receipt",
      "subject": "Your receipt, {{name}}",
      "html": "<p>Thanks, {{name}}. Total: {{total}}</p>",
      "text": null,
      "version": 3,
      "createdAt": 1793100000000,
      "updatedAt": 1793250000000
    }
  ]
}
14

Get a template

GET /v1/templates/{id or name} · scope emails:read

One template, in the same shape as the list.

Requestcurl
curl https://api.mysendbox.com/v1/templates/receipt \
  -H "Authorization: Bearer msb_live_…"
15

Create a template

POST /v1/templates · scope templates:write

  • name string, required — up to 100 characters: lowercase letters, digits, dots, dashes and underscores, starting with a letter or digit. Must be unique (409 if taken).
  • subject string, required — up to 998 characters.
  • html, text string — one is required. Up to 1,000,000 characters each.
Requestcurl
curl https://api.mysendbox.com/v1/templates \
  -H "Authorization: Bearer msb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "receipt",
    "subject": "Your receipt, {{name}}",
    "html": "<p>Thanks, {{name}}. Total: {{total}}</p>"
  }'
Response201
{
  "id": "tpl_01k8qz7c2m5v9x3b6n4d8f1h0j",
  "name": "receipt",
  "subject": "Your receipt, {{name}}",
  "html": "<p>Thanks, {{name}}. Total: {{total}}</p>",
  "text": null,
  "version": 1,
  "createdAt": 1793100000000,
  "updatedAt": 1793100000000
}
16

Update a template

PATCH /v1/templates/{id or name} · scope templates:write

Send only the fields to change: name, subject, html, text, with the same rules as create. Set html or text to null to remove it. Each update adds 1 to version. Returns the updated template.

Requestcurl
curl -X PATCH https://api.mysendbox.com/v1/templates/receipt \
  -H "Authorization: Bearer msb_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Thanks, {{name}}. Total: {{total}}" }'
17

Delete a template

DELETE /v1/templates/{id or name} · scope templates:write

Requestcurl
curl -X DELETE https://api.mysendbox.com/v1/templates/receipt \
  -H "Authorization: Bearer msb_live_…"
Response200
{ "id": "tpl_01k8qz7c2m5v9x3b6n4d8f1h0j", "deleted": true }
18

List mailboxes

GET /v1/mailboxes · scope messages:read

Your team's agent mailboxes, by address. Use the id in the mailbox routes below.

Requestcurl
curl https://api.mysendbox.com/v1/mailboxes \
  -H "Authorization: Bearer msb_live_…"
Response200
{
  "mailboxes": [
    { "id": "mbx_01k8m0d3x7c9v2b5n8q4w6e1rt", "address": "bookings@acme.example", "createdAt": 1792500000000 }
  ]
}

GET /v1/mailboxes/{id}/storage · scope messages:read — what the mailbox stores (messages and attachments, received and sent) against its quota, 5 GB:

Response200
{ "used": 1288490188, "quota": 5368709120, "state": "ok", "retentionDays": null }

state is ok, warn (80% full) or full. When a message doesn't fit, the oldest mail in Trash, then Quarantine, is deleted to make room; if it still doesn't fit, the message is dropped and a message.dropped webhook event is sent. Owners can set a retention period in the dashboard.

19

List messages

GET /v1/mailboxes/{id}/messages · scope messages:read

Message summaries in one folder, newest first. Query parameters, all optional:

  • folder — default INBOX. Mailboxes also have Sent, Drafts, Archive, Trash, Held and Quarantine.
  • before — a message uid. Returns older messages: pass the last uid of a page to get the next.
  • limit — 1 to 200. Default 50.
Requestcurl
curl "https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/messages?limit=20" \
  -H "Authorization: Bearer msb_live_…"
Response200
{
  "messages": [
    {
      "id": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
      "folder": "INBOX",
      "uid": 412,
      "modseq": 980,
      "threadId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
      "from": "sam@example.com",
      "subject": "Booking for Friday",
      "snippet": "Hi, can I move my booking to Friday at 3?",
      "flags": ["seen"],
      "hasAttachments": false,
      "quarantineReason": null,
      "receivedAt": 1793270000000
    }
  ]
}
20

Get a message

GET /v1/mailboxes/{id}/messages/{messageId} · scope messages:read

The full message: the summary fields plus recipients, threading headers, the text and HTML bodies, attachment metadata, authentication results, and the agent's state.

  • agentState — new, queued, running, done, skipped or failed, or null. agentNote says why, when there's a reason.
  • attachments — id, filename, contentType and size for each. Contents aren't included.
  • quarantineReason — virus, dmarc_reject or spam for mail kept in Quarantine, else null.
Requestcurl
curl https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/messages/01K8R1T6Y3W9C2V5B8N4M7Q0XZ \
  -H "Authorization: Bearer msb_live_…"
Response200, trimmed
{
  "id": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
  "folder": "INBOX",
  "threadId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
  "from": "sam@example.com",
  "to": ["bookings@acme.example"],
  "cc": [],
  "subject": "Booking for Friday",
  "messageIdHeader": "<CAF3x9…@mail.example.com>",
  "text": "Hi, can I move my booking to Friday at 3?\n\nSam",
  "html": null,
  "attachments": [],
  "agentState": "done",
  "agentNote": null,
  "receivedAt": 1793270000000,
  …
}
21

Run the agent on a message

POST /v1/mailboxes/{id}/messages/{messageId}/agent · scope agent:run

Queues the built-in agent for an INBOX message that it hasn't handled: one it never ran on, skipped, or failed on. No body.

  • 202 with "status": "queued" — the run is queued.
  • 409 with "status": "busy" — a run is already queued or in progress.
  • 409 with "status": "done" — the agent already handled it.
  • 404 — the message isn't in INBOX.
Requestcurl
curl -X POST https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/messages/01K8R1T6Y3W9C2V5B8N4M7Q0XZ/agent \
  -H "Authorization: Bearer msb_live_…"
Response202
{ "messageId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ", "status": "queued" }
22

Actions

An action is something the agent does or wants to do: send an email reply (kind: "send_email") or call one of your tools (kind: "tool_call"). Actions that need a person wait as held; drafts wait as draft.

  • status — held, draft, approved, sending, executed, rejected or failed.
  • payload — for an email: to, subject, body, inReplyTo, references. For a tool call: the tool and its arguments.
  • reasons — why it was held, such as rate_limit:mailbox. Empty when policy approved it.
  • decidedBy, decidedAt — who approved or rejected it, and when. API decisions show as apikey:{key id}.
  • providerMessageId, deliveryStatus — set once an email is sent and delivery events come in.
  • result — a send failure, delivery details, or the tool's result.
23

List actions

GET /v1/mailboxes/{id}/actions · scope actions:read

The 50 newest actions in the mailbox. Add ?status=held (or any status above) to filter; without it, you get every kind of action, as an audit trail.

Requestcurl
curl "https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/actions?status=held" \
  -H "Authorization: Bearer msb_live_…"
Response200, trimmed
{
  "actions": [
    {
      "id": "01K8R1V2C7X4B9N3M6Q8W5E0TY",
      "kind": "send_email",
      "threadId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
      "sourceMessageId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
      "status": "held",
      "payload": {
        "to": ["sam@example.com"],
        "subject": "Re: Booking for Friday",
        "body": "Hi Sam, Friday at 3 works. See you then.",
        "inReplyTo": "<CAF3x9…@mail.example.com>",
        "references": "<CAF3x9…@mail.example.com>"
      },
      "reasons": ["rate_limit:mailbox"],
      "decidedBy": null,
      "createdAt": 1793270004000,
      "decidedAt": null,
      "providerMessageId": null,
      "deliveryStatus": null,
      "result": null,
      …
    }
  ]
}
24

Get an action

GET /v1/mailboxes/{id}/actions/{actionId} · scope actions:read

One action, in the same shape as the list.

Requestcurl
curl https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/actions/01K8R1V2C7X4B9N3M6Q8W5E0TY \
  -H "Authorization: Bearer msb_live_…"
25

Approve an action

POST /v1/mailboxes/{id}/actions/{actionId}/approve · scope actions:write

Approves a held action or a draft, and returns it with status approved. An approved email is sent; an approved tool call runs. An action that isn't pending is a 409. The body is optional.

  • payload object, optional — an edited email to send instead. Email actions only (400 for a tool call, which is approved exactly as proposed). It replaces the whole payload: to array, required (1 to 50 addresses), subject string, required (up to 998 characters), body string, required (plain text, up to 100,000 characters), inReplyTo and references string, optional (copy them from the original payload to keep the reply in its thread).
Requestcurl
curl -X POST https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/actions/01K8R1V2C7X4B9N3M6Q8W5E0TY/approve \
  -H "Authorization: Bearer msb_live_…"
Requestwith an edit
curl -X POST https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/actions/01K8R1V2C7X4B9N3M6Q8W5E0TY/approve \
  -H "Authorization: Bearer msb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "payload": {
      "to": ["sam@example.com"],
      "subject": "Re: Booking for Friday",
      "body": "Hi Sam, Friday at 3 works. We have moved your booking.",
      "inReplyTo": "<CAF3x9…@mail.example.com>",
      "references": "<CAF3x9…@mail.example.com>"
    }
  }'
Response200, trimmed
{
  "id": "01K8R1V2C7X4B9N3M6Q8W5E0TY",
  "kind": "send_email",
  "status": "approved",
  "decidedBy": "apikey:key_01K8M0F5Z2X8C4V7B1N9Q3W6ER",
  "decidedAt": 1793271200000,
  …
}
26

Reject an action

POST /v1/mailboxes/{id}/actions/{actionId}/reject · scope actions:write

Rejects a held action or a draft. Nothing is sent or run. Returns the action with status rejected. An action that isn't pending is a 409. No body.

Requestcurl
curl -X POST https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/actions/01K8R1V2C7X4B9N3M6Q8W5E0TY/reject \
  -H "Authorization: Bearer msb_live_…"
27

Create a draft

POST /v1/mailboxes/{id}/drafts · scope drafts:write

Writes an email draft from the mailbox. A draft never sends by itself: it waits as an action with status draft until a person approves it in the dashboard, or a key with actions:write approves it.

  • to array, required — 1 to 50 addresses.
  • subject string, required — up to 998 characters.
  • body string, required — plain text, up to 100,000 characters.
  • replyToMessageId string, optional — a message in this mailbox to reply to. The draft joins its thread with the right reply headers. 404 if it doesn't exist.
Requestcurl
curl https://api.mysendbox.com/v1/mailboxes/mbx_01k8m0d3x7c9v2b5n8q4w6e1rt/drafts \
  -H "Authorization: Bearer msb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["sam@example.com"],
    "subject": "Re: Booking for Friday",
    "body": "Hi Sam, Friday at 3 works. See you then.",
    "replyToMessageId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ"
  }'
Response201, trimmed
{
  "id": "01K8R2A9D4X7C1V6B3N8M5Q2WE",
  "kind": "send_email",
  "threadId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
  "sourceMessageId": "01K8R1T6Y3W9C2V5B8N4M7Q0XZ",
  "status": "draft",
  "payload": {
    "to": ["sam@example.com"],
    "subject": "Re: Booking for Friday",
    "body": "Hi Sam, Friday at 3 works. See you then.",
    "inReplyTo": "<CAF3x9…@mail.example.com>",
    "references": "<CAF3x9…@mail.example.com>"
  },
  "reasons": ["created_by:apikey:key_01K8M0F5Z2X8C4V7B1N9Q3W6ER"],
  …
}
28

For AI agents: MCP

Agents that speak MCP don't need this API to work in a mailbox. Point them at https://mcp.mysendbox.com/mcp to read, search, draft and reply, with the same screening, approvals and limits as the built-in agent. Setup and tools: Bring your own agent.