Send, reply and forward
Send a message, reply to or forward mail, and read the result.
Read as Markdown ↗Send from an inbox with one request, then check the outcome. Write only to people who asked to hear from it: see permitted sending.
Send a message
Set INBOX_ID and CHERAMI_API_KEY, save the request, then send it:
jq -n --arg key "$(uuidgen)" \
'{to: [{address: "recipient@example.com", name: "Alex"}],
subject: "Review ready", text: "The change is ready for review.",
idempotency_key: $key}' > send.json
curl --fail-with-body --silent --show-error \
"https://cherami.to/v1/inboxes/$INBOX_ID/sent" \
-H "Authorization: Bearer $CHERAMI_API_KEY" \
-H 'Content-Type: application/json' --data-binary @send.jsonWith MCP, call send_message with the same fields plus inbox_id.
Mail goes out from the inbox's address, under its sender name if it has one. Each to, cc and bcc recipient is an object with an address and an optional name. text is required; html and attachments are optional. We add a “Sent via Cherami” line to every message, replies included, so don't add your own.
To have a message reviewed before it goes out, save it as a draft instead.
Keep a key for safe retries
Give each email its own idempotency_key, such as a fresh UUID, and save the request before you send it. If you don't get a response, send the identical request again within 24 hours, key included. We return the original attempt instead of sending it twice or counting it against your allowance twice. Full retry rules.
The TypeScript and Python SDKs build this saved request, key included, with prepareSend and prepare_send, for replies and forwards too: save what it returns, then submit it.
Inspect the outcome
Save message.id and check message.status:
status | What it means |
|---|---|
accepted | Our email provider accepted it for delivery. We can't see whether it reached the recipient. |
rejected | The provider refused it. error_code says why: fix that, then send it again with a new idempotency_key. |
unknown | We couldn't confirm whether it went out, so it may have. The status stays unknown, and repeating the request with the same key returns this same attempt. Don't send it again automatically: a new key or a changed request can deliver a duplicate. |
Reply to a message
With MCP, reply with reply_message, or reply_all_message to include everyone visible on the message, giving inbox_id, message_id, your text and an idempotency_key. We fill in the recipients, subject and threading headers, and send only your text, without quoted history or the original attachments.
With HTTP, set MESSAGE_ID, INBOX_ID and CHERAMI_API_KEY first:
jq -n --arg message "$MESSAGE_ID" --arg key "$(uuidgen)" \
'{message_id: $message, text: "I checked the change. The notes cover the remaining limitation.",
idempotency_key: $key}' > reply.json
curl --fail-with-body --silent --show-error \
"https://cherami.to/v1/inboxes/$INBOX_ID/reply" \
-H "Authorization: Bearer $CHERAMI_API_KEY" \
-H 'Content-Type: application/json' --data-binary @reply.jsonFor reply-all, send the same body to /reply-all.
With the TypeScript SDK, prepare the reply, save it, then submit it; replyAllMessage takes the same shape:
import { writeFile } from "node:fs/promises";
import { prepareSend } from "@cherami/sdk";
const intent = prepareSend("replyMessage", {
inbox_id: inboxId,
body: {
message_id: messageId,
text: "I checked the change. The notes cover the remaining limitation.",
},
});
await writeFile(intentPath, JSON.stringify(intent));
const { data: receipt } = await client.submit(intent);With Python, reply_all_message takes the same shape:
from cherami import prepare_send
intent = prepare_send("reply_message", {
"inbox_id": inbox_id,
"body": {
"message_id": message_id,
"text": "I checked the change. The notes cover the remaining limitation.",
},
})
intent_path.write_text(intent.to_json(), encoding="utf-8")
receipt = client.submit(intent).dataA reply goes to the message's Reply-To address, or From if it has none; a reply to mail you sent goes to its original To recipients. Neither reply nor reply-all copies the original Bcc, and if your inbox was Bcc'd, reply-all shows everyone that you received it. You can reply to a received message once it's ready, or to mail you sent once it's accepted, from the inbox that holds it. To choose the recipients or subject yourself, send a message with in_reply_to set to the message's ID.
Forward a message
With MCP, call forward_message; with HTTP, POST /v1/inboxes/INBOX_ID/forward. Give the message_id, the to recipients, an idempotency_key and, if you like, a plain-text note:
jq -n --arg message "$MESSAGE_ID" --arg key "$(uuidgen)" \
'{message_id: $message, to: [{address: "recipient@example.com", name: "Alex"}],
note: "Here is the original proposal for your review.", idempotency_key: $key}' > forward.json
curl --fail-with-body --silent --show-error \
"https://cherami.to/v1/inboxes/$INBOX_ID/forward" \
-H "Authorization: Bearer $CHERAMI_API_KEY" \
-H 'Content-Type: application/json' --data-binary @forward.jsonA forward carries the original message in full, its attachments and embedded images included, and starts a new conversation. To leave the files out, or if the forward is refused with 413 message_too_large, set include_attachments: false.
If a send is refused
If the inbox has sending rules, every To, Cc and Bcc address must be allowed, including those a reply adds. A blocked address returns 403 recipient_not_allowed naming it: drop it, or have the account owner allow it in Account → Sending rules.
Check the shared allowance
All inboxes in your account share one sending allowance. Each To, Cc and Bcc entry counts as one recipient for 24 hours, even when an address appears twice. Accepted and unknown sends count; rejected ones don't. See what's left with GET /v1/outbound/quota, get_outbound_quota or on the Account home.
A send that doesn't fit returns 429 outbound_limit_reached. If it will fit once the allowance refills, sufficient_capacity_at and Retry-After say when. If reason is message_exceeds_allowance, the message has more recipients than your whole allowance, so waiting won't help: send it to fewer recipients or ask us for more. Meanwhile, you can save it as a draft.