Emails

List inbound emails

Changed on

Returns a paginated list of INBOUND emails received at your verified domains. Outbound messages sent via /send-mail are not included; this endpoint is the inbox view, not a unified send/receive history.

Supports filtering by domain, status, date range, and free-text search across subject, sender, and recipient fields.

For a compact text-table summary of the most recent N inbounds (no filters, no cursor pagination), the CLI ships primitive emails:latest as a one-line-per-email shortcut. It's TTY-aware so id columns are full UUIDs when piped, and a --json flag returns the same envelope this endpoint does. Use whichever fits the call site.

get/emails

Request

  • Base URL: https://api.primitive.dev/v1
  • URL: https://api.primitive.dev/v1/emails
  • Auth: HTTP bearer

Query parameters

cursorstring

Pagination cursor from a previous response's meta.cursor field. Format: {ISO-datetime}|{id}

limitinteger

Number of results per page

domain_idstring uuid

Filter by domain ID

status'pending' | 'accepted' | 'completed' | 'rejected'

Lifecycle status of an INBOUND email (a row in the emails table). Distinct from SentEmailStatus, which describes the OUTBOUND lifecycle (the sent_emails table) and uses a different vocabulary because the lifecycles differ. Possible values:

  • pending: the row was inserted at ingestion (mx_main) and has not yet completed the spam / filter / auth pipeline. Body and parsed fields are present; webhook delivery is not yet scheduled. Most rows transition out of pending within seconds.
  • accepted: the inbound passed the policy gates and is queued for webhook delivery. The webhook_status field tracks the separate webhook-delivery lifecycle from this point.
  • completed: terminal success. Webhook delivery attempted and acknowledged by every active endpoint, OR no endpoints are configured, so the row is durably archived.
  • rejected: terminal failure at ingestion (spam, blocked sender, filter rule, malformed). The body and metadata are stored for auditing but no webhook fires and the row is not repliable.

See also webhook_status (separate enum tracking the webhook-delivery state machine) and SentEmailStatus (the outbound vocabulary).

Filter inbound rows by lifecycle status. See EmailStatus for what each value means. Note that the webhook delivery state is a SEPARATE lifecycle on the same row; filter by webhook_status semantics is not currently supported on this endpoint.

searchstring

Search subject, sender, and recipient (case-insensitive)

date_fromstring date-time

Filter emails created on or after this timestamp. An ISO 8601 timestamp with Z or a numeric UTC offset (2026-10-02T00:00:00Z, 2026-10-02T00:00:00-04:00). An offset is read as the instant it names. A time with no zone, or a bare date, is rejected.

date_tostring date-time

Filter emails created on or before this timestamp. An ISO 8601 timestamp with Z or a numeric UTC offset (2026-10-02T00:00:00Z, 2026-10-02T00:00:00-04:00). An offset is read as the instant it names. A time with no zone, or a bare date, is rejected.

sincestring

Forward-tail cursor. Returns rows that became visible AFTER this cursor, oldest-first, so a caller can stream new inbound mail by re-passing the cursor from each response. Mutually exclusive with cursor (which pages history newest-first). Pass the meta.cursor from the previous since response; an empty page means caught up.

waitinteger

Long-poll: hold the request up to this many seconds waiting for new mail past since, returning as soon as any arrives (or an empty page when the wait elapses). Requires since. Omitted means no wait (returns immediately); the server treats an absent value as 0. NOT given an OpenAPI default on purpose: a default makes some generators (e.g. openapi-python-client) send wait=0 on every call, which then fails the wait requires since check for plain history listings.

awaiting'you' | 'them'

Only return emails whose awaiting has this value. awaiting=you lists mail waiting on your reply. Combines with every other filter and with both cursor and since. The filter (either value, here, on search and as the awaiting: search term) matches delivered mail only: emails whose status is rejected are never returned by it. A rejected email (for example one refused because the organization is over its storage limit) was never delivered, so nobody can answer it and it is awaiting no one; its own awaiting field still reports the stored value, usually you. Whose turn it is in this email's conversation. A send counts as a reply unless its status is gate_denied, agent_failed, canceled (queued and scheduled replies count: they are committed to go). For an email with a thread_id: them when the thread's latest counted outbound send (by send created_at, including any counted reply to this email itself) is at or after the thread's latest inbound message (by received_at); otherwise you. For an email with no thread_id: them when it has at least one counted reply (reply_count > 0 for an API key), otherwise you. It is a thread-level fact: every email in a thread carries the same value, and it reflects sends made by any credential in the organization, including other agent connections. It changes without a new inbound message, for example when a reply is sent, a queued reply fails, a scheduled reply is canceled, or a message is deleted, and is always current when read.

automated'true' | 'false'

Only return emails whose automated has this value. awaiting=you&automated=false lists mail from people that is waiting on your reply. Combines with every other filter and with both cursor and since. An inbound email is automated when any of these holds, decided once when it arrives: null_envelope_sender (an empty MAIL FROM, a bounce); no_identifiable_sender (no address in the envelope or From); own_address (a sender address is exactly one of the addresses the mail was delivered to; sharing a domain with the recipient is not enough, so a colleague or another agent in the organization is not automated); mailer_daemon (mailer-daemon@ or postmaster@ on any domain); auto_submitted (Auto-Submitted with any keyword but no, an empty one included); precedence (Precedence bulk, list, junk or auto_reply); list_unsubscribe (List-Unsubscribe present); list_id (List-Id present); auto_response_suppress (X-Auto-Response-Suppress asks for no automatic reply: All, OOF or AutoReply); failed_recipients (X-Failed-Recipients present, a bounce); report (a delivery, feedback or disposition report). Mail Primitive's own automatic responders decline to answer is always automated. The header rules only see headers captured at ingest, so older mail may lack them. This is loop and noise protection, not sender authentication.

recipientstring email

Exact case-insensitive delivered recipient mailbox, applied before pagination. Combines with existing filters and cursors; does not search message text.

exclude_fyi'true' | 'false'

With true, leave out emails whose fyi is true. Combines with every other filter and with both cursor and since.

Response

Paginated list of emails

successboolean required

Changes

    • info

      added the optional property /// to the response with the status

    • info

      removed the invalid_signup_code enum value from the / response property for the response status

    • info

      removed the invalid_signup_code enum value from the / response property for the response status