Skip to main content
import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY

const result = await client.messages.forward(
  "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
  {
    to: "warehouse@example.com",
    body_text: "Forwarding the shipping confirmation.",
  },
);
console.log(result);
{
  "id": "msg_01JZXD2B4C6D8E0F2G4H6J8K1N",
  "agent_id": "agt_01JZ9F8H7KQ2M3N4P5R6S7T8U9",
  "thread_id": "thrd_01JZX8K3M9Q4P7VN2YB6RTDC11",
  "to": ["warehouse@example.com"],
  "subject": "Fwd: Your order #10473 has shipped",
  "status": "sent",
  "cost_usd": 0.0004,
  "created_at": "2026-06-24T18:10:02.340Z",
  "forwarded_from": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E"
}

Forward a message

Forwards a sent or received message to new recipients with a quoted body and Fwd: subject.
POST/v1/messages/{id}/forwardscope · send
import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY

const result = await client.messages.forward(
  "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
  {
    to: "warehouse@example.com",
    body_text: "Forwarding the shipping confirmation.",
  },
);
console.log(result);
{
  "id": "msg_01JZXD2B4C6D8E0F2G4H6J8K1N",
  "agent_id": "agt_01JZ9F8H7KQ2M3N4P5R6S7T8U9",
  "thread_id": "thrd_01JZX8K3M9Q4P7VN2YB6RTDC11",
  "to": ["warehouse@example.com"],
  "subject": "Fwd: Your order #10473 has shipped",
  "status": "sent",
  "cost_usd": 0.0004,
  "created_at": "2026-06-24T18:10:02.340Z",
  "forwarded_from": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E"
}

Path Parameters

idstringrequired

The message being forwarded (msg_… or rcv_…).

Body Parameters

tostring | string[]required

Who the forward goes to: one address, or a list of 1 to 50.

ccstring[]

Addresses to copy, up to 50. Every recipient sees them.

bccstring[]

Addresses to blind-copy, up to 50. They receive the forward but appear in none of its headers.

subjectstring

Subject of the forward, 1 to 998 characters. Left out, it is Fwd: and the original's subject, kept as it is when that already starts with Fwd: or Fw:.

body_textstring

Your note, up to 1,000,000 characters, placed above a ---------- Forwarded message ---------- block that quotes the original's From, To, Subject and plain text. Left out, the forward carries that block alone.

body_htmlstring

Your note as HTML, up to 1,000,000 characters, placed above the same ---------- Forwarded message ---------- block as the plain-text part and then the original's own HTML (or its plain text when it has none). Of a whole HTML document, only what is inside <body> is used. Left out, body_text is the note there, and the forward has an HTML part only when the original has one.

htmlstring

Alias of body_html: used when body_html is left out, ignored when both are sent.

textstring

Alias of body_text: used when body_text is left out, ignored when both are sent.

list_unsubscribeboolean

Opt in to RFC 8058 one-click unsubscribe: the message carries List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click headers, so the recipient's mail app shows an Unsubscribe button. One click suppresses the address for your whole workspace: every later send to it from any of your agents is refused with recipient_suppressed until you allowlist it (POST /v1/suppression/allow). Turn it on for newsletters, digests and other recurring mail. Leave it off (the default) for password resets, sign-in links, receipts and anything else the recipient must keep receiving.

Needs exactly one to recipient and no cc or bcc.

Headers

Idempotency-Keystring

Replays the stored response for a repeated key (kept 24 hours), so a retried request never double-sends; the replay carries Idempotent-Replay: true. An error is kept on the key only once the mail has gone out, so a replayed error means it was sent. Once a scheduled send is stored, a failure after that keeps the message's answer on the key instead: the retry answers with the message (201) and raises its message.scheduled if the first request did not. After a failure before either, the same key sends again, as it does after a batch that stored no message and sent no mail.

Always sends immediately. Both parts carry the forwarded message: the HTML part is the covering note (body_html, else body_text), the forwarded header and the original (its own HTML, or its text), sent whenever there is HTML to carry. A received message's files go with it, up to the limits of any send (10 files, 25 MB in all), one shown inside its HTML keeping its content_id; they are listed in attachments. Files that cannot go are named in both parts under Not attached:: a sent message's (only their names are kept), a received file that was too large to keep, and any past the limits. A forward cannot add files of its own: attachments in the request answers 422 invalid_field (param attachments); send files with POST /v1/messages.

Response

idstring

Message id (msg_…), the {id} in /v1/messages/{id}.

agent_idstring

Id of the agent that sent the message (agt_…).

fromstring

The agent address this was sent as — the resolved from. Read it back when you pass from as a naming hint instead of an explicit agent: it is the receipt for which identity the API picked. On a verified custom domain the message goes out from this exact address. On the shared mails.ai domain it goes out from <name>.<workspace>@send.mails.ai, and replies come back through <name>.<workspace>@in.mails.ai.

thread_idstring

The thread the message belongs to (thrd_…).

in_reply_to_message_idstring

The message this one answers: the message replied to or forwarded, or the in_reply_to_message_id the send set. Present on reply responses and on the single-message GET, when set.

tostring[]

