# Forward a message

POST /v1/inboxes/{inbox_id}/forward

Source: https://cherami.to/docs/api/sending/forward-message



{/* Generated from apps/web/openapi. Edit the contract, not this file. */}

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

Requires a [Claim-issued API key](https://cherami.to/docs/api#authentication): `Authorization: Bearer YOUR_CREDENTIAL`.

Creates an ordinary sent message with the [send outcomes and recovery contract](https://cherami.to/docs/api/sending/send-message). HTTP success can report rejected or unknown; accepted means provider acceptance, not delivery.

`message_id` selects a ready received or accepted sent message in the sending inbox. Missing, deleted, other-inbox and other-account sources return `404`; unready or unaccepted sources return `409 reply_not_ready`. A forward does not require an RFC Message-ID.

Supply `message_id` and nonempty `to`. Optional fields are `cc`, `bcc`, plain-text `note`, boolean `include_attachments` (default `true`), `labels`, and `idempotency_key`. Recipient, label and size limits are the same as explicit send. Forward recipients are explicit and are not automatically deduplicated.

The subject receives `Fwd: ` unless it already starts with `Fw:` or `Fwd:`. The note precedes a forwarded header block containing From, available Date, Subject, To and Cc, never Bcc. Original text and HTML are retained, including quoted history and earlier attribution. HTML-only originals get a non-rendered plain-text alternative. A forward has no reply parent or inherited reply headers and starts a new Cherami conversation.

Attachments are included by default with their original bytes; embedded images retain their Content-ID relationships. Unsafe or missing filenames get safe transport names. Unusable MIME types become `application/octet-stream`. Setting `include_attachments: false` excludes all original files, including embedded images, so images referenced by the HTML may be unavailable. No attachment is silently removed to fit a limit. Missing expected content returns `503 content_unavailable`; oversized forwards return `413 message_too_large`, or a provider rejection if generated MIME exceeds its limit. An unusable original inline Content-ID returns `400 invalid_message`. Retrieve the original later for unavailable content; explicitly exclude attachments or use an explicit send for a deliberately reduced message.

Excluding Bcc from generated headers does not redact anything already written in the original body. Review the original content before authorizing disclosure.

### Recover a helper send [#recover-a-helper-send]

Save the operation, sending inbox, exact helper payload, key and first request time. Helpers share the account's sending-key namespace: changing between reply, reply-all, forward or explicit send conflicts. Omitted `include_attachments` and `true` are equivalent; omitted and empty notes are equivalent. Derived recipients, bodies and the current sender name do not change retry intent.

Within the 24-hour protection window, a matching retry recovers the reserved attempt before reading the source, even if the source or its files have since been deleted. It never derives a replacement message or submits again. Deleting the resulting sent copy instead returns `409 idempotency_result_unavailable`. Current permissions and sending-inbox ownership still apply. The [ordinary uncertainty and expiry rules](https://cherami.to/docs/api/sending/send-message#retry-a-send-with-an-idempotency-key) apply unchanged.

## Parameters [#parameters]

| Parameter  | Location | Required | Type   | Meaning                                        |
| ---------- | -------- | -------- | ------ | ---------------------------------------------- |
| `inbox_id` | path     | Yes      | string | Owned Cherami resource ID returned by the API. |

## Request body [#request-body]

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

### Schema: ForwardInput [#schema-forwardinput]

| Field                 | Required | Type                                | Meaning and constraints                                                                                                                                                              |
| --------------------- | -------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `message_id`          | Yes      | string                              | Received or accepted sent resource ID in this inbox. The source must have usable reply headers. `pattern`: `^[a-f0-9-]{36}$`                                                         |
| `to`                  | Yes      | array of [Mailbox](#schema-mailbox) | `maxItems`: `50` `minItems`: `1`                                                                                                                                                     |
| `cc`                  | No       | array of [Mailbox](#schema-mailbox) | `maxItems`: `50`                                                                                                                                                                     |
| `bcc`                 | No       | array of [Mailbox](#schema-mailbox) | `maxItems`: `50`                                                                                                                                                                     |
| `note`                | No       | string                              | Optional plain-text introduction.                                                                                                                                                    |
| `include_attachments` | No       | boolean                             | `default`: `true`                                                                                                                                                                    |
| `labels`              | No       | array of [Label](#schema-label)     | Trimmed, deduplicated and case-sensitive. Order is not significant. `maxItems`: `32`                                                                                                 |
| `idempotency_key`     | No       | string                              | Retain 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 [#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](https://cherami.to/docs/api/errors#retry-by-operation).

```json
{
  "message_id": "22222222-2222-4222-8222-222222222222",
  "to": [
    {
      "address": "recipient@example.com"
    }
  ],
  "note": "For your review.",
  "idempotency_key": "RETAIN_A_UNIQUE_SEND_KEY"
}
```

```sh
curl --silent --show-error --include --request POST \
  "https://cherami.to/v1/inboxes/INBOX_ID/forward" \
  --header "Authorization: Bearer $CHERAMI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json
```

## Responses [#responses]

### HTTP 200 [#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 [#part-1]

[SendReceipt](#schema-sendreceipt)

### Part 2 [#part-2]

| Field                    | Required | Type   | Meaning and constraints                                                                                                              |
| ------------------------ | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `replayed`               | Yes      | `true` |                                                                                                                                      |
| `idempotency_expires_at` | Yes      | string | UTC 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$` |

```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": "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,
  "idempotency_expires_at": "2026-10-02T00:00:00.000Z"
}
```

### HTTP 201 [#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 [#part-1-1]

[SendReceipt](#schema-sendreceipt)

### Part 2 [#part-2-1]

| Field      | Required | Type    | Meaning and constraints |
| ---------- | -------- | ------- | ----------------------- |
| `replayed` | No       | `false` |                         |

### HTTP 400 [#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.

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

Content type: `application/json`.

[Error](#schema-error)

### HTTP 401 [#http-401]

`unauthorized`: Provide a valid bearer credential. Use [human-approved recovery](https://cherami.to/docs/guides/recovery) if access is lost.

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

Content type: `application/json`.

[Error](#schema-error)

### HTTP 403 [#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](https://cherami.to/account/sending-rules); do not bypass them through another inbox.

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

Content type: `application/json`.

[Error](#schema-error)

### HTTP 404 [#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](#schema-error)

### HTTP 409 [#http-409]

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

`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.

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

Content type: `application/json`.

[Error](#schema-error)

### HTTP 413 [#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](#schema-error)

### HTTP 415 [#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](#schema-error)

### HTTP 429 [#http-429]

`outbound_limit_reached`: Read `quota`, `reason` and `sufficient_capacity_at`. Waiting cannot fix `message_exceeds_allowance`; see [sending recovery](https://cherami.to/docs/api/sending/get-outbound-quota).

* `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](#schema-quotaerror)

### HTTP 503 [#http-503]

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

`outbound_unavailable`: Outbound operation failed and a send's outcome may be unknown. Recover using the original key and unchanged payload within its window, or inspect sent messages.

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

Content type: `application/json`.

[Error](#schema-error)

## Schema: SendReceipt [#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.

| Field                    | Required | Type                                 | Meaning and constraints                                                                                                              |
| ------------------------ | -------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `limited`                | Yes      | `false`                              |                                                                                                                                      |
| `message`                | Yes      | [SentMetadata](#schema-sentmetadata) |                                                                                                                                      |
| `outcome_persisted`      | Yes      | boolean                              |                                                                                                                                      |
| `replayed`               | No       | boolean                              |                                                                                                                                      |
| `idempotency_expires_at` | No       | string                               | UTC 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 [#schema-sentmetadata]

| Field                 | Required | Type                                        | Meaning and constraints                                                                                                              |
| --------------------- | -------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                  | Yes      | string                                      | Cherami resource ID, distinct from the RFC Message-ID. Use the returned value.                                                       |
| `inbox_id`            | Yes      | string                                      | Cherami resource ID, distinct from the RFC Message-ID. Use the returned value.                                                       |
| `created_at`          | Yes      | string                                      | UTC 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_count`     | Yes      | integer                                     | `minimum`: `0`                                                                                                                       |
| `status`              | Yes      | `"accepted"` or `"rejected"` or `"unknown"` |                                                                                                                                      |
| `provider_message_id` | Yes      | string or null                              |                                                                                                                                      |
| `error_code`          | Yes      | string or null                              |                                                                                                                                      |
| `thread_id`           | Yes      | string or null                              |                                                                                                                                      |
| `in_reply_to`         | Yes      | string or null                              |                                                                                                                                      |
| `labels`              | Yes      | array of string                             |                                                                                                                                      |

## Schema: Error [#schema-error]

| Field   | Required | Type   | Meaning and constraints |
| ------- | -------- | ------ | ----------------------- |
| `error` | Yes      | object |                         |

### `error` fields [#error-fields]

| Field     | Required | Type   | Meaning and constraints                                                                       |
| --------- | -------- | ------ | --------------------------------------------------------------------------------------------- |
| `code`    | Yes      | string | Programmatic error code. Handle unrecognized codes by status and operation-specific recovery. |
| `message` | Yes      | string | Human-readable context, not a stable string to match.                                         |

## Schema: QuotaError [#schema-quotaerror]

| Field                    | Required | Type                                                      | Meaning and constraints |
| ------------------------ | -------- | --------------------------------------------------------- | ----------------------- |
| `error`                  | Yes      | object                                                    |                         |
| `quota`                  | Yes      | [Quota](#schema-quota)                                    |                         |
| `requested_recipients`   | Yes      | integer                                                   | `minimum`: `0`          |
| `reason`                 | Yes      | `"temporary_exhaustion"` or `"message_exceeds_allowance"` |                         |
| `sufficient_capacity_at` | Yes      | string or null                                            |                         |
| `guidance`               | Yes      | string                                                    |                         |

### `error` fields [#error-fields-1]

| Field     | Required | Type                       | Meaning and constraints |
| --------- | -------- | -------------------------- | ----------------------- |
| `code`    | Yes      | `"outbound_limit_reached"` |                         |
| `message` | Yes      | string                     |                         |

## Schema: Quota [#schema-quota]

| Field                  | Required | Type                     | Meaning and constraints |
| ---------------------- | -------- | ------------------------ | ----------------------- |
| `allowance`            | Yes      | integer                  | `minimum`: `0`          |
| `used`                 | Yes      | integer                  | `minimum`: `0`          |
| `remaining`            | Yes      | integer                  | `minimum`: `0`          |
| `next_capacity_at`     | Yes      | string or null           |                         |
| `next_capacity_amount` | Yes      | integer                  | `minimum`: `0`          |
| `window_hours`         | Yes      | `24`                     |                         |
| `unit`                 | Yes      | `"recipient_deliveries"` |                         |
| `increase_request`     | Yes      | string                   |                         |
| `policy_url`           | Yes      | string                   |                         |

## Schema: Mailbox [#schema-mailbox]

| Field     | Required | Type   | Meaning and constraints                                                                                                              |
| --------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `address` | Yes      | string | Bare ASCII address, at most 254 characters, local part at most 64. No display-name header syntax. `maxLength`: `254`                 |
| `name`    | No       | string | Unicode name, trimmed; blank means unnamed. No control characters. At most 256 UTF-8 bytes after trimming. `x-max-utf8-bytes`: `256` |

Unknown fields are rejected.

## Schema: Label [#schema-label]

1–128 UTF-8 bytes after trimming. Well-formed Unicode without control characters; case-sensitive.

string. `x-max-utf8-bytes`: `128`

[HTTP conventions, errors and pagination](https://cherami.to/docs/api/errors) · [Download OpenAPI 3.1](https://cherami.to/openapi.json)
