> ## 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.

# Status & tracking

> Follow a letter from queued to delivered — the status lifecycle, the event timeline, webhook events, and milestone emails.

Every letter leaves a trail. Dairo tracks each letter through printing,
posting, and delivery, and keeps its `status` and event timeline current —
there is no carrier feed for you to integrate. Read the letter back by `id`,
subscribe to a webhook event, or let Dairo email you at each milestone.

## The lifecycle

A letter's `status` is a stable enum you can build on. The happy path runs:

```text theme={null}
queued → processing → printable → submitted → in_transit → delivered
                                                         └→ undeliverable
```

| Status          | Meaning                                                                                                                                        |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`         | Created with `autoSend: false`. Stored but held — nothing is printed or posted. A draft can be canceled freely.                                |
| `queued`        | Dairo accepted the letter and is preparing it for print. The status a normal `autoSend: true` create returns.                                  |
| `processing`    | The document is being validated and made ready for production.                                                                                 |
| `printable`     | Validated and ready to print.                                                                                                                  |
| `submitted`     | Handed off for printing and posting. `submittedAt` is set.                                                                                     |
| `in_transit`    | Printed, enclosed, and posted — the letter is moving through the postal network. `trackingNumber` is set when the delivery class provides one. |
| `delivered`     | Delivered to the recipient. Terminal.                                                                                                          |
| `undeliverable` | The carrier could not deliver the letter. `undeliverableReason` carries the reason when known. Terminal.                                       |
| `canceled`      | You canceled the letter before dispatch. `canceledAt` is set. Terminal.                                                                        |
| `failed`        | Validation or production failed before posting; the letter's `error` field says why. Terminal.                                                 |

An `undeliverable` letter cannot be re-routed — correct the address and create
a new letter. A `failed` letter was never posted; fix the document and create
it again.

<Warning>
  Cancellation is only possible before dispatch — while a letter is `draft`,
  `queued`, `processing`, `printable`, or `submitted`. Once it is `in_transit`
  it cannot be recalled; a late cancel returns `409` with
  `code: "letter_not_cancelable"`. The cancel call itself is on
  [Send a letter](/letters/sending-a-letter).
</Warning>

## The event timeline

Alongside its `status`, each letter carries a list of `letter_event` objects —
the detailed delivery timeline. The timeline comes inlined on
`GET /v1/letters/{id}` (newest first, up to 100 events) or on its own from
`GET /v1/letters/{id}/events`.

```json theme={null}
{
  "object": "letter_event",
  "eventId": "lev_2",
  "letterId": "let_01HZX...",
  "type": "in_transit",
  "code": "handed_to_carrier",
  "description": "Handed to postal carrier",
  "location": null,
  "reason": null,
  "hasImage": false,
  "occurredAt": "2026-06-23T08:00:00Z",
  "recordedAt": "2026-06-23T08:00:11Z"
}
```

| Field         | Meaning                                                                                                                                                               |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`        | The lifecycle stage the event reports, in the same vocabulary as the letter `status` — for example `submitted`, `in_transit`, `delivered`, `undeliverable`, `failed`. |
| `code`        | A machine-readable detail code, when the event carries one; `null` otherwise.                                                                                         |
| `description` | A short human label for the event.                                                                                                                                    |
| `producer`    | The source that reported the event, when known; `null` otherwise.                                                                                                     |
| `location`    | Where the event was recorded, when known; `null` otherwise.                                                                                                           |
| `reason`      | The reason for an `undeliverable` event, when known; `null` otherwise.                                                                                                |
| `hasImage`    | Whether a scan or image is associated with the event.                                                                                                                 |
| `occurredAt`  | When the event happened in the field. Order the timeline by this.                                                                                                     |
| `recordedAt`  | When Dairo recorded the event.                                                                                                                                        |

<Tip>
  Order a letter's history by `occurredAt`, not the time you fetched it —
  delivery updates can land slightly out of order.
</Tip>

## Track a letter

**Poll it.** The most direct way to follow a letter is to fetch it by `id`:
`GET /v1/letters/{id}` returns the current `status`, the `trackingNumber` once
a tracked class provides one, and the event timeline. Fetching an in-flight
letter refreshes its tracking state, so a poll returns the latest known
status.

**Subscribe to it.** Every status change emits a `letter.status_changed`
event carrying the `letterId`, the new `status`, and the `batchId` when the
letter belongs to a batch. Point a [webhook](/webhooks/webhooks) at your
endpoint to receive it. The same event is also kept in the durable
[event stream](/events/event-ledger), alongside everything else happening in
your account. These events fire regardless of the email `notifications`
setting below.

## Owner email notifications

Dairo can also email the account owner at each delivery milestone — no code,
no endpoint. When a letter reaches a milestone, the owner gets a short status
email ("Your letter to Zürich, CH is on its way"), with the tracking number
when there is one and a link to the letter's timeline in the dashboard.

Milestone emails are on by default. Turn them off for a single letter with
`notifications: false` — useful for high-volume sends where you would rather
watch the timeline than receive an email per milestone. Batches accept the
same flag once for the whole run.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl -X POST https://api.dairo.app/v1/letters \
    -H "Authorization: Bearer $DAIRO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "pdfBase64": "JVBERi0xLjQK...",
      "fileName": "invoice-2026-06.pdf",
      "to": {
        "name": "Jane Doe",
        "street": "Hauptstrasse", "houseNumber": "12",
        "postalCode": "8001", "city": "Zürich", "country": "CH"
      },
      "delivery": "economy",
      "notifications": false
    }'
  ```

  ```ts title="TypeScript" theme={null}
  const letter = await dairo.letters.create({
    pdfBase64: pdf.toString("base64"),
    fileName: "invoice-2026-06.pdf",
    to: { name: "Jane Doe", street: "Hauptstrasse", houseNumber: "12", postalCode: "8001", city: "Zürich", country: "CH" },
    delivery: "economy",
    notifications: false, // no milestone emails for this letter
  });
  ```

  ```python title="Python" theme={null}
  letter = dairo.letters.create(
      pdf_base64=base64.b64encode(pdf).decode(),
      file_name="invoice-2026-06.pdf",
      to={"name": "Jane Doe", "street": "Hauptstrasse", "house_number": "12", "postal_code": "8001", "city": "Zürich", "country": "CH"},
      delivery="economy",
      notifications=False,  # no milestone emails for this letter
  )
  ```
</CodeGroup>

### When a notification email is sent

An email goes out only when the status changes into one of the delivery
milestones — once per transition, never for a re-confirmed update, and never
for internal churn like `queued` or `processing`.

| Status reached  | Notification                                             |
| --------------- | -------------------------------------------------------- |
| `submitted`     | "…has been handed off for printing and posting."         |
| `in_transit`    | "…is on its way."                                        |
| `delivered`     | "…was delivered."                                        |
| `undeliverable` | "…could not be delivered" (with the reason, when known). |
| `failed`        | "…could not be sent."                                    |

The remaining statuses — `draft`, `queued`, `processing`, `printable`, and
`canceled` — never trigger an email.

## Next steps

* [Send a batch](/letters/batches) — track many letters together with a live per-status rollup.
* [Pricing](/letters/pricing) — what a letter costs before you send it.
