CarlyEmail docs

Sending

Getting mail out, and what can stop it.

curl -X POST "https://api.carlyemail.com/v0/inboxes/$INBOX_ID/messages/send" \
  -H "Authorization: Bearer $CARLYEMAIL_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "to": ["someone@example.org"],
    "cc": [],
    "subject": "Hello",
    "text": "Plain body",
    "html": "<p>HTML body</p>"
  }'

Supply text, html, or both. Both is best — some clients will only render one.

The response carries message_id and thread_id. The message also lands in the inbox labelled sent, so a thread reads as a conversation rather than only the half that came back.

Four things refuse a send

In the order you will meet them:

  1. The organization is unverified403 organization_unverified. Confirm the owner email. See limits.
  2. Quota exceeded429 with an exact Retry-After.
  3. The recipient is suppressed — they hard-bounced or complained. See suppression.
  4. The key lacks message_send403 insufficient_permissions.

Replying

Reply rather than send, so the conversation threads in the recipient's client too:

curl -X POST "https://api.carlyemail.com/v0/inboxes/$INBOX_ID/messages/$MESSAGE_ID/reply" \
  -H "Authorization: Bearer $CARLYEMAIL_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"text": "Thanks — looking into it now.", "reply_all": false}'

Recipients are derived from the message being replied to, so to is optional. The reply goes to whoever sent the message — or, when the message is one this inbox sent, on to its original recipients. reply_all: true adds everyone else on the thread as Cc.

The inbox itself is always excluded, so an auto-replying agent cannot answer itself in a loop. That leaves one case with nothing to derive: a message whose only participant is this inbox, which is what a "mail yourself" test produces. Replying to one of those returns no_reply_recipient — pass to to say where the reply should actually go.

Naming to overrides the derived recipients entirely, which is how you redirect an answer somewhere else. Naming cc replaces the derived Cc rather than adding to it, so a reply_all can be narrowed as well as widened.

Scheduling

Create a draft with a future send_at timestamp to send later. The scheduled worker runs once a minute, applies the same verification, suppression, quota, attachment and threading rules as an immediate send, and leaves a failed draft visible rather than silently dropping it. See Drafts for the complete example and cancellation behavior.