---
title: "Send a message"
method: POST
path: "/v1/inboxes/{inbox_id}/sent"
tags: ["sending"]
---

# Send a message

`POST /v1/inboxes/{inbox_id}/sent`

## Request

- Base URL: `https://cherami.to`
- URL: `https://cherami.to/v1/inboxes/{inbox_id}/sent`
- Auth: HTTP bearer

Sends an email from the inbox. Send only [permitted mail](https://cherami.to/docs/guides/safety#permitted-sending): to recipients who asked to hear from this inbox. To save a message for review or to send later, [create a draft](https://cherami.to/docs/api/drafts/create-draft) instead.

### Request fields

Recipients are objects with an `address` and an optional `name`, never strings such as `Alex <alex@example.com>`. Mail goes out from the inbox's address with its [`sender_name`](https://cherami.to/docs/api/inboxes/update-inbox). Your sent copy keeps Bcc recipients.

Cherami adds a "Sent via Cherami" line at the end of the text and HTML bodies; don't add one yourself. Sent copies you read back include it.

### Reply targets

To answer a message while choosing recipients and subject yourself, set `in_reply_to` to its ID: a ready received message or an accepted sent message in the same inbox that has a Message-ID header. It is a Cherami resource ID, not an RFC Message-ID or a thread ID. Cherami sets the reply headers so the email joins that conversation. To have recipients and subject derived for you, use [reply](https://cherami.to/docs/api/sending/reply-message) or [reply-all](https://cherami.to/docs/api/sending/reply-all-message).

### Response and outcomes

`201` means the attempt was recorded, not that the email went out. Check `message.status`:

- `accepted`: the provider took the message. This is not delivery: Cherami doesn't report delivery or bounces.
- `rejected`: the provider refused it before accepting, so nothing was sent and the recipients aren't charged. `error_code` says why: with `E_RECIPIENT_SUPPRESSED`, a suppressed recipient refused the whole message, so send again without that address; with `E_RATE_LIMIT_EXCEEDED` or `E_DAILY_LIMIT_EXCEEDED`, send again later. A new attempt needs a new key.
- `unknown`: acceptance couldn't be confirmed, so the email may have gone out, and the status stays `unknown`. Treat it as possibly sent rather than sending it again: a resend can deliver a duplicate.

Accepted and unknown attempts are charged one unit per recipient.

When `outcome_persisted` is `false`, later reads may still say `unknown`: keep this response as the record of the outcome, and don't resend because a later read disagrees.

### Retry a send with an idempotency key

Include an `idempotency_key`, a value you choose once per email, and save it with the exact request before sending. If the response is lost or you get a `5xx` such as `503 outbound_unavailable`, repeat the exact request with the same key within 24 hours of your first attempt.

A match returns `200` with `replayed: true` and the original `message`; check `message.status` as above. It never sends or charges again. Keys are unique across the account: reusing one for a different request, on any inbox or endpoint, returns `409 idempotency_conflict`.

A request refused before sending (an invalid field, a blocked recipient, no allowance) leaves its key unused, so you can correct it and send with the same key. Use a new key only for a new email, never to get past an uncertain result.

After 24 hours, or if you sent without a key, look for the email in [sent messages](https://cherami.to/docs/api/sending/list-sent-messages) before sending again: an unprotected repeat sends a second email.

## Path parameters

- `inbox_id` string, required

## Request body

- SendInput: At most 50 To, Cc and Bcc recipients in total and 32 attachments. The whole email, counting text, HTML and base64 attachments, must fit in 5 MiB. Cherami sets From and the other headers.
  - `to` Mailbox[], required
    - `address` string, required: Bare address such as alex@example.com: ASCII, at most 254 characters and 64 before the @.
    - `name` string: Optional name; blank means none. At most 256 UTF-8 bytes after trimming, without control characters.
  - `cc` Mailbox[]
    - `address` string, required: Bare address such as alex@example.com: ASCII, at most 254 characters and 64 before the @.
    - `name` string: Optional name; blank means none. At most 256 UTF-8 bytes after trimming, without control characters.
  - `bcc` Mailbox[]
    - `address` string, required: Bare address such as alex@example.com: ASCII, at most 254 characters and 64 before the @.
    - `name` string: Optional name; blank means none. At most 256 UTF-8 bytes after trimming, without control characters.
  - `subject` string, required: No control characters; at most 998 UTF-8 bytes. Must not be blank.
  - `text` string, required: Required nonblank plain text.
  - `html` string: HTML alternative, sent as supplied.
  - `attachments` AttachmentInput[], nullable: Omitted, null or [] means no attachments.
    - `filename` string, required: At most 255 UTF-8 bytes, without control characters, slashes or backslashes.
    - `type` string, required: MIME type without parameters.
    - `content` string, byte, required: The file's bytes as padded base64, without line breaks.
  - `in_reply_to` string: ID of a ready received message or an accepted sent message in this inbox that has a Message-ID header.
  - `labels` Label[]: Label names, case-sensitive; order and duplicates don't matter.
  - `idempotency_key` string: Your unique value for this one operation. Save it with the exact request before sending; repeating that request with the same key within 24 hours returns what the first one did instead of acting again.

## Response `200`

A retry matched an earlier request: this is that request's result as it is now. Nothing new was created or sent.

- object: accepted in message.status means the provider took the message, not that it was delivered.
  - `limited` unknown, required
  - `message` SentMetadata, required
    - `id` string, required: Cherami resource ID.
    - `inbox_id` string, required: Cherami resource ID.
    - `created_at` string, date-time, required: UTC timestamp with milliseconds.
    - `recipient_count` integer, required
    - `status` 'accepted' | 'rejected' | 'unknown', required
    - `provider_message_id` string, nullable, required: Message-ID the provider assigned when it accepted the message.
    - `error_code` string, nullable, required: Reason code for a rejected or unknown outcome, such as E_RECIPIENT_SUPPRESSED or acceptance_unknown.
    - `thread_id` string, nullable, required: Cherami resource ID.
    - `in_reply_to` string, nullable, required: Cherami resource ID.
    - `labels` string[], required
  - `outcome_persisted` boolean, required: false: this response is the only record of the outcome; keep it rather than resending.
  - `replayed` true, required: true when a retry matched an earlier request: the result is that request's, and nothing new was created or sent.
  - `idempotency_expires_at` string, date-time, required: When the key stops protecting retries.

## Other responses

- `201`: Attempt recorded; check message.status.
- `400`: `invalid_json`: Send a valid UTF-8 JSON object, not an array or scalar. `invalid_message`: Correct the recipients, subject, body, `in_reply_to`, attachments or reply and forward fields as the message describes. `invalid_name`: Use plain text without control characters, at most 256 UTF-8 bytes after trimming. The message names the field. `invalid_labels`: Correct the label name, label change, filter group or discovery prefix the message names. `invalid_idempotency_key`: Use 1–128 ASCII letters, digits, hyphens or underscores.
- `401`: `unauthorized`: Send a valid API key as `Authorization: Bearer <key>`. If the key stopped working, get a new one through [recovery](https://cherami.to/docs/guides/recovery).
- `403`: `operation_not_allowed`: Sending, or deletion and restore, is turned off for this account; the message says which. Contact hello@cherami.to. `recipient_not_allowed`: Nothing was sent: the inbox's [sending rules](https://cherami.to/account/sending-rules) block one or more recipients, which the message lists. Use allowed recipients, or ask the account owner to allow them; do not send through another inbox to get around the rules.
- `404`: `not_found`: The resource does not exist or does not belong to this account. A reply or forward source must also be in the sending inbox.
- `409`: `reply_not_ready`: The source cannot be replied to or forwarded yet. A received message must have `processing_status` `ready`; a sent message must have `status` `accepted`. `reply_headers_unavailable`: The target has no usable Message-ID to reply to. Send a new message without `in_reply_to`. `idempotency_conflict`: The key was first used with different input. To recover that request, repeat it with its original inbox and payload; a different request needs its own key. `idempotency_result_unavailable`: The key already created an inbox, draft or send whose result has since been deleted. Nothing new was created or sent; do not repeat the request under a new key. If a sent message is still in Trash, restore it to read the outcome.
- `413`: `body_too_large`: Reduce the JSON request to the operation's body limit. `message_too_large`: The message exceeds 5 MiB counting text, HTML and base64 attachment content, or a forwarded original has more than 32 attachments. Reduce the content or attachments; for a forward, set `include_attachments` to false or attach selected files to a new message.
- `415`: `unsupported_media_type`: Send `Content-Type: application/json`.
- `429`: `outbound_limit_reached`: The rolling 24-hour sending allowance cannot cover this message, at one charge per recipient. With `reason` `temporary_exhaustion`, retry at `sufficient_capacity_at`; with `message_exceeds_allowance`, the message needs more than the whole allowance: reduce its recipients or [request a higher allowance](https://cherami.to/docs/guides/support). See [sending allowance](https://cherami.to/docs/api/sending/get-outbound-quota).
- `503`: `content_unavailable`: Stored content could not be read. Retry later. `outbound_unavailable`: The request failed. If it was a send, reply or forward, its outcome is unknown: repeat it with the same idempotency key and unchanged payload within 24 hours of the first request; without a key, check sent messages before sending again. Retry a read.

---

[API](https://skmtc.dev/cherami/apis/cherami-http-api.md) · [All operations](https://skmtc.dev/cherami/apis/cherami-http-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cherami/apis/cherami-http-api/revisions/7bd993d538af?raw)
