Forward a message
POST /v1/inboxes/{inbox_id}/forward
Read as Markdown ↗POST /v1/inboxes/{inbox_id}/forward
Requires a Claim-issued API key: Authorization: Bearer YOUR_CREDENTIAL.
Creates an ordinary sent message with the send outcomes and recovery contract. 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
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 apply unchanged.
Parameters
| Parameter | Location | Required | Type | Meaning |
|---|---|---|---|---|
inbox_id | path | Yes | string | Owned Cherami resource ID returned by the API. |
Request body
JSON object. Total UTF-8 request body limit: 8388608 bytes.
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 | maxItems: 50 minItems: 1 |
cc | No | array of Mailbox | maxItems: 50 |
bcc | No | array of Mailbox | maxItems: 50 |
note | No | string | Optional plain-text introduction. |
include_attachments | No | boolean | default: true |
labels | No | array of 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
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.
{
"message_id": "22222222-2222-4222-8222-222222222222",
"to": [
{
"address": "recipient@example.com"
}
],
"note": "For your review.",
"idempotency_key": "RETAIN_A_UNIQUE_SEND_KEY"
}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.jsonResponses
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 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$ |
{
"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
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 2
| Field | Required | Type | Meaning and constraints |
|---|---|---|---|
replayed | No | false |
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.
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.
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.
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.
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.
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.
HTTP 415
unsupported_media_type: Send Content-Type: application/json.
X-Request-ID: Support correlation ID, not an idempotency key.
Content type: application/json.
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.
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.
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 | |
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
| 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
| Field | Required | Type | Meaning and constraints |
|---|---|---|---|
error | Yes | object |
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
| Field | Required | Type | Meaning and constraints |
|---|---|---|---|
error | Yes | object | |
quota | Yes | 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
| Field | Required | Type | Meaning and constraints |
|---|---|---|---|
code | Yes | "outbound_limit_reached" | |
message | Yes | string |
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
| 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
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 · Download OpenAPI 3.1