Skip to main content
POST
/
api
/
email
/
messages
/
send
Send an email message
curl --request POST \
  --url https://your-instance.example.com/api/email/messages/send \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data @- <<EOF
{
  "from": "ops@acme.example",
  "to": [
    "jamie.lee@example.com"
  ],
  "subject": "Weekly report",
  "body": "Hi Jamie — attaching this week's report.",
  "isHtml": false
}
EOF
{
  "message": {
    "_id": "665f1a0c0e0a4b001a2c9f60",
    "channelType": "email",
    "status": "delivered",
    "body": "Hi Jamie — attaching this week's report."
  },
  "conversationId": "665f1a0c0e0a4b001a2c9f70",
  "threadId": "gthread-1902a9f3c4b1e0d5"
}

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Body

application/json

Payload to send an email through a connected mailbox. The server resolves which mailbox to send from by matching from (and optionally provider) against the authenticated user's connected email accounts.

from
string<email>
required

Email address of the sending mailbox. Must belong to a mailbox the authenticated user has connected via the OAuth flow (Gmail or Outlook). Example: aneesh@tetherai.ca.

Example:

"aneesh@tetherai.ca"

to
required

Recipient address, or an array of recipient addresses. Each entry may be a bare email (user@x.com) or RFC 5322 formatted ("Name" <user@x.com>).

Example:
["recipient@example.com"]
body
string
required

Message body. Plain text by default; set isHtml: true to send as HTML. Required.

Example:

"Hi — here is the report you asked for."

provider
enum<string>

Tie-breaker used only when the same from address is connected under more than one provider for the same user. Omit in the common case — the server resolves it automatically.

Available options:
gmail,
outlook
Example:

"gmail"

cc
string<email>[]

Optional CC recipients. Same format rules as to.

bcc
string<email>[]

Optional BCC recipients. Same format rules as to.

subject
string

Subject line. Optional — when omitted on a reply, the server reuses the subject from the conversation; when omitted on a new thread, (no subject) is used.

Example:

"Weekly report"

isHtml
boolean
default:false

When true, body is sent as text/html instead of text/plain.

threadId
string

Join key to continue an existing email thread. For Gmail this is the Gmail threadId; for Outlook it is the conversationId. Both are normalised to the same field.

New email: omit this field. The server creates a new conversation and the provider assigns a brand-new thread.

Reply: pass the thread id of the conversation you want to continue. The provider appends the reply to the same thread, and the server auto-fills inReplyTo / references from the latest message in that thread.

How to obtain it:

  1. From the response of the first POST /api/email/messages/send — the body returns threadId. Persist it for future replies.
  2. From GET /api/conversation (or /api/conversation/:id) — the conversation's channelIdentifier field holds the email thread id for email-channel conversations.
  3. From a stored message — each email message carries emailMetadata.threadId on the Message document.

Recipient address alone is not enough to continue a thread, because the same recipient can have multiple unrelated threads.

Example:

"gthread-1902a9f3c4b1e0d5"

inReplyTo
string

RFC 5322 In-Reply-To header — the Message-ID of the message being replied to (with angle brackets). Used for threading in recipient clients. If omitted on a reply, the server fills it from the latest message in the thread.

references
string

RFC 5322 References header — space-separated Message-IDs forming the reply chain. If omitted on a reply, the server fills it from the latest message in the thread.

isInternal
boolean

Reserved. Must be false or omitted on this endpoint. Private notes are posted through the messages endpoint, not the email send endpoint. Sending true here returns 400.

attachments
object[]

File attachments to include. Each entry must supply either storageKey (preferred — an R2 storage key produced by the upload endpoint) or a pre-signed url the server can GET.

Response

Email sent. Returns the stored message document, conversation id, and provider thread id.

Success response for POST /api/email/messages/send.

message
object
required

Stored email message document (the message field on the send response).

Example:
{
"_id": "665f1a0c0e0a4b001a2c9f60",
"organizationId": "64ee9a8b1e7f2a0011223344",
"conversationId": "665f1a0c0e0a4b001a2c9f70",
"contactId": "665f1a0c0e0a4b001a2c9f01",
"userId": "64ee9a8b1e7f2a0011223399",
"channelType": "email",
"isIncoming": false,
"isInternal": false,
"status": "delivered",
"body": "Hi Jamie — attaching this week's report.",
"attachments": [],
"createdAt": "2026-05-18T12:34:56.000Z",
"updatedAt": "2026-05-18T12:34:56.000Z"
}
conversationId
string
required

Internal id of the conversation the message was appended to (or created, on a new thread). Use this with GET /api/conversation/:id to fetch the full conversation.

threadId
string

Provider-side thread id for this email (Gmail threadId / Outlook conversationId). Save this — pass it back as threadId on subsequent POST /api/email/messages/send calls to continue the same thread.

Example:

"gthread-1902a9f3c4b1e0d5"