Drafts and approvals
How drafts, scheduled sends and owner approval work, and how to approve, reject or resolve a message whose outcome is unknown.
Drafts
A draft is an unsent message. Agents and the owner can create, edit, schedule and delete drafts with the draft_* permissions. Drafts may be incomplete; recipients, size and attachment limits are checked only when the draft is sent or approved.
inbot drafts create support@agents.example.com --to customer@example.org \
--subject "Your order" --text "Hello..."
inbot drafts list --inbox support@agents.example.com
inbot drafts send support@agents.example.com <draft_id> --idempotency-key order-42
A draft can start a new thread, reply to a message (in_reply_to, with reply_all) or forward one (forward_of). Replying or forwarding also needs message_read on the original.
| Field | Values |
|---|---|
approval_state |
pending, approved or rejected |
pending_approval |
true while the owner has to decide |
send_status |
scheduled, sending or failed, when relevant |
send_at |
Scheduled time, if any |
rejection_reason |
The owner’s reason, after a rejection |
A draft that is sending cannot be edited, deleted or rejected; edits return resource_taken.
Scheduling
Set send_at when creating or updating a draft to schedule it. Scheduling needs draft_send and a send policy other than read_only. Set send_at to null, or delete the draft, to cancel.
Creation, editing and approval wake the scheduler; it waits until the next due time and wakes the outbox as soon as a send commits. Its 30-second idle and 2-second after-work checks are recovery caps.
The scheduler only sends approved drafts, and only while the service is running. After downtime it sends overdue drafts as soon as it starts, and the message’s timestamp is the time it was actually queued. send_at is the earliest time Inbot queues the message, not a delivery promise. drafts send sends immediately and overrides any schedule.
Approval
A key with the approval send policy never sends on its own:
- Its drafts are created as
pending, and editing an approved draft makes itpendingagain. send_message,reply_to_message,forward_messageandsend_draftcreate or reuse a pending draft and return the errorapproval_required, with the draft’s ID inmessageandfix. No message ID exists yet.- A
draft.approval_requiredevent fires, so you can be notified by webhook.
Pending drafts appear on the Approvals page. Review the recipients, text and attachment download buttons, then:
- Approve queues the message, or keeps it scheduled if
send_atis in the future. If the transport or composition fails, the draft stays pending. - Reject, with an optional reason, is final. The draft can no longer be sent or approved, and any schedule is cancelled. The agent sees
approval_state: "rejected"and the reason.
inbot drafts approve support@agents.example.com <draft_id>
inbot drafts reject support@agents.example.com <draft_id> --reason "Wrong customer"
Attachment buttons issue signed five-minute download capabilities. They require no bearer, recheck the issuer’s current access, and must never be shared or logged.
Approve and reject are owner-only. There is no MCP tool for them, and agent keys get missing_permission. An agent that retries a send with the same idempotency key gets the same approval_required answer for the same draft, even after you decide.
Resolve an unknown send outcome
If Inbot cannot tell whether Cloudflare accepted a message, for example after a network timeout, the message ends in send_unknown. Inbot never resends it automatically, because that could send it twice. See Message states.
Find out whether the message went out, for example by asking the recipient or checking Cloudflare, then record the answer. In the web app, open the message and choose Resolve outcome. From the CLI:
inbot messages resolve support@agents.example.com <message_id> --decision sent
inbot messages resolve support@agents.example.com <message_id> --decision failed
Resolving only records the outcome and emits message.sent or message.failed; it never calls Cloudflare. To try again after failed, send a new message with a new idempotency key. Resolving is owner-only, available through the local CLI and POST /v0/inboxes/{inbox_id}/messages/{message_id}/resolve, and blocked over the tunnel.