Docs · Guides

Drafts and scheduled send

Let an agent write email that a person approves before it goes out, and schedule sends for later with send_at.

A draft is an email an agent (or you) wrote that hasn’t been sent. It waits in the inbox’s Drafts tab in the dashboard until someone edits it, approves it, schedules it or cancels it. Use drafts for a human in the loop: the agent can write, but only a person, or a supervising agent with messages:send, can send.

POST /v1/inboxes/:id/drafts
curl -s -X POST https://api.agentboxd.com/v1/inboxes/$INBOX_ID/drafts \
  -H "Authorization: Bearer $AGENTBOXD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reply_to_message_id":"'$MESSAGE_ID'","text":"Hi Dana, the duplicate charge is refunded."}'

# approve it
curl -s -X POST https://api.agentboxd.com/v1/inboxes/$INBOX_ID/drafts/$DRAFT_ID/send \
  -H "Authorization: Bearer $AGENTBOXD_API_KEY"

#Statuses

StatusMeansYou can
draftWaiting for review.Edit, send, schedule, cancel, delete.
scheduledWill be sent at send_at.Edit, send now, reschedule, unschedule (send_at: null), cancel, delete.
sendingBeing handed to the outbound queue right now.Wait a moment.
sentQueued as a message: sent_message_id points at it. The draft’s text and files are cleared; the message keeps them.Delete.
failedA scheduled send was refused; error says why.Send again, schedule again, cancel, delete.
cancelledWill never be sent.Delete.

#Replies

Create a draft with reply_to_message_id (or thread_id, which replies to the thread’s latest message) and it is filled in like a reply: the recipients (reply_all like the reply call), the Re: subject, and In-Reply-To and References when it is sent. Anything you pass explicitly wins. A draft may also start incomplete; send checks that it has a recipient and some text (400 draft_incomplete).

Reply drafts from the model can go straight into a draft: POST /v1/messages/:id/draft-reply with { "save": true }. See Reply drafts.

#Approving a draft

POST /v1/inboxes/:id/drafts/:draftId/send sends it through exactly the same path as a normal send: allow/block lists, suppressions, the plan’s monthly emails, the burst limit and the daily caps are all checked, and it honours Idempotency-Key. It answers 202 { draft, message }. If a check refuses it, you get that error and the draft stays as it was, with the reason in error.

#Scheduled send

Set send_at (ISO 8601, at least 1 minute and at most 30 days ahead) when you create or update a draft, or call POST /v1/inboxes/:id/drafts/:draftId/schedule { send_at }. The checks run again when it is sent, so a send that became impossible meanwhile (quota used up, recipient blocked) ends failed with a draft.failed webhook. A send that hits the burst limit is retried a few minutes later. POST …/cancel stops it.

Plain sends can be scheduled too: send_at on POST /v1/inboxes/:id/messages/send or …/reply creates a scheduled draft and returns it (202) instead of a message.

schedule a send
curl -s -X POST https://api.agentboxd.com/v1/inboxes/$INBOX_ID/messages/send \
  -H "Authorization: Bearer $AGENTBOXD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"dana@example.com","subject":"Your weekly report","text":"…","send_at":"2026-10-01T08:00:00Z"}'

#Webhooks and permissions

Drafts emit draft.created, draft.updated, draft.sent, draft.failed and draft.cancelled (Webhooks). Reading drafts needs drafts:read; creating, editing, cancelling and deleting need drafts:write; sending or scheduling one also needs messages:send. An inbox-scoped key sees only its inbox’s drafts.

MCP clients have create_draft, list_drafts, get_draft, send_draft, schedule_draft and cancel_draft (MCP server).