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);from mailsai import create_client
client = create_client() # reads MAILS_API_KEY
result = client.forward(
"msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
to="warehouse@example.com",
body_text="Forwarding the shipping confirmation.",
)
print(result)curl -X POST 'https://api.mails.ai/v1/messages/msg_01JZX8K3M9Q4P7VN2YB6RTDC0E/forward' \
-H "Authorization: Bearer $MAILS_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"to": "warehouse@example.com",
"body_text": "Forwarding the shipping confirmation."
}'mails emails forward rcv_01JABC \
--to warehouse@example.com \
--text "For your records."{
"name": "mails_forward",
"arguments": {
"message_id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
"to": "warehouse@example.com",
"body_text": "Forwarding the shipping confirmation."
}
}{
"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"
}201 The forwarded message.
400 Invalid request — malformed JSON or a field failed
validation. Also feature_not_enabled when this
server has a feature switched off (custom domains
before they are available).
401 Missing or invalid API key.
403 The API key lacks the required scope
(insufficient_scope), or the token belongs to an
app connected by sign-in and this operation is
closed to connected apps
(connected_app_not_allowed).
404 Resource not found in this workspace, or the agent
a send names is archived (agent_archived).
422 Unprocessable — agent paused, recipient suppressed,
a recipient at test.mails.ai other than
reply@test.mails.ai (unknown_test_address), or
abuse guard.
429 Rate or quota limit exceeded. The body's resets_at
and retry_after_seconds say when the request can
succeed. A Retry-After header is sent only when
that is 60 seconds or less; a longer wait (an
hourly, daily or monthly cap) sends none, so retry
at resets_at or raise the limit instead of
sleeping.
500 Internal server error.
502 The email service the mail goes out through did not
confirm it took it (upstream_error), so it is
recorded as not sent. The message is a fixed
sentence naming the request id to quote to
support@mails.ai; the service's own error is never
returned. When the service refuses every recipient
as undeliverable the answer is 422
recipient_suppressed instead, and when it is only
not taking more mail for now, 503
upstream_unavailable.
503 Temporarily unavailable, and nothing was done. On a
send: the email service is not taking more mail
right now (upstream_unavailable), so nothing was
sent; send it again after the seconds in
Retry-After.Forward a message
Fwd: subject.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);from mailsai import create_client
client = create_client() # reads MAILS_API_KEY
result = client.forward(
"msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
to="warehouse@example.com",
body_text="Forwarding the shipping confirmation.",
)
print(result)curl -X POST 'https://api.mails.ai/v1/messages/msg_01JZX8K3M9Q4P7VN2YB6RTDC0E/forward' \
-H "Authorization: Bearer $MAILS_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"to": "warehouse@example.com",
"body_text": "Forwarding the shipping confirmation."
}'mails emails forward rcv_01JABC \
--to warehouse@example.com \
--text "For your records."{
"name": "mails_forward",
"arguments": {
"message_id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
"to": "warehouse@example.com",
"body_text": "Forwarding the shipping confirmation."
}
}{
"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"
}201 The forwarded message.
400 Invalid request — malformed JSON or a field failed
validation. Also feature_not_enabled when this
server has a feature switched off (custom domains
before they are available).
401 Missing or invalid API key.
403 The API key lacks the required scope
(insufficient_scope), or the token belongs to an
app connected by sign-in and this operation is
closed to connected apps
(connected_app_not_allowed).
404 Resource not found in this workspace, or the agent
a send names is archived (agent_archived).
422 Unprocessable — agent paused, recipient suppressed,
a recipient at test.mails.ai other than
reply@test.mails.ai (unknown_test_address), or
abuse guard.
429 Rate or quota limit exceeded. The body's resets_at
and retry_after_seconds say when the request can
succeed. A Retry-After header is sent only when
that is 60 seconds or less; a longer wait (an
hourly, daily or monthly cap) sends none, so retry
at resets_at or raise the limit instead of
sleeping.
500 Internal server error.
502 The email service the mail goes out through did not
confirm it took it (upstream_error), so it is
recorded as not sent. The message is a fixed
sentence naming the request id to quote to
support@mails.ai; the service's own error is never
returned. When the service refuses every recipient
as undeliverable the answer is 422
recipient_suppressed instead, and when it is only
not taking more mail for now, 503
upstream_unavailable.
503 Temporarily unavailable, and nothing was done. On a
send: the email service is not taking more mail
right now (upstream_unavailable), so nothing was
sent; send it again after the seconds in
Retry-After.Path Parameters
The message being forwarded (msg_… or rcv_…).
Body Parameters
Who the forward goes to: one address, or a list of 1 to 50.
Addresses to copy, up to 50. Every recipient sees them.
Addresses to blind-copy, up to 50. They receive the forward but appear in none of its headers.
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:.
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.
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.
Alias of body_html: used when body_html is left out, ignored when both are sent.
Alias of body_text: used when body_text is left out, ignored when both are sent.
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
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
Message id (msg_…), the {id} in /v1/messages/{id}.
Id of the agent that sent the message (agt_…).
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.
The thread the message belongs to (thrd_…).
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.
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.
Cc recipients. Always present on reply responses ([] when none); on the list and the single-message GET, present when there are any.
Bcc recipients. Present only on the list and the single-message GET, when there are any.
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.
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.
The HTML body as sent. Present only on the single-message GET, when there is one.
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.
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).
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.
USD cost of the send. 0 for scheduled or test-mode sends.
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.
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.
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.
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.
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.
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.
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.
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.
When the scheduled send was canceled. Present only on the list and the single-message GET, for a canceled message.
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.
When a recipient's complaint about the message was last reported. Present only on the single-message GET.
Source message id (forward responses only).
True for a message sent with a test key (mk_test_…): nothing was transmitted and nothing was billed.
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.
When the message was created; for a scheduled send, when it was scheduled, not when it went out.
Was this page helpful?