Skip to main content
Messages represent individual emails in a mailbox. List and detail endpoints return message metadata; full body content (text/HTML) is fetched from storage on detail requests. All list endpoints use cursor-based pagination. POST /messages sends immediately. To save incomplete content, autosave changes, or review attachments before sending, use Drafts. Drafts do not appear in message, thread, or search results until sent.

List messages

List messages in a mailbox, newest first, with cursor pagination.

Path parameters

Query parameters

start_datetime, end_datetime, and tz filter on created_at. Both bounds are optional and non-breaking — omit them for unchanged behavior. See Filtering by date.

Response (200)

JSON

Code examples


Get message

Get a single message with its full body content (plain text and HTML). The body returned is the body Inkbox stored, which is not always the body the caller submitted: outbound body_text/body_html include any automatically applied custom signature and, while it applies, the “Sent via Inkbox” footer — see Branding footer under Send message.
Fetching marks inbound mail read. A GET here with an API key marks an unread inbound message read — is_read comes back true in the same response. Human (Console) reads don’t, and neither do the list, thread, or attachment routes, so an agent that only reads through those still needs the flags PATCH to mark read. This is distinct from first_opened_at, which reflects the recipient opening a message you sent (see Open tracking).

Path parameters

Response (200)

JSON

Code examples


Send message

Compose and send an email from a mailbox. Threading is automatically resolved from in_reply_to_message_id.
Storage limitsEvery inbox has a storage cap set by your plan. A send that would push the inbox above its cap is refused with a 402 — from this endpoint, reply-all, and forward alike — until space is freed (delete messages or threads) or the plan is upgraded. See pricing for per-plan caps.Receiving is never blocked. Inbound mail is always accepted, and still counts toward the inbox’s usage. Current usage and the cap are on the mailbox object as storage_used_bytes and storage_limit_bytes.

Path parameters

Request body

Provide at least one address across recipients.to, recipients.cc, and recipients.bcc. A message can have at most 50 distinct recipients across these lists; repeated addresses count once, ignoring case. The complete MIME message, including headers, body, attachments, and encoding overhead, must not exceed 10 MiB (10,485,760 bytes). Base64 encoding increases attachment size, so an attachment below the 25 MiB decoded-attachment cap can still exceed the message limit. AttachmentUpload object:

Request example

JSON

Response (201)

Returns the created message metadata (same as a list item — body content is not included).

Custom signatures

When enabled and available for the sending mailbox, a custom signature is added to sends, replies, reply-all, and forwards. Configure it through Update mailbox, not in each message request. The stored message includes the applied signature. An HTML signature adds a formatted HTML version to a text-only message while retaining the plain-text fallback. This does not enable open tracking. A text-only signature does not create an HTML version; an existing HTML body keeps its formatting. Original messages forwarded as attachments are unchanged. If the Inkbox branding footer also applies, it appears after the custom signature.

Idempotency

Send, reply-all, and forward accept an optional Idempotency-Key header of up to 255 characters. Omit it and every call is an independent send. Supply it and the key is bound to the email before it is handed off for delivery, so retrying after a lost or timed-out response cannot put a second copy of the email on the wire. This is at-most-once protection, not a replay. A repeat under a key that already sent returns an error rather than the original 201: A 409 means the email went out. Fetch it with GET /messages?direction=outbound. A 503 means Inkbox cannot confirm what happened to the earlier attempt, so it will not send again under that key; check your outbound messages to see whether one exists. A 503 keeps returning while the outcome stays unresolved, which is deliberate — resolve it by looking the message up, not by forcing a send.
  • Keys are scoped to your organization and to the endpoint. One key used for a send and for a forward does not collide.
  • A retry does not spend quota twice. It reuses the original attempt’s recipient allowance rather than consuming it again.
  • A key is remembered for 7 days, after which it can be used again.
  • The request body is not part of the key. Reusing a key with different content is refused as an already-accepted send, not delivered. Use a fresh key for each distinct email.
  • Retry with the same key. Never mint a new one for the same email. A new key is a new send, so if the first attempt did reach delivery, retrying under a fresh key is what puts the second copy on the wire. Errors raised before the send is accepted — validation, policy, storage and rate limits — leave the key unused, so retrying with it behaves like a first attempt.
For send-then-confirm with a response you can safely replay, use drafts instead: create the draft with Idempotency-Key, then send it. A lost response there can be retried and returns the original message. By default, outbound mail carries a short “Sent via Inkbox” footer, appended to the text and HTML parts of every send, reply-all, and forward. Organizations on a paid plan can remove it — see Branding footer. While it applies, the footer is written into the stored message, so it changes what the API gives back: a later GET /messages/{message_id} returns body_text and body_html with the footer appended — not the exact bytes you submitted. A send with no body at all comes back with a body containing only the footer. Snippets and search text are computed from the body you submitted, so snippet and search are unaffected.

