# Sending and quota

Submission fields, reply targets, sent-message responses, uncertain outcomes, and shared allowances.

Source: https://cherami.to/docs/api/sending



All routes require bearer authentication. Follow [permitted sending](https://cherami.to/docs/guides/safety#permitted-sending).

## Submit a message [#submit-a-message]

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

```json
{
  "to": ["recipient@example.com"],
  "subject": "Notes",
  "text": "Here are the notes.",
  "attachments": [{"filename":"notes.txt","type":"text/plain","content":"SGVsbG8="}]
}
```

### Request fields [#request-fields]

| Field         | Required | Contract                                                            |
| ------------- | -------- | ------------------------------------------------------------------- |
| `to`          | Yes      | Nonempty array of bare ASCII email addresses, not display names.    |
| `subject`     | Yes      | Nonblank string, at most 998 UTF-8 bytes, no control characters.    |
| `text`        | Yes      | Nonblank plain-text string.                                         |
| `cc`, `bcc`   | No       | Arrays of bare ASCII email addresses.                               |
| `html`        | No       | HTML string. Plain-text-only messages stay plain text.              |
| `attachments` | No       | Array of at most 32 files with `filename`, `type`, and `content`.   |
| `in_reply_to` | No       | Cherami received or accepted sent resource ID from this same inbox. |

At most 50 combined To/Cc/Bcc entries are accepted. Unknown top-level and attachment fields are rejected. You cannot override From or supply arbitrary headers, remote attachment URLs, inline attachments, or raw MIME.

Attachment `filename` is a nonempty string of at most 255 UTF-8 bytes, with no control characters, `/`, or `\`. `type` is a MIME type such as `text/plain`, without parameters, at most 127 characters. `content` is padded base64 without whitespace.

JSON is limited to 8 MiB. The total email must fit the provider's 5 MiB limit, including generated MIME and attachments. Local size checks do not guarantee that generated MIME will fit.

Cherami appends “Sent via Cherami” to plain text and supplied HTML, after your body including quoted history. Do not add it yourself. Returned sent bodies include the attribution.

### Reply targets [#reply-targets]

`in_reply_to` is a resource ID, not an RFC Message-ID or thread ID. Received parents must be ready; sent parents must be accepted. Both need a usable Message-ID and must belong to the sending inbox. Cherami sets `In-Reply-To` and accumulated `References`, shortening long ancestry as needed.

Supply recipients and subject explicitly. There is no automatic reply-all or implicit latest-message selection. Unready, unaccepted, or headerless targets return `409`; missing, deleted, other-account, or other-inbox targets return `404`.

### Response and outcomes [#response-and-outcomes]

A created sent resource returns `201` and `Location: /v1/sent/{id}`:

```json
{
  "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": "accepted",
    "provider_message_id": "<outgoing-id@example.com>",
    "error_code": null,
    "thread_id": "44444444-4444-4444-8444-444444444444",
    "in_reply_to": null
  },
  "outcome_persisted": true
}
```

The identifiers are illustrative. Inspect `message.status`, not just HTTP status:

| Status     | Interpretation                                                                                                                |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `accepted` | Provider accepted submission, not proof of inbox delivery. `provider_message_id` is the outgoing RFC Message-ID.              |
| `rejected` | Explicit pre-acceptance rejection. `error_code` describes the provider failure. Quota is released when this outcome is saved. |
| `unknown`  | Acceptance could not be confirmed. The sending charge remains; do not automatically retry.                                    |

`provider_message_id`, `error_code`, `thread_id`, and `in_reply_to` can be null. `in_reply_to` identifies the Cherami parent resource when available.

When `outcome_persisted` is false, the response reports a known provider outcome that could not be saved. Later reads may still say `unknown`; do not resend because of that mismatch. There is no automatic application retry, idempotency-key support, or delivery/bounce tracking.

If a response is lost or an infrastructure error reports uncertainty, inspect sent messages before considering another send. Repeating POST can send a duplicate. An absent match in a short list alone is not proof that retrying is safe.

Provider errors such as `E_RECIPIENT_SUPPRESSED`, `E_RATE_LIMIT_EXCEEDED`, or `E_DAILY_LIMIT_EXCEEDED` are reported as rejected sent outcomes, not necessarily HTTP errors. A suppressed recipient rejects the whole submission. Check the actual response rather than inferring success from `201`.

## List sent messages [#list-sent-messages]

`GET /v1/inboxes/{inbox_id}/sent?limit=20` returns `200` with `{"messages":[...],"next_cursor":null}`.

Each entry contains the fields shown inside `message` above. All statuses are listed, newest-submitted first. Limit is 1–100, default 20. Pass `next_cursor` as a URL-encoded `cursor` parameter on the same inbox's sent URL. Lists do not contain bodies or attachments.

## Read a sent message [#read-a-sent-message]

`GET /v1/sent/{message_id}` returns `200` with those same fields plus `submission`:

* `from`: the sending inbox address; `to`, `cc`, and `bcc`: recipient arrays.
* `subject`, `text`, and optional `html`: the submitted subject and attributed bodies.
* `attachments`: files with `filename`, `type`, base64 `content`, and `disposition: "attachment"`.
* Optional `headers`: `In-Reply-To` and `References` for a reply.

Missing or other-account IDs return `404`; unavailable content can return `503`. An available sent copy is not evidence of delivery; inspect its status.

## Delete a sent copy [#delete-a-sent-copy]

`DELETE /v1/sent/{message_id}` returns `202` with `id` and `status: "deletion_pending"`. Permanently deletes the sent copy and its attachments with no trash or undo. Deletion does not refund sending quota. Repeating DELETE is safe and can return `202` or `404`.

## Read quota [#read-quota]

`GET /v1/outbound/quota` returns `200` with:

| Field              | Meaning                                                                         |
| ------------------ | ------------------------------------------------------------------------------- |
| `allowance`        | Account's allowed recipient-deliveries per rolling window; ordinary default 25. |
| `used`             | Currently charged recipient-deliveries.                                         |
| `remaining`        | Available recipient-deliveries.                                                 |
| `next_capacity_at` | ISO expiry time of the oldest charged submission, or null when none.            |
| `window_hours`     | `24`.                                                                           |
| `unit`             | `"recipient_deliveries"`.                                                       |
| `increase_request` | Human-readable instructions for requesting an increase.                         |

Every To/Cc/Bcc entry costs one, including repeats. All inboxes share this allowance. Accepted and unknown submissions count; rejected submissions do not when the outcome is saved. Later bounces and message or inbox deletion do not refund charges.

Insufficient capacity returns `429` with `error.code: "outbound_limit_reached"` and `quota` containing the fields above. `Retry-After` is supplied when existing charges will expire. That first expiry need not free enough capacity for your whole message. Manual increases are available through [feedback](https://cherami.to/docs/api/feedback) or email to [hello@cherami.to](mailto:hello@cherami.to), not guaranteed.
