> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dairo.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Track delivery

> Follow every send by its id — read its current status, page its per-recipient delivery events, and react to bounces and complaints.

Every email you send leaves a timeline. Read a send's current status with the
id the send call returned, page its per-recipient delivery events, or have the
same signals pushed to your app as they happen.

## The lifecycle

A send's `status` tells you where it stands right now:

| Status      | Meaning                                                                                                                           |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `scheduled` | Accepted with a future `sendAt`; waiting to fire.                                                                                 |
| `queued`    | Waiting for delivery — the state a broadcast's per-recipient sends pass through. An immediate single send never returns `queued`. |
| `sent`      | The message left Dairo. Confirmation from the recipient's mail server arrives as a `Delivery` event.                              |
| `bounced`   | A terminal negative signal landed — a bounce stamps `bouncedAt`, a spam complaint stamps `complainedAt`.                          |
| `failed`    | The send could not be completed; `error` carries the reason. Includes a scheduled send that failed its fire-time re-check.        |
| `canceled`  | A scheduled send you canceled before it fired.                                                                                    |

Delivery confirmations do not change `status` — they are recorded as events and
advance the send's `lastEventType` and `lastEventAt` fields, so a `sent` message
with `lastEventType: "Delivery"` has been accepted by the recipient's mail
server.

## Read a send

Fetch a send with `GET /v1/messages/{messageId}` (scope `messages:read`), using
the `id` returned when you [sent it](/sending/sending-email).

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl https://api.dairo.app/v1/messages/msg_3fa85f64 \
    -H "Authorization: Bearer $DAIRO_API_KEY"
  ```

  ```ts title="TypeScript" theme={null}
  const message = await dairo.messages.get("msg_3fa85f64");
  console.log(message.status, message.lastEventType);
  ```

  ```python title="Python" theme={null}
  message = dairo.messages.get("msg_3fa85f64")
  print(message.status)
  ```

  ```bash title="CLI" theme={null}
  dairo outbound get msg_3fa85f64
  ```
</CodeGroup>

```json theme={null}
{
  "object": "message",
  "id": "msg_3fa85f64",
  "inboxId": "inbox_7c9e6679",
  "channel": "email",
  "from": "receipts@yourapp.com",
  "to": ["nobody@example.com"],
  "subject": "Your receipt",
  "status": "bounced",
  "sentAt": "2026-06-12T11:59:58Z",
  "providerMessageId": "msg_pm_9f8c2a1b4e7d",
  "lastEventType": "Bounce",
  "lastEventAt": "2026-06-12T12:00:00Z",
  "bouncedAt": "2026-06-12T12:00:00Z",
  "complainedAt": null,
  "createdAt": "2026-06-12T11:59:57Z"
}
```

## Page the delivery events

For the full timeline, list one send's delivery events with
`GET /v1/messages/{messageId}/events` — newest first, default 50, up to 100 per
request.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl "https://api.dairo.app/v1/messages/msg_3fa85f64/events?limit=50" \
    -H "Authorization: Bearer $DAIRO_API_KEY"
  ```

  ```ts title="TypeScript" theme={null}
  const { events } = await dairo.messages.listEvents("msg_3fa85f64", { limit: 50 });

  // Convenience filters over the same timeline:
  const bounces = await dairo.messages.listBounces("msg_3fa85f64");
  const complaints = await dairo.messages.listComplaints("msg_3fa85f64");
  ```

  ```python title="Python" theme={null}
  events = dairo.messages.list_events("msg_3fa85f64", limit=50)
  bounces = dairo.messages.list_bounces("msg_3fa85f64")
  ```

  ```bash title="CLI" theme={null}
  dairo outbound events --email-id msg_3fa85f64
  ```
</CodeGroup>

```json theme={null}
{
  "events": [
    { "eventId": "evt_9d2b", "messageId": "msg_3fa85f64", "type": "Bounce", "recipient": "nobody@example.com", "bounceType": "Permanent", "occurredAt": "2026-06-12T12:00:00Z" },
    { "eventId": "evt_5c1a", "messageId": "msg_3fa85f64", "type": "Send", "occurredAt": "2026-06-12T11:59:58Z" }
  ]
}
```

## Event fields

Each event says what happened, to whom, and when.

| Field                   | Meaning                                                                                                           |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `type`                  | The delivery event: `Send`, `Delivery`, `DeliveryDelay`, `Bounce`, `Complaint`, `Reject`, or `Rendering Failure`. |
| `recipient`             | The affected address.                                                                                             |
| `occurredAt`            | When the event happened at the recipient's mail server.                                                           |
| `bounceType`            | `Permanent`, `Transient`, or `Undetermined`.                                                                      |
| `bounceSubType`         | A finer classification, e.g. `General` or `MailboxFull`.                                                          |
| `diagnosticCode`        | The reason the receiving mail server gave for a bounce.                                                           |
| `complaintFeedbackType` | The kind of complaint, e.g. `abuse` or `fraud`.                                                                   |
| `agentHint`             | On bounce and complaint events, a short machine-readable recommendation for what to do next.                      |

<Tip>
  Events can arrive out of order. Sort by `occurredAt` — not by when you received
  each event — when you reconstruct a message's lifecycle.
</Tip>

## What to do with each signal

| Signal                                  | What it means                                                                    | What to do                                                                                                            |
| --------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `Bounce` with `bounceType: "Permanent"` | The address doesn't exist or refuses mail.                                       | Take it off your lists and don't retry — repeated hard bounces hurt your reputation and push future mail toward spam. |
| `Bounce` with `bounceType: "Transient"` | A full mailbox or a temporary server problem.                                    | Back off and retry later rather than giving up right away.                                                            |
| `Complaint`                             | The recipient marked your mail as spam — the strongest negative signal there is. | Stop contacting them. Dairo blocks future sends to that address for you.                                              |

## Get events pushed to you

Polling works, but for real-time updates subscribe to `message.delivered`,
`message.bounced`, and `message.complained` via [webhooks](/webhooks/webhooks),
and Dairo calls your app the moment each one lands. For a single, durable,
account-wide stream of everything, read the [event ledger](/events/event-ledger).

## Next steps

* [Land in the inbox](/webhooks/deliverability) — cut bounces and complaints at the source.
* [Audiences & broadcasts](/audiences/audiences) — track a fan-out per recipient.
* [API reference](/api-reference) — every messages endpoint, with copy-paste requests.
