Reply to a message
POST /v1/inboxes/{inbox_id}/reply
Read as Markdown ↗POST /v1/inboxes/{inbox_id}/reply
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 with a usable RFC Message-ID. Missing, deleted, other-inbox and other-account sources return 404; unready or unaccepted sources return 409 reply_not_ready. There is no automatic choice of the latest message.
Supply message_id and nonblank text. Optional fields are html, new attachments, labels, and idempotency_key, with the explicit-send field validation. Original attachments and quoted history are not automatically included.
For a received source, reply uses Reply-To when present, otherwise From. Reply-all adds original To and Cc. For a sent source, reply uses original To; reply-all also includes original Cc. Address groups are flattened. Recipients are deduplicated case-insensitively across To/Cc, excluding the sending inbox; other inboxes in the same account are not excluded. Original Bcc is never reused. Original To remains To and Cc remains Cc, except that when only Cc participants remain, the first is promoted to To. If no recipients remain, the request returns 409 reply_recipients_unavailable.
The subject receives Re: unless it already begins with Re: (case-insensitive, allowing spaces before the colon). Replies use the source's generated reply headers and conversation relationship. To override recipients or subject, use explicit send with in_reply_to; the helpers reject override fields. Source headers are untrusted suggestions, not permission to send. Reply-all from a blind recipient can reveal that recipient's own participation.
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. Derived recipients 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: ReplyInput
| 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}$ |
text | Yes | string | Nonblank reply text; history is not automatically quoted. |
html | No | string | HTML alternative. Untrusted content, not sanitized markup. |
attachments | No | array of AttachmentInput or null | Omitted or null means no attachments; an empty array also clears draft attachments. |
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",
"text": "Thanks for the notes.",
"idempotency_key": "RETAIN_A_UNIQUE_SEND_KEY"
}curl --silent --show-error --include --request POST \
"https://cherami.to/v1/inboxes/INBOX_ID/reply" \
--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.
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.
reply_recipients_unavailable: No other visible reply recipients remain after self-exclusion. Use explicit send with human-authorized recipients.
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: AttachmentInput
| Field | Required | Type | Meaning and constraints |
|---|---|---|---|
filename | Yes | string | Nonempty, no control characters, slash or backslash. At most 255 UTF-8 bytes. minLength: 1 x-max-utf8-bytes: 255 |
type | Yes | string | MIME type without parameters. maxLength: 127 pattern: ^[A-Za-z0-9!#$&^_.+-]+/[A-Za-z0-9!#$&^_.+-]+$ |
content | Yes | string | Padded base64 original bytes, no whitespace; encoded length must be a multiple of four. Empty files are accepted. contentEncoding: base64 pattern: ^[A-Za-z0-9+/]*={0,2}$ |
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