---
title: "List sent messages"
method: GET
path: "/v1/messages"
tags: ["Messages"]
---

# List sent messages

`GET /v1/messages`

## Request

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

**Requires scope:** `read`.

## Query parameters

- `limit` integer
- `cursor` string
- `agent_id` string
- `thread_id` string
- `q` string

## Response `200`

A page of sent messages.

- object
  - `data` SentMessage[], required: The items on this page, at most `limit` of them.
    - `id` string, required: Message id (`msg_…`), the `{id}` in `/v1/messages/{id}`.
    - `agent_id` string: Id of the agent that sent the message (`agt_…`).
    - `from` string, 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_id` string: The thread the message belongs to (`thrd_…`).
    - `in_reply_to_message_id` string: 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.
    - `to` string[]: 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` string[]: Cc recipients. Always present on reply responses (`[]` when none); on the list and the single-message GET, present when there are any.
    - `bcc` string[]: Bcc recipients. Present only on the list and the single-message GET, when there are any.
    - `subject` string: 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_text` string: 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_html` string: The HTML body as sent. Present only on the single-message GET, when there is one.
    - `classifier_score` number: 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_recipients` string[]: 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_usd` number: USD cost of the send. 0 for scheduled or test-mode sends.
    - `ses_message_id` string: 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.
    - `attachments` AttachmentMeta[]: 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.
      - `filename` string, required: The file name, as given in the send's `attachments`.
      - `content_type` string, required: 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_bytes` integer, required: Size of the file in bytes: `content_base64` decoded, or the bytes fetched from `path`.
      - `content_id` string: Only on a file shown inside the HTML body: the id its `cid:` names.
    - `references` string[]: 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.
    - `metadata` object: 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.
    - `tags` Tag[]: 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.
      - `name` string, required: The tag's name, as given on the send, reply or draft.
      - `value` string, required: The tag's value, as given; it may be an empty string.
    - `sent_at` string, 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_at` string, 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_at` string, 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_at` string, date-time: When the scheduled send was canceled. Present only on the list and the single-message GET, for a canceled message.
    - `bounced_at` string, 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_at` string, date-time: When a recipient's complaint about the message was last reported. Present only on the single-message GET.
    - `forwarded_from` string: Source message id (forward responses only).
    - `test_mode` boolean: True for a message sent with a test key (`mk_test_…`): nothing was transmitted and nothing was billed.
    - `classifier_warning` object: 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.
      - `code` 'cold_email_prohibited', required: Always `cold_email_prohibited`, the error code a live key would get.
      - `message` string, required: The verdict in words: why a live key would refuse the message, what would pass instead, and that nothing was transmitted.
      - `score` number, required: The firewall's score for the message, from 0 (clean) to 1 (cold or spam).
      - `reason` string, required: What the message was classified as, such as `cold_outreach`, `spam_pattern` or `phishing_lure`.
    - `created_at` string, date-time: When the message was created; for a scheduled send, when it was scheduled, not when it went out.
  - `has_more` boolean, required: True when more items follow this page: pass `next_cursor` as `cursor` to get them.
  - `next_cursor` string: Pass as `cursor` to fetch the next page. Absent on the last page.

## Other responses

- `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`).
- `409`: Conflict — duplicate resource, an in-progress idempotent request, a draft or scheduled message that started sending while it was being changed, or a thread deleted for good while one of its messages is being sent (`duplicate_resource`); or an agent name more than one agent has (`agent_name_conflict`: use the agent's id); or a status change on an archived agent, since archiving cannot be undone (`agent_archived`).
- `500`: Internal server error.

---

[API](https://skmtc.dev/mails/apis/mails-ai-api.md) · [All operations](https://skmtc.dev/mails/apis/mails-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mails/apis/mails-ai-api/revisions/4d4930a8c33b?raw)
