cherami.
API referenceSaved drafts

Send a draft

POST /v1/drafts/{draft_id}/send

Read as Markdown ↗

POST /v1/drafts/{draft_id}/send

Requires a Claim-issued API key: Authorization: Bearer YOUR_CREDENTIAL.

POST /v1/drafts/{draft_id}/send with {} or {"idempotency_key":"YOUR_SEND_KEY"}.

Sending takes the current saved content, not a previously retrieved copy. The draft freezes as submitted when an outgoing attempt is reserved, with sent_message_id identifying that attempt. An edit that wins before reservation must be included or cause 409 draft_busy; a send never silently submits an older saved copy. Concurrent sends cannot reserve multiple submissions for the same draft.

Validation, ownership, permission, recipient-policy or quota failures before reservation leave it editable. After reservation it remains submitted for every provider outcome, including rejection and uncertainty. There is no automatic retry or return-to-draft operation. A deliberately new attempt requires a new draft; do not create one merely to resolve an unknown outcome.

The response uses the ordinary send receipt and outcomes: 201 for a new attempt, 200 with replayed: true when recovering. Location points to /v1/sent/{sent_message_id}. Acceptance is not proof of delivery. If outcome_persisted is false, retain the stronger immediate outcome even if later reads lag.

The draft ID itself prevents another submission, without expiry. Repeat the same send request to recover an uncertain attempt, not to restart it. This can preserve a possibly unsent attempt rather than risk a duplicate. Deleting the sent copy does not unlock the draft; recovery then returns 409 draft_result_unavailable (or idempotency_result_unavailable for an active sending key).

Optional sending keys share the ordinary account-scoped sending namespace and 24-hour lifetime. Their intent identifies this draft, not its mutable fields. Changing the draft ID or reusing a key from an ordinary send conflicts. Key expiry does not remove the draft's permanent submitted state. A replay recovered through the draft association need not include idempotency_expires_at; it does not allocate or renew a key.

Parameters

ParameterLocationRequiredTypeMeaning
draft_idpathYesstringOwned Cherami resource ID returned by the API.

Request body

JSON object. Total UTF-8 request body limit: 4096 bytes.

Schema: SendDraft

FieldRequiredTypeMeaning and constraints
idempotency_keyNostringRetain a unique key, exact payload and first request time for this intended operation. Account-scoped protection lasts 24 hours without renewal. pattern: ^[A-Za-z0-9_-]{1,128}$

Unknown fields are rejected.

curl example

Replace resource-ID placeholders with returned IDs. Supply CHERAMI_API_KEY through your private shell environment.

Save the intended payload as a private request.json file and replace illustrative values. For an uncertain response, follow the operation-specific recovery rules.

{}
curl --silent --show-error --include --request POST \
  "https://cherami.to/v1/drafts/DRAFT_ID/send" \
  --header "Authorization: Bearer $CHERAMI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json

Responses

HTTP 200

Matching replay: current resource or saved attempt, without another allocation or provider submission.

  • X-Request-ID: Support correlation ID, not an idempotency key.
  • Location: Relative URL of the resulting resource.

Content type: application/json.

All of these schemas apply.

Part 1

SendReceipt

Part 2

FieldRequiredTypeMeaning and constraints
replayedYestrue
{
  "limited": false,
  "message": {
    "id": "33333333-3333-4333-8333-333333333333",
    "inbox_id": "11111111-1111-4111-8111-111111111111",
    "created_at": "2026-10-01T00:00:00.000Z",
    "recipient_count": 1,
    "status": "unknown",
    "provider_message_id": null,
    "error_code": null,
    "thread_id": "44444444-4444-4444-8444-444444444444",
    "in_reply_to": null,
    "labels": []
  },
  "outcome_persisted": true,
  "replayed": true
}

HTTP 201

Successful operation; inspect resource state and outcome fields.

  • X-Request-ID: Support correlation ID, not an idempotency key.
  • Location: Relative URL of the resulting resource.

Content type: application/json.

All of these schemas apply.

Part 1

SendReceipt

Part 2

FieldRequiredTypeMeaning and constraints
replayedNofalse

HTTP 400

invalid_json: Send a valid UTF-8 JSON object, not an array or scalar.

invalid_message: Correct the send fields, recipients, reply ID, or attachments using the message's guidance.

invalid_name: Correct the inbox, sender or recipient display name using the returned guidance.

invalid_labels: Correct label names, changes, filter groups, or discovery prefix.

invalid_idempotency_key: Use 1–128 ASCII letters, digits, hyphens or underscores.

invalid_draft: Use supported draft fields, source preparation or listing parameters. Draft cursors that are malformed or do not match the inbox/state also use this code.

  • X-Request-ID: Support correlation ID, not an idempotency key.

Content type: application/json.

Error

HTTP 401

unauthorized: Provide a valid bearer credential. Use human-approved recovery if access is lost.

  • X-Request-ID: Support correlation ID, not an idempotency key.
  • WWW-Authenticate: "Bearer"

