<!-- Canonical: https://staging.startupmail.dev/docs/receiving -->
<!-- Documentation index: https://staging.startupmail.dev/llms.txt -->

# Receive mail and read threads

> Work with incoming messages, labels, complete threads, and real-time notifications.

## How receiving starts

Incoming mail reaches Startup Mail after the domain’s MX record is verified and a matching mailbox exists. You can optionally configure a domain catch-all mailbox, which receives mail sent to unconfigured addresses at that domain. The delivered message retains its original recipient address; catch-all does not create a new sending identity.

Configure it from **Settings → Domains → Catch-all routing**, or with an organization-scoped key:

```ts
await mail.setDomainCatchAll("dom_123", "mbx_support");
// Pass null to turn the route off.
```

## List threads

Use `GET /v1/threads` or `mail.listThreads()`. Omit `mailboxId` for a combined inbox across every mailbox the credential can read, or filter with a mailbox ID. Use one of these labels: `inbox`, `starred`, `sent`, `important`, or `spam`.

```ts
const threads = await mail.listThreads({
  mailboxId: "mbx_123",
  label: "inbox",
});
```

A thread summary includes its mailbox and delivered-to address as well as subject, snippet, sender, last-message time, unread and message counts, starred and important state, and whether it has attachments.

## Read a complete thread

Use `GET /v1/threads/:id` or `mail.getThread(threadId)`. The response contains messages in the conversation, including direction, delivery status, addresses, plain and cleaned HTML bodies, timestamps, and attachment metadata.

Access is checked when the thread is requested. Knowing a thread ID does not bypass private mailbox permissions.

## React without polling

Subscribe to `message.received` with a signed webhook. Its payload identifies the mailbox, thread, and message so your worker can fetch the complete thread using `mail:read` scope.

> **Fetch after the event**
>
> Treat a webhook as a durable notification, not a replacement for the API response. Use its IDs to
> retrieve the current thread and make your processing idempotent.
