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

const client = createClient(); // reads MAILS_API_KEY

const result = await client.received.list({ limit: 10 });
console.log(result);
{
  "data": [
    {
      "id": "rcv_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "agent_id": "agt_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "thread_id": "thrd_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "from": {
        "address": "jordan@example.com",
        "name": "<name>"
      },
      "envelope_from": "<envelope_from>",
      "to": "jordan@example.com",
      "subject": "<subject>",
      "is_thread_reply": false,
      "in_reply_to_message_id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "thread_root_message_id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "body_text": "<body_text>",
      "body_html": "<body_html>",
      "body_text_excerpt": "<body_text_excerpt>",
      "extracted_text": "<extracted_text>",
      "extracted_html": "<extracted_html>",
      "spf_pass": false,
      "dkim_pass": false,
      "dkim": "pass",
      "dkim_domain": "<dkim_domain>",
      "dmarc": "pass",
      "auth_failed": false,
      "has_attachment": false,
      "attachments": [
        {
          "id": null,
          "filename": null,
          "content_type": null,
          "size": null,
          "content_id": null,
          "inline": null
        }
      ],
      "attachments_omitted": [
        {
          "filename": null,
          "content_type": null,
          "size": null,
          "reason": null
        }
      ],
      "attachments_omitted_count": 0,
      "raw_omitted": "too_large",
      "over_limit": "quota_exceeded",
      "parse_status": "parsed",
      "injection_score": 0,
      "quarantined": false,
      "received_at": "2026-10-04T12:00:00.000Z",
      "raw_url": "<raw_url>",
      "created_at": "2026-10-04T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": "<next_cursor>"
}

List received messages

GET/v1/messages/receivedscope · read
import { createClient } from "@mailsai/sdk";

const client = createClient(); // reads MAILS_API_KEY

const result = await client.received.list({ limit: 10 });
console.log(result);
{
  "data": [
    {
      "id": "rcv_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "agent_id": "agt_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "thread_id": "thrd_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "from": {
        "address": "jordan@example.com",
        "name": "<name>"
      },
      "envelope_from": "<envelope_from>",
      "to": "jordan@example.com",
      "subject": "<subject>",
      "is_thread_reply": false,
      "in_reply_to_message_id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "thread_root_message_id": "msg_01JZX8K3M9Q4P7VN2YB6RTDC0E",
      "body_text": "<body_text>",
      "body_html": "<body_html>",
      "body_text_excerpt": "<body_text_excerpt>",
      "extracted_text": "<extracted_text>",
      "extracted_html": "<extracted_html>",
      "spf_pass": false,
      "dkim_pass": false,
      "dkim": "pass",
      "dkim_domain": "<dkim_domain>",
      "dmarc": "pass",
      "auth_failed": false,
      "has_attachment": false,
      "attachments": [
        {
          "id": null,
          "filename": null,
          "content_type": null,
          "size": null,
          "content_id": null,
          "inline": null
        }
      ],
      "attachments_omitted": [
        {
          "filename": null,
          "content_type": null,
          "size": null,
          "reason": null
        }
      ],
      "attachments_omitted_count": 0,
      "raw_omitted": "too_large",
      "over_limit": "quota_exceeded",
      "parse_status": "parsed",
      "injection_score": 0,
      "quarantined": false,
      "received_at": "2026-10-04T12:00:00.000Z",
      "raw_url": "<raw_url>",
      "created_at": "2026-10-04T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": "<next_cursor>"
}

Query Parameters

limitinteger

Page size (1–100).

Default: 25

cursorstring

Opaque cursor from a previous page's next_cursor.

agent_idstring

Filter by agent id or name. A key tied to one agent always reads that agent's mail, and naming another agent is a 403.

thread_idstring

Filter by thread id.

qstring

Case-insensitive substring of the subject, body or sender address. For word search across all mail, use GET /v1/search.

Response

dataobject[]

The items on this page, at most limit of them.

Show propertiesHide properties
idstring

Received message id (rcv_…).

agent_idstring

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.

fromobject

Who the message is from: the first mailbox of its From header, the address dkim_domain and dmarc are about. It is proven only when dmarc is pass. When no raw email reached us it is read from the From header our mail server passed on, and the envelope sender stands in only when there was none; on mail received before envelope_from was added it can be the envelope sender.

Show propertiesHide properties
addressstring

The sender's address, in lower case. On mail made with POST /v1/test/inbound it is the from that was sent, as written, or the raw email's From address.

namestring | null

The display name beside that address, decoded. Absent when there is none, or when the envelope sender stands in for from.

envelope_fromstring | null

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

The receiving agent address (single string, not an array).

subjectstring

The email's subject. Absent when it has none, and on a rate_limited 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 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.

dkimstring

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.

One of: pass, fail, none, unknown

dkim_domainstring | null

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

dmarcstring

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.

One of: pass, fail, none, unknown

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

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

attachmentsobject[]

Every part of the message that is a file, embedded images included. Download one from GET /v1/messages/received/{id}/attachments/{attachment_id}.

Show propertiesHide properties
idstring

Attachment id (att_…), unique within the message.

filenamestring

The file's name, cleaned: no path, no control characters, at most 255 characters.

content_typestring

The MIME type the sender declared, e.g. application/pdf.

sizeinteger

Size in bytes of the decoded file.

content_idstring | null

The part's Content-ID without angle brackets, which the HTML body references as cid:.

inlineboolean

True for an image embedded in the HTML body rather than attached as a file.

attachments_omittedobject[]

Files the message carried that were not kept, each with the reason. At most 100 are listed; attachments_omitted_count counts them all. Present only when there are some.

Show propertiesHide properties
filenamestring

The file's name, cleaned like a kept attachment's: no path, no control characters, at most 255 characters. A file the email did not name reads attachment.<ext>.

content_typestring

The MIME type the sender declared, in lower case; application/octet-stream when it was missing or malformed.

sizeinteger | null

Size in bytes when known.

reasonstring

Why the file was not kept: the reasons of raw_omitted, or too_many for files past the first 100 of one message.

One of: too_large, budget, over_quota, unavailable, too_many

attachments_omitted_countinteger

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

raw_omittedstring

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.

One of: too_large, budget, over_quota, unavailable

over_limitstring

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), a record of who sent it and when, without its subject or body (at most 20 such records an hour; past that, flood mail is not recorded). Each raises message.received.over_limit.

One of: quota_exceeded, subscription_inactive, rate_limited

parse_statusstring

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.

One of: parsed, quarantined, pending, failed, quota_skipped

injection_scorenumber | null

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 | null

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

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

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

has_moreboolean

True when more items follow this page: pass next_cursor as cursor to get them.

next_cursorstring

Pass as cursor to fetch the next page. Absent on the last page.

Was this page helpful?