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:
| 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. A letter that is held (see below) also stays here. |
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. |
delivered | Delivered to the recipient. Terminal. |
undeliverable | The carrier could not deliver the letter. 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. |
A letter that was sent with autoSend: true never goes back to draft. Once
Dairo has handed it to production it moves forward only.
Test mode and held letters
Every letter carries providerMode. live means it will be printed and
posted. test means it is validated and priced but never printed, mailed, or
charged. A letter is in test mode when you sent it with dryRun: true or when
the account's print mode is test.
To learn the account's print mode before you send, read letters.printMode
from GET /v1/whoami (the MCP get_account_info tool with action: "whoami").
GET /v1/letters/requirements carries the same value as printMode.
A test-mode letter stops at processing. Its timeline gets one event that
says why:
A test-mode letter stays at processing permanently. It never advances, it
is never printed, posted or charged, and it does not expire. Cancel it to
close it out: POST /v1/letters/{id}/cancel, or the MCP send_letters tool
with action: "cancel". A dryRun: true send creates no letter at all.
A live letter that production holds back gets the same kind of event with
code: "hold".
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.
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.
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.
| 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 | Why the event happened, when known: the reason for an undeliverable event, or why a letter is held (test_mode, hold). 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. |
Order a letter's history by occurredAt, not the time you fetched it —
delivery updates can land slightly out of order.
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 and the event timeline
(the print partner reports no tracking numbers, so a letter carries none).
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 at your
endpoint to receive it. The same event is also kept in the durable
event stream, 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 a link to the letters page 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.
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 — track many letters together with a live per-status rollup.
- Pricing — what a letter costs before you send it.