---
title: "List sample emails without authentication"
method: GET
path: "/demo/emails"
tags: ["Demo"]
---

# List sample emails without authentication

`GET /demo/emails`

Public, no-auth demo. Returns synthetic email objects shaped exactly like the authenticated `GET /emails` response (same `EmailSummary` schema), so an arriving agent can read the data model before obtaining credentials. No real tenant data is exposed; every object is a fixed sample. Rate-limited per IP.

## Response `200`

Synthetic list of emails, same shape as the authenticated /emails response.

- object
  - `success` true, required
  - `demo` true, required
  - `note` string — Reminder that the data is synthetic.
  - `data` object[], required
    - `id` string, uuid, required
    - `message_id` string, nullable
    - `domain_id` string, uuid, nullable
    - `org_id` string, uuid, nullable
    - `status` 'pending' | 'accepted' | 'completed' | 'rejected', required — 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).
    - `sender` string, required — SMTP envelope sender (return-path) the inbound mail server accepted. For most legitimate mail this equals the bare address in the From header; for mailing lists, bounce handlers, and forwarders it is typically the bounce address rather than the human-visible sender. For the parsed From-header value (with display name handling and a sender-fallback when the header is unparseable), GET the email by id and use `from_email`.
    - `recipient` string, required
    - `subject` string, nullable
    - `domain` string, required
    - `spam_score` number, nullable
    - `created_at` string, date-time, required
    - `received_at` string, date-time, required
    - `raw_size_bytes` integer, nullable
    - `webhook_status` 'pending' | 'in_flight' | 'fired' | 'failed' | 'exhausted' | 'null', nullable — Webhook-delivery state for an inbound email. Tracks a SEPARATE lifecycle from the email's `status` field; the same row carries both. Possible values: - `pending`: ingestion is past `pending` (the email itself is `accepted`) but the webhook fan-out has not yet started for this row. - `in_flight`: at least one delivery attempt is in flight. - `fired`: terminal success. Every active endpoint acknowledged the delivery (or accepted it after retries). - `failed`: terminal partial-failure. At least one endpoint exhausted its retry budget; some endpoints may still have succeeded. - `exhausted`: terminal failure. Every endpoint exhausted its retry budget without success. - `null`: no endpoints configured, so no webhook lifecycle applies. Note that the value `pending` here does NOT mean the email is `pending`; it means the email is past ingestion but webhook delivery has not yet begun. Two overlapping uses of the word `pending` for distinct lifecycle phases.
    - `webhook_attempt_count` integer, required
    - `thread_id` string, uuid, nullable — Conversation thread this message belongs to. Fetch `/threads/{thread_id}` for the full ordered thread. NULL on messages received before threading was enabled.

## Other responses

- `429` — Rate limit exceeded

---

[API](https://skmtc.dev/primitive/apis/primitive-api.md) · [All operations](https://skmtc.dev/primitive/apis/primitive-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/primitive/primitive-api/revisions/c2299d28022c/schema)
