Skip to main content
A draft is a composed message that has not been sent. It is the right primitive whenever a person should see what the agent wrote before the recipient does.

The approval loop

1

The agent composes

Your model produces the reply; you store it as a draft rather than sending it.
2

A human reviews

Show the draft in your own UI. GET /v0/inboxes/{inbox_id}/drafts lists what is pending.
3

Edit if needed

4

Send, or discard

Sending threads the message exactly as a direct send would — thread_id and in_reply_to are honoured, so an approved reply lands on the right conversation rather than starting a new one.
This loop is worth reaching for earlier than feels necessary. An agent that drafts is recoverable when it gets something wrong; an agent that sends is not, because email has no unsend.

Scheduled sending

Set send_at and the draft sends itself at that time — no cron on your side, no process that has to stay up.
The inbox schedules an internal alarm for that timestamp. A scheduled draft is an ordinary draft until it fires, so you can still edit it, reschedule it by patching send_at, or delete it to cancel.
send_at combines well with thread_id: a follow-up queued at the moment the agent makes the promise, on the conversation where it made it. Nothing has to remember to do it later.

Idempotency

client_id is yours to set and is stored with the draft. Use it to tie a draft back to whatever produced it — a ticket, an approval row, a run id — so a retry in your own code can look up whether a draft already exists rather than creating a second one.