Messages

Send a message

Requires scope: send. Sends an email from one of your agents. With a mk_test_ key the message is stored and its message.sent event reaches your webhooks (marked test_mode: true), but no email is sent and nothing is billed. Quota, suppression and rate-limit refusals are skipped, and a message the cold-email firewall would refuse comes back 201 with classifier_warning instead. Send to reply@test.mails.ai to get an automatic reply within about a second; it answers at most 3 times in one thread, so a 4th message there gets no reply. It is the only address at test.mails.ai: a send naming any other there is refused with 422 unknown_test_address, and nothing is sent.

post/v1/messages

Request

  • Base URL: https://api.mails.ai
  • URL: https://api.mails.ai/v1/messages
  • Auth: HTTP bearer

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.

Request body

agentstring

Which agent sends this — its name or agt_ id. Optional: omit it and we use the workspace's single agent, or provision one named from from when the key may create agents (the manage scope); a workspace with no active or paused agent gets its first one that way even without from. Required only when the workspace has several agents and no from names one of them. When it is given it decides the sender: a from beside it picks no agent and creates none. A key tied to one agent always sends as that agent and may leave this out; naming another agent here, or in from when this is left out, is a 403 insufficient_scope.

fromstring

The sender you want, as a handle or address — billing, billing@acme.com and Acme <billing@acme.com> all mean the agent called billing. It SELECTS that agent: resolved if it exists. If it doesn't, it is created when the key may create agents (the manage scope) and your plan has room for it; otherwise the send is refused, with 403 insufficient_scope when the key may not create agents and 402 plan_limit_exceeded when your plan has no room. An address at a domain you have verified is matched whole: billing@yourdomain.com means the agent at exactly that address, created on your domain on the same terms if it doesn't exist yet. It is never resolved to a different agent, and it cannot spoof a sender — the address on the From line is always derived from the agent's own identity. Beside an explicit agent it selects nothing: agent decides the sender. Either way a display name in it, like Acme above, goes on the From line, a scheduled send's too. The address that actually sent comes back as from on the response.

ccstring[]

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

bccstring[]

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

subjectstring required

Subject line, 1 to 998 characters.

body_textstring

Plain-text body, up to 1,000,000 characters. At least one of body_text and body_html is required, or their aliases text and html.

body_htmlstring

HTML body, up to 1,000,000 characters. With body_text as well, the email carries a plain-text part and an HTML part.

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.

reply_tostring email

The address replies should go to. Left out, replies go to the agent's receiving_address.

in_reply_to_message_idstring

Id of the message this one answers (msg_… or rcv_…), named in the email's In-Reply-To header. The send joins its thread when the same agent sent or received it. Left out, it starts a new thread.

referencesstring[]

Message-IDs for the References header. Write one from another system with its angle brackets (<…>); a value without them is read as a mails.ai message id.

metadataobject

Your own key-value strings, kept with the message and returned by GET /v1/messages/{id}; they are not part of the email. Keys up to 64 characters, values up to 500.

scheduled_atstring date-time

Send at this time (ISO 8601) instead of now. A time in the past sends at once. A future time cannot be combined with attachments. When a passing failure stops a scheduled send (one of ours, or the email service not taking more mail for now), it is tried again a minute or more later, up to 3 tries in all, and then fails with message.failed; a scheduled draft is tried the same way and fails with draft.failed.

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.

Response

Message accepted (sent or scheduled).

idstring required

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

agent_idstring

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

fromstring email

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.

status'scheduled' | 'sending' | 'sent' | 'delivered' | 'delayed' | 'bounced' | 'complained' | 'rejected' | 'canceled' required

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).

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.

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.

sent_atstring date-time

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 date-time

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 date-time

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 date-time

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

bounced_atstring date-time

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 date-time

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.

created_atstring date-time

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

Changes

No recorded changes to this operation across all 1 revision of this API.