CarlyEmail docs

Drafts

Compose now, send later.

A draft is a message that has not been sent. Useful when a human approves what an agent wrote before it goes out.

curl -X POST https://api.carlyemail.com/v0/inboxes/$INBOX_ID/drafts \
  -H "Authorization: Bearer $CARLYEMAIL_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "to": ["customer@example.org"],
    "subject": "Following up",
    "text": "Draft body"
  }'

Update it, attach files, then send:

curl -X POST https://api.carlyemail.com/v0/inboxes/$INBOX_ID/drafts/$DRAFT_ID/send \
  -H "Authorization: Bearer $CARLYEMAIL_API_KEY"

Sending a draft applies the same quota, suppression and verification checks as a direct send — a draft is not a way around them.

Reply and forward drafts

Pass in_reply_to to build a reply from an existing message. CarlyEmail derives the recipients, Re: subject and threading headers, so the saved draft is ready for human review rather than merely tagged as a reply. Set reply_all: true to include the other original recipients; do not also supply to, cc or bcc.

Pass forward_of to build a forward. CarlyEmail derives the Fwd: subject and merges the original body and attachments when the draft is sent, while keeping the editable draft body as your preface. in_reply_to and forward_of are mutually exclusive, and both require message_read permission.

Schedule a send

Set send_at when creating or updating a draft. It must be a future ISO-8601 timestamp with a timezone:

curl -X POST https://api.carlyemail.com/v0/inboxes/$INBOX_ID/drafts \
  -H "Authorization: Bearer $CARLYEMAIL_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "to": ["customer@example.org"],
    "subject": "Checking in tomorrow",
    "text": "How did the launch go?",
    "send_at": "2026-08-04T14:00:00Z"
  }'

The worker checks once a minute, so delivery starts within roughly a minute of send_at; it is not a second-precision timer. While waiting, send_status is scheduled. The draft is consumed after a successful send. If sending fails, the draft stays readable with send_status: "failed" instead of retrying forever or disappearing.

Cancel a pending schedule without deleting the draft:

curl -X PATCH https://api.carlyemail.com/v0/inboxes/$INBOX_ID/drafts/$DRAFT_ID \
  -H "Authorization: Bearer $CARLYEMAIL_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"send_at": null}'

Scheduled delivery is at-least-once. The worker atomically claims a draft to prevent concurrent sends, but no email provider can make “SES accepted the message” and “our database recorded success” one transaction. A process crash in that narrow gap can produce a duplicate, so recipients of automated mail should tolerate one.