Content type: application/json.

Error

HTTP 403

operation_not_allowed: This account cannot send mail, delete mail, or delete inboxes. Contact support if unexpected.

recipient_not_allowed: The inbox’s sending rules block one or more recipients. Nothing was submitted or charged. Use allowed recipients or ask the human to review Sending rules; do not bypass them through another inbox.

  • X-Request-ID: Support correlation ID, not an idempotency key.

Content type: application/json.

Error

HTTP 404

not_found: Resource is absent or inaccessible to this account. Reply targets must be in the sending inbox.

  • X-Request-ID: Support correlation ID, not an idempotency key.

Content type: application/json.

Error

HTTP 409

reply_not_ready: A received reply or forward source must be ready; a sent source must have confirmed provider acceptance.

reply_headers_unavailable: The target has no usable RFC Message-ID. Send a new message without in_reply_to.

idempotency_conflict: The key belongs to different input. Recover with the original inbox and payload, not a replacement key.

idempotency_result_unavailable: The key was used but its inbox, draft or sent copy is unavailable. No replacement was created or submitted; do not bypass protection with a new key.

draft_busy: An edit/send raced other changes. Retrieve current content before trying again.

draft_result_unavailable: The draft was already submitted but its sent copy is unavailable. Nothing was resubmitted.

  • X-Request-ID: Support correlation ID, not an idempotency key.

Content type: application/json.

Error

HTTP 413

body_too_large: Reduce the JSON request to the endpoint's body limit.

message_too_large: Reduce message content and attachments. Passing local checks does not guarantee generated MIME fits the provider limit.

  • X-Request-ID: Support correlation ID, not an idempotency key.

Content type: application/json.

Error

HTTP 415

unsupported_media_type: Send Content-Type: application/json.

  • X-Request-ID: Support correlation ID, not an idempotency key.

Content type: application/json.

Error

HTTP 429

outbound_limit_reached: Read quota, reason and sufficient_capacity_at. Waiting cannot fix message_exceeds_allowance; see sending recovery.

  • X-Request-ID: Support correlation ID, not an idempotency key.
  • Retry-After: Delay in seconds when supplied. Quota uses sufficient_capacity_at; absent when waiting cannot make the message fit.

Content type: application/json.

QuotaError

HTTP 503

content_unavailable: Expected stored content is unavailable. Retry the read later.

draft_unavailable: Draft operation is uncertain. Follow draft-specific recovery; do not blindly create a replacement.

  • X-Request-ID: Support correlation ID, not an idempotency key.

Content type: application/json.

Error

Schema: SendReceipt

Inspect message.status even on HTTP 201. accepted is provider acceptance, not delivery. Preserve a known outcome when outcome_persisted is false; later reads may lag. Keyed receipts add replayed and expiry. Draft-association recovery adds replayed without requiring an expiry.

FieldRequiredTypeMeaning and constraints
limitedYesfalse
messageYesSentMetadata
outcome_persistedYesboolean
replayedNoboolean
idempotency_expires_atNostringUTC service instant with milliseconds and Z suffix. format: date-time pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$

Schema: SentMetadata

FieldRequiredTypeMeaning and constraints
idYesstringCherami resource ID, distinct from the RFC Message-ID. Use the returned value.
inbox_idYesstringCherami resource ID, distinct from the RFC Message-ID. Use the returned value.
created_atYesstringUTC service instant with milliseconds and Z suffix. format: date-time pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$
recipient_countYesintegerminimum: 0
statusYes"accepted" or "rejected" or "unknown"
provider_message_idYesstring or null
error_codeYesstring or null
thread_idYesstring or null
in_reply_toYesstring or null
labelsYesarray of string

Schema: Error

FieldRequiredTypeMeaning and constraints
errorYesobject

error fields

FieldRequiredTypeMeaning and constraints
codeYesstringProgrammatic error code. Handle unrecognized codes by status and operation-specific recovery.
messageYesstringHuman-readable context, not a stable string to match.

Schema: QuotaError

FieldRequiredTypeMeaning and constraints
errorYesobject
quotaYesQuota
requested_recipientsYesintegerminimum: 0
reasonYes"temporary_exhaustion" or "message_exceeds_allowance"
sufficient_capacity_atYesstring or null
guidanceYesstring

error fields

FieldRequiredTypeMeaning and constraints
codeYes"outbound_limit_reached"
messageYesstring

Schema: Quota

FieldRequiredTypeMeaning and constraints
allowanceYesintegerminimum: 0
usedYesintegerminimum: 0
remainingYesintegerminimum: 0
next_capacity_atYesstring or null
next_capacity_amountYesintegerminimum: 0
window_hoursYes24
unitYes"recipient_deliveries"
increase_requestYesstring
policy_urlYesstring

HTTP conventions, errors and pagination · Download OpenAPI 3.1

On this page