Open tracking

Set track_opens on a send or forward to embed an invisible tracking pixel in the HTML body. When the recipient’s mail client loads it, the message reports two fields (present on every message response): track_opens requires an HTML part — a plain-text-only send returns 422. Tracking is per-message (“at least one observed open”), not per-recipient, and the pixel can raise spam scores, so a borderline message may be more likely to be filtered. first_opened_at (the recipient loaded the pixel) is distinct from is_read (your agent consumed the message via the API — see Get message).

Error responses

Mail limits are shared by every mailbox in the organization and count distinct envelope recipients, not messages. The default fixed-window ceilings are 50 recipients per 10 minutes, 80 per hour, and 100 per 24 hours; plan limits may be higher. An address repeated across To, Cc, or Bcc counts once. A retryable limit response uses structured detail:
If one request itself exceeds the effective ceiling, waiting cannot help. The API returns 403 with detail.error set to mail_rate_limit_exceeded, along with limit, window_seconds, and recipient_count, and omits Retry-After. Other send controls use distinct detail.error values: Use the error code rather than the status alone to choose remediation. A 429 with Retry-After is retryable after the stated delay; a 403 requires a change to the request, recipient relationship, sending mailbox, or plan.

Code examples


Reply to everyone

Reply to every visible participant on a stored message. Recipients are resolved server-side from the original — you don’t pass them. The reply stays in the original thread and counts against the same send rate limits as POST /messages.

Path parameters

Request body

Recipient resolution

  • Inbound originalTo is the original Reply-To if present, otherwise the original sender. Cc is the original To + Cc.
  • Outbound originalTo and Cc reuse the original To and Cc.
  • In both cases the replying mailbox, duplicates, and the original BCC recipients are removed.
The message detail (GET .../messages/{message_id}) returns the resolved reply_all_recipients ({ to, cc }) so UIs can prefill editable fields.

Request example

JSON

Response (201)

Returns a MessageResponse — the brand-new message representing the reply (same shape as a list item; body content is not included). Accepts an optional Idempotency-Key header, under its own key namespace — see Idempotency.

Error responses

Code examples


Forward message

Forward a stored message from this mailbox. Each forward creates a new thread. Forwards count against the same send rate limits as POST /messages and fire a message.forwarded webhook event on success (not message.sent).

Path parameters

Request body

At least one address is required across recipients.to, recipients.cc, and recipients.bcc. The combined limit is 50 distinct recipients, ignoring case.

Modes

  • inline (default): Renders the original body below a preamble containing its From:, Date:, Subject:, To:, and Cc: headers. Original attachments appear as direct attachments to the forward. Inline cid: images may break, and complex multipart structure is flattened.
  • wrapped: Attaches the original message as a single message/rfc822 part, preserving its headers, body parts, attachments, and nested structure. Recipients may see it inline or as a forwarded.eml attachment, depending on their mail client. Use this for originals with inline images, calendar invites, or complex multipart content. The result preserves the message’s meaning and structure but may change its byte representation. DKIM-preserving forwarding is not supported.

Forwarding behaviour

  • Original-attachment bytes are excluded from the 25 MiB cap on additional attachments. The complete MIME message, including the original content and encoding overhead, must fit within 10 MiB.
  • The forward gets a brand-new Message-ID. The original is referenced via an X-Forwarded-Message-Id header.
  • Filename collisions across original attachments and caller additional_attachments are deduplicated automatically (caller wins; originals get a forwarded- prefix and a numeric suffix).

Request example

JSON

Response (201)

Returns a MessageResponse — the brand-new message representing the forward (same shape as a list item; body content is not included). Accepts an optional Idempotency-Key header, under its own key namespace — see Idempotency.

Error responses

Code examples


Update message flags

Update is_read or is_starred on a message. Omit a field to leave it unchanged. This is how to mark mail read in bulk, to mark a message unread, or to mark read when you process from the list or thread routes (which never mark read on their own). Fetching a single inbound message with an API key marks it read automatically — see Get message.

Path parameters

Request body

Code examples


Delete message

Delete a message. Returns 204 No Content on success. Deleting a message frees its storage immediately — the message and its associated storage overhead stop counting toward the inbox’s storage cap right away.

Path parameters

Code examples


Download attachment

Download an attachment from a message. By default, responds with a 302 redirect to a temporary download URL. Set redirect=false to get the URL as JSON instead.

Path parameters

Query parameters

Response (302) — redirect mode (default)

Redirects to a temporary download URL with a Content-Disposition: attachment header. The URL is valid for 15 minutes.

Response (200) — JSON mode

JSON

Error responses

Code examples


Message object

MessageResponse (list items)

MessageDetailResponse (single message)

Includes all MessageResponse fields plus: