---
title: "POST /email/send"
method: POST
path: "/email/send"
---

# POST /email/send

`POST /email/send`

## Request

- Base URL: `https://api.useanima.sh/v1`
- URL: `https://api.useanima.sh/v1/email/send`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

## Request body

- object: Request body for sending an email with threading support
  - `agentId` string, required: ID of the agent sending the email
  - `fromIdentityId` string: Optional EmailIdentity ID to send from. An agent has exactly one email identity, so the only valid value is that one — supply it to assert which address you expect to send as, or omit it and the agent's identity is used. Discover the ID via GET /agents/{agentId}/email-identities.
  - `to` string[], required: List of recipient email addresses (at least one required)
  - `cc` string[]: List of CC recipient email addresses
  - `bcc` string[]: List of BCC recipient email addresses
  - `subject` string, required: Email subject line (max 998 characters per RFC 2822)
  - `body` string, required: Plain-text body of the email
  - `bodyHtml` string: Optional HTML body of the email
  - `attachments` object[]: Optional file attachments. Max 25MB total across all attachments (after base64 decode for inline, after fetch for URL-source); max 20 attachments per email. Each entry provides either inline base64 `content` or a public `url` for server-fetch.
    - `filename` string: Filename presented to the recipient. Inferred from the URL path when `url` is used and this is omitted; falls back to 'attachment' when neither is available.
    - `contentId` string: Content-ID for inline attachments referenced in the HTML body via `cid:<id>` URIs (e.g. set to 'logo' to be referenced as `<img src="cid:logo">`). When present, the attachment uses `Content-Disposition: inline`; when absent, `attachment`.
    - `contentType` string: MIME type. Auto-detected from `filename` extension if omitted (e.g. 'application/pdf' for .pdf, 'image/png' for .png). Falls back to 'application/octet-stream' if undetectable.
    - `content` string: Base64-encoded attachment bytes. Provide either `content` or `url`. Single base64 string up to ~33MB on the wire (decodes to ~25MB binary).
    - `url` string, uri: Public URL the server fetches and attaches. Provide either `content` or `url`. URLs to private/loopback/link-local IPs are rejected to prevent SSRF. Max 25MB after download.
  - `headers` object: Custom email headers as key-value pairs
  - `metadata` object: Arbitrary key-value metadata to attach to the message
  - `inReplyTo` string: The message this email replies to: either an RFC 5322 Message-ID (with or without angle brackets, e.g. from an inbound email's externalId) or an Anima message id (cuid). The reply joins that message's thread and emits In-Reply-To/References so external clients thread it too.
  - `references` string[]: Explicit References chain, oldest first: one RFC 5322 Message-ID per entry (an email's externalId). Anything else, such as an Anima message id, is ignored. On a reply, this chain is used only if it includes the replied-to message's Message-ID; otherwise, or if omitted, the chain is derived from the replied-to message automatically (its References plus its Message-ID).

## Response `200`

OK

- object: Full message resource representation
  - `id` string, required: Unique identifier of the message
  - `agentId` string, required: ID of the agent that sent or received the message
  - `inboxId` string, nullable, required: ID of the inbox this message belongs to — the inbox it was delivered to (inbound) or sent from (outbound). Null when no inbox backs the address: SMS/MMS/voice messages, and email sent from an email identity that has no inbox (such as a custom-domain identity added to an agent). Pass it to GET /messages?inboxId= to list one inbox's mail.
  - `channel` 'EMAIL' | 'SMS' | 'MMS' | 'VOICE' | 'WHATSAPP' | 'RCS', required: Communication channel used
  - `direction` 'INBOUND' | 'OUTBOUND', required: Whether the message was inbound or outbound
  - `status` 'QUEUED' | 'SENT' | 'DELIVERED' | 'FAILED' | 'BOUNCED' | 'BLOCKED' | 'PENDING_APPROVAL', required: Current delivery status
  - `fromAddress` string, required: Sender address (email or phone number)
  - `toAddress` string, required: Recipient address (email or phone number)
  - `subject` string, nullable, required: Message subject line, or null for channels that do not support subjects
  - `body` string, required: Plain-text content of the message
  - `bodyHtml` string, nullable, required: HTML content of the message, or null if not available
  - `extractedText` string, nullable, required: Plain-text body with the quoted reply chain and signature stripped — just what this sender actually wrote. Read this instead of `body` to avoid re-reading (and paying for) the whole thread on every message. Null when nothing was extracted: non-email channels, messages your agent sent, and mail received before this field existed — so `extractedText ?? body` is always correct. Best-effort and heuristic; `body` remains the verbatim source of truth. Known weak cases: forwards and bottom-posted replies have no separable 'new' part, so this returns the full body rather than nothing.
  - `extractedHtml` string, nullable, required: HTML body with quoted reply chains removed. Same semantics as `extractedText`; `bodyHtml` stays verbatim. Quoting styles that mark the chain with a sibling element rather than wrapping it (Outlook) are not stripped here — `extractedText` still is.
  - `headers` object, nullable, required: Raw message headers as key-value pairs, or null
  - `metadata` object, nullable, required: Arbitrary metadata attached to the message, or null
  - `threadId` string, nullable, required: Thread identifier for conversation grouping, or null
  - `labels` string[], required: Workflow labels on this message. Always contains exactly one of the system labels `unread` or `read`; may also contain `archived`, `spam` (the inbound spam verdict — see `includeSpam` on list/search), and any labels you add yourself. Stored lowercase, deduped, and sorted. Change them with PATCH /messages/{id}/labels.
  - `inReplyTo` string, nullable, required: Message ID this message is replying to, or null
  - `externalId` string, nullable, required: External identifier, or null. For email: the RFC 5322 Message-ID (bare, no angle brackets) — pass it as inReplyTo to reply in-thread. For SMS: the provider-assigned message id.
  - `deletedAt` string, date-time, nullable, required: When this message was moved to Trash, or null if it is live. Deleted messages are hidden from listings unless `deleted` is set to `include` or `only`, and can be brought back with POST /messages/{id}/restore. Nothing purges them.
  - `sentAt` string, date-time, nullable, required: Timestamp when the message was sent, or null if not yet sent
  - `receivedAt` string, date-time, nullable, required: Timestamp when the message was received, or null for outbound messages
  - `readAt` string, date-time, nullable, required: Outbound WhatsApp and RCS messages only: when the recipient's device reported the message as read. Null for inbound messages and for every other channel. Unrelated to the read and unread labels, which record whether the agent has handled a message.
  - `attachments` object[], required: File attachments associated with this message
    - `id` string, required: Unique identifier of the attachment
    - `filename` string, required: Original filename of the attachment
    - `mimeType` string, required: MIME type of the attachment (e.g. application/pdf)
    - `sizeBytes` integer, required: File size in bytes
    - `storageKey` string, required: Internal storage key for retrieving the file
    - `url` string, uri, nullable, required: Pre-signed download URL, or null if not yet generated
    - `createdAt` string, date-time, required: Timestamp when the attachment was created
    - `scanStatus` 'PENDING' | 'CLEAN' | 'FLAGGED' | 'BLOCKED', required: Content-scan verdict. BLOCKED attachments cannot be downloaded — inspect before fetching bytes.
    - `detectedMimeType` string, nullable, required: MIME type inferred from magic bytes; may differ from the declared mimeType
  - `createdAt` string, date-time, required: Timestamp when the message record was created
  - `updatedAt` string, date-time, required: Timestamp when the message record was last updated

---

[API](https://skmtc.dev/useanima/apis/anima-api.md) · [All operations](https://skmtc.dev/useanima/apis/anima-api/llms.txt) · [OpenAPI document](https://skmtc.dev/useanima/apis/anima-api/revisions/aaa148188980?raw)
