Inbound

Retrieve a received message

Requires scope: read.

get/v1/messages/received/{id}

Request

  • Base URL: https://api.mails.ai
  • URL: https://api.mails.ai/v1/messages/received/{id}
  • Auth: HTTP bearer

Path parameters

idstring required

Received message id (rcv_…).

Response

The received message, including full body and raw_url.

idstring required

Received message id (rcv_…).

agent_idstring required

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

thread_idstring

The thread (thrd_…) the message is filed in. Absent when it was stored without one.

envelope_fromstring nullable

The envelope sender (SMTP MAIL FROM): where bounces go, which a sending service often sets to its own bounce address. Replies go to the Reply-To or, without one, to from, never here. Null on mail received before this field was added and on messages made with POST /v1/test/inbound.

tostring email required

The agent that received it, by its API address (its email; a single string, not an array). People write to an agent at its receiving_address.

delivered_tostring nullable

The address the email was sent to, in lower case: the agent's receiving_address, or a plus address of it (qa.acme+run-1842@in.mails.ai), which reaches the same agent. What the to filter matches. On a message made with POST /v1/test/inbound it is that request's to, or the receiving_address. Null on mail received before 2026-10-16.

subjectstring

The email's subject. Absent when it has none, and on a rate_limited, plus_address_limit or held_code record (over_limit), which keeps no content.

is_thread_replyboolean

True when the email's In-Reply-To names one of this agent's own sent messages; false otherwise, even when the message joined a thread by its subject, as mail from someone already in that thread does.

in_reply_to_message_idstring

The email's In-Reply-To header: a bare msg_… id when it names one of our own messages, otherwise as received. Absent when there is none, and on mail received over a limit.

thread_root_message_idstring

Id (msg_…) of the agent's sent message this one replies to. Present only when is_thread_reply is true.

body_textstring

Full body — single-message GET only.

body_htmlstring

The HTML part — single-message GET only, and only for mail the injection scan read (the scan reads the HTML too). Absent for mail answered by the rules layer, or received while the workspace was over its inbound quota.

body_text_excerptstring

The first 500 characters of the plain-text body; the whole text is body_text on the single-message GET. Absent on a rate_limited, plus_address_limit or held_code record, which keeps no content.

extracted_textstring

Reply text with quoted history stripped.

extracted_htmlstring

The HTML counterpart of extracted_text: the email's HTML part with its quoted history stripped, which for mail that quotes nothing is the whole HTML part. Present only for mail the injection scan read, as body_html is; absent when the email had no HTML part or nothing was left once the quoted history was stripped, and on mail received before this field was filled in.

spf_passboolean

True when SPF passed, false when it failed. SPF is not checked on real mail, so it is absent there; mail made with POST /v1/test/inbound carries the spf that was sent (default pass), absent for unknown.

dkim_passboolean

True when a DKIM signature verified, false when the email was signed and none did; absent when it was unsigned or could not be checked. Mail made with POST /v1/test/inbound carries the dkim that was sent.

dkim'pass' | 'fail' | 'none' | 'unknown'

As the inbound webhook events carry it: pass when a DKIM signature verifies, whichever domain signed it; fail; none for an unsigned email; unknown when it could not be checked, and on mail received before this field was added.

dkim_domainstring nullable

The domain of a passing DKIM signature that matches the domain of from.address, or null.

dmarc'pass' | 'fail' | 'none' | 'unknown'

For the domain of from.address: pass when it publishes DMARC and a DKIM signature aligned with it verifies, the one case in which from.address is proven; none when it publishes no DMARC record; unknown when no aligned signature verifies or it was not checked (as on mail received before this field was added). fail only where an SPF verdict is trusted.

auth_failedboolean

True when the sender was judged forged: no DKIM signature aligned with the domain of from.address verifies, that domain's DMARC policy is quarantine or reject, and no listed mail service that forwarded the email vouches for the sender (the event's trusted_forwarder). It is the verdict behind message.received.unauthenticated, whose auth_failed says the same. Mail kept unread because the workspace was over a limit is not checked, and is false. Mail made with POST /v1/test/inbound is true when it was sent with spf and dkim both fail.

has_attachmentboolean required

True when the message carries at least one file attachment (images embedded in the HTML body do not count).

attachments_omitted_countinteger

How many files were not kept, when more than 100 could not all be listed. Present only when above 0.

raw_omitted'too_large' | 'budget' | 'over_quota' | 'unavailable'

Present when the raw email was not kept, and why: too_large (over 25 MiB, or over 3 MB from an edge that could not send it in pieces), budget (the workspace kept its day's allowance of raw mail), over_quota (received while the workspace was over a limit; over_limit says which), unavailable (it never reached us, e.g. mail from before raw emails were kept). Without the raw email the message has its text but no downloadable attachments.

over_limit'quota_exceeded' | 'subscription_inactive' | 'rate_limited' | 'plus_address_limit' | 'held_code'

Present when the message arrived while the workspace was over a limit, and which: quota_exceeded (the month's inbound allowance was used up) or subscription_inactive (the subscription was canceled or paused), both kept but not classified; rate_limited (more mail in an hour or a day than the plan takes), plus_address_limit (sent to a new plus address after the plan's plus addresses for the day were used: 20 on Free, 1,000 on Pro, 10,000 on Scale) or held_code (sent to a plus address with a sign-in code or link from a service such as Google, Microsoft, Apple, a payment app or a bank, whose codes plus addresses do not receive), each a record of who sent it, to whom and when, without its subject or body (at most 20 such records an hour; past that, such mail is not recorded). Each raises message.received.over_limit.

parse_status'parsed' | 'quarantined' | 'pending' | 'failed' | 'quota_skipped' required

quota_skipped: not classified or scanned, because the workspace was over a limit (over_limit says which). It is still in a thread, so it can be trashed and deleted, and raised message.received.over_limit instead of message.received.

injection_scorenumber nullable required

How likely the message is a prompt-injection attempt, from 0 to 1, as the inbound scan scored it (the same score as the inbound event's injection_score). Null when the message was never classified (parse_status pending, failed or quota_skipped).

quarantinedboolean nullable required

True when the scan scored the message at or above the quarantine line (0.95): it is still delivered, marked, so your agent can skip it. Null when the message was never classified.

received_atstring date-time required

When the message reached us (UTC), not the email's own Date header.

raw_urlstring

Path to the message as an .eml file — single-message GET only. The exact message as received when it was kept, else one rebuilt from the stored fields.

created_atstring date-time

When the message's record was written; received_at is when the mail arrived.

Changes

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