The To recipients, always a list. On a reply they come from the original: its Reply-To or sender for a received message, its to for one of your sent messages.

ccstring[]

Cc recipients. Always present on reply responses ([] when none); on the list and the single-message GET, present when there are any.

bccstring[]

Bcc recipients. Present only on the list and the single-message GET, when there are any.

subjectstring

The subject as sent. On a reply, Re: and the original's subject, unless that already starts with Re:; on a forward, the subject you gave or Fwd: and the original's.

body_textstring

The plain-text body as sent; on a forward it includes the quoted original. Present only on the single-message GET, when there is one.

body_htmlstring

The HTML body as sent. Present only on the single-message GET, when there is one.

classifier_scorenumber

The cold-email firewall's score for the message, from 0 (clean) to 1 (cold or spam). Present on the send response, the list and the single-message GET.

statusstring

sent once handed off; delivered, bounced or complained when the receiving side reports it (with several recipients the latest report wins). delayed when a transient (soft) bounce came back: the receiving server has not taken the message yet; it is not a bounce, does not count toward the bounce rate, raises message.delayed (sent only to endpoints that list it) and may still become delivered; with no delivery confirmed within 5 days of sending, it becomes bounced. sending only while a scheduled send is going out; rejected when the send failed, or a scheduled send was refused when it came due; canceled when a scheduled send was stopped. A live send only to the reserved test address reply@test.mails.ai is delivered at once, with a message.delivered event, since it is never transmitted; with a test key it stays sent, as every test-mode send does. Either way its automatic reply arrives as reply.received (at most 3 replies in one thread; a 4th message there gets none).

One of: scheduled, sending, sent, delivered, delayed, bounced, complained, rejected, canceled

refused_recipientsstring[]

The recipients the email service refused when the message was sent, while it took the message for the others: it went to everyone else, and not to these. The message is still a send, so sending it again with the same Idempotency-Key gives this answer back and sends nothing; to reach them, check the addresses and send them a new message. Present only when there were any: on the send, batch, reply and forward responses, the list, the single-message GET and the message's message.sent event. When every recipient is refused, nothing is sent and the send fails instead.

cost_usdnumber

USD cost of the send. 0 for scheduled or test-mode sends.

ses_message_idstring

The sending provider's id for the message, which its delivery, bounce and complaint reports refer to; test-<message id> for a test-mode send. Present only on the list and the single-message GET, once the message has been handed off.

attachmentsobject[]

Name, type and size of each file the send carried; the files themselves are not kept. Present when there were any, on the send response, the list and the single-message GET.

Show propertiesHide properties
filenamestring

The file name, as given in the send's attachments.

content_typestring

The MIME type given in the send's attachments. A file fetched from its path without one takes the type the URL answered with, or application/octet-stream when it named none.

size_bytesinteger

Size of the file in bytes: content_base64 decoded, or the bytes fetched from path.

content_idstring

Only on a file shown inside the HTML body: the id its cid: names.

referencesstring[]

The References chain kept with the message: the references the send passed, or on a reply the ids of the earlier messages it answers. Present only on the single-message GET, when there is one.

metadataobject

The metadata the send passed; a scheduled send refused when it came due also carries rejected_reason and rejected_at. Present only on the single-message GET, when there is any.

tagsobject[]

The tags the message was sent with. Present when there are any, on send, batch and reply responses, the list and the single-message GET.

Show propertiesHide properties
namestring

The tag's name, as given on the send, reply or draft.

valuestring

The tag's value, as given; it may be an empty string.

sent_atstring

When the message was handed off for sending: at once for an immediate send, when it came due for a scheduled one. Present only on the list and the single-message GET, once that has happened.

delivered_atstring

When delivery was confirmed; with several recipients, the time of the latest report. Present only on the list and the single-message GET, once a delivery is confirmed.

scheduled_atstring

The time a scheduled send was set for. On send, batch and reply responses it is present only when that time was in the future; the list and the single-message GET keep it after the message has gone out or been canceled.

canceled_atstring

When the scheduled send was canceled. Present only on the list and the single-message GET, for a canceled message.

bounced_atstring

When a bounce was last reported for the message; a soft bounce, which marks it delayed, does not set it. Present only on the single-message GET.

complained_atstring

When a recipient's complaint about the message was last reported. Present only on the single-message GET.

forwarded_fromstring

Source message id (forward responses only).

test_modeboolean

True for a message sent with a test key (mk_test_…): nothing was transmitted and nothing was billed.

classifier_warningobject

Present only on a test-mode send that a live key would refuse: the cold-email firewall's verdict, reported instead of thrown. Nothing was transmitted.

Show propertiesHide properties
codestring

Always cold_email_prohibited, the error code a live key would get.

One of: cold_email_prohibited

messagestring

The verdict in words: why a live key would refuse the message, what would pass instead, and that nothing was transmitted.

scorenumber

The firewall's score for the message, from 0 (clean) to 1 (cold or spam).

reasonstring

What the message was classified as, such as cold_outreach, spam_pattern or phishing_lure.

created_atstring

When the message was created; for a scheduled send, when it was scheduled, not when it went out.

Was this page helpful?