Messages
Send#
POST /v0/inboxes/:inbox_id/messages/send
| Field | Notes |
|---|---|
to, cc, bcc | A string or an array. At least one recipient, at most 50 unique recipients in total. |
subject, text, html | Send text and html together for best compatibility. |
reply_to | Addresses for the Reply-To header. |
attachments | See attachments. |
headers | Extra headers as an object of strings. |
labels | Extra labels; sent is always added. |
track_opens | Add an open-tracking pixel; needs html. See open tracking. |
The response is { "message_id", "thread_id" }. Add an Idempotency-Key header to make retries safe on send, reply, reply-all, forward and draft send: the same key on the same path replays the stored response (with an Idempotent-Replayed: true header) and the same key on a different path returns 409 idempotency_key_reuse. The request body is not compared, so use a new key for a different message. Keys are kept for 48 hours.
A send can fail with 403 inbox_paused, 403 send_locked, 403 email_unverified, 403 recipient_blocked (block list), 403 message_rejected (the mail provider refused it), 422 recipient_suppressed (earlier bounce or complaint), 422 too_many_recipients, 402 plan_limit_exceeded or 429 rate_limited (including the default 100 sends per inbox per rolling 24 hours). See errors.
Mail is sent from the inbox address on the shared service domain. Sending from your own domain is not connected yet; see domains.
Reply and forward#
curl -X POST "$BASE_URL/v0/inboxes/$INBOX_ID/messages/$MESSAGE_ID/reply" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{"text": "Thanks, got it."}'
Replies set In-Reply-To and References and keep the thread. Without to, the reply goes to the sender (or the Reply-To address). POST .../reply-all or "reply_all": true includes the other recipients. POST .../forward requires to, carries the original attachments, and starts a new thread with a Fwd: subject. The dashboard thread view can reply to the sender or to everyone; forwarding is API and SDK only.
:message_id accepts the internal msg_... id or the RFC 822 Message-ID. Message objects expose the internal id as message_id and the RFC id as rfc_message_id.
Read#
GET /v0/inboxes/:inbox_id/messages/:message_id returns the full message: text, html, extracted_text, extracted_html, headers, attachments, labels, in_reply_to, references. List and search responses omit the bodies and include a preview, so fetch the message to read extracted_text.
List and filter#
GET /v0/inboxes/:inbox_id/messages accepts limit (1 to 100, default 50), page_token, labels (comma separated, all must match), before, after (ISO 8601), ascending, and substring filters from, to and subject. Spam, blocked, unauthenticated and trash messages are hidden unless you ask for that label explicitly.
Search#
GET or POST /v0/inboxes/:inbox_id/messages/search takes q and ranks results by relevance over sender, recipients, subject and body. Each result includes highlights. There is no search box in the dashboard and no search method in the TypeScript SDK; the Python SDK has messages.search and threads.search.
curl -G "$BASE_URL/v0/inboxes/$INBOX_ID/messages/search" \
-H "Authorization: Bearer $API_KEY" --data-urlencode "q=invoice march"
Batch operations#
POST .../messages/batch-get with {"message_ids": [...]} and POST .../messages/batch-update with message_ids, add_labels, remove_labels handle up to 100 ids (more returns 400 validation_error); unknown ids are skipped.
Raw messages#
GET .../messages/:message_id/raw returns download_url, size and expires_at (one hour); the download returns the .eml.
Delete#
DELETE .../messages/:message_id returns 204.
Receiving#
Mail sent to an inbox on the shared service domain arrives as a message with the labels received and unread, joins or starts a thread, and emits message.received. Messages that fail checks get spam, unauthenticated or blocked labels instead (see labels). Mail to a paused inbox is dropped silently.