Send a transactional email
Build a retry-safe sendReceipt function that sends an HTML receipt in one call and confirms delivery with webhooks.
Transactional email is the mail your product sends one person at a time —
receipts, password resets, order confirmations. This guide builds a single
sendReceipt(order) function you call from checkout. It sends an HTML receipt,
never double-sends when the request is retried, and reports delivery back so you
can mark the order fulfilled.
Verify a domain and create the inbox
Verify the domain you send from, then create the inbox the receipts come from. Both are one-time steps.
The DNS records to publish are covered in
Add and verify a domain. The code below expects an API key
with the messages:send scope in the DAIRO_API_KEY environment variable.
Send the receipt
One call sends the receipt. Two details keep it safe to run from checkout:
- An idempotency key tied to the order. A retry with the same key returns the original send instead of mailing a second receipt. See Retries and idempotency.
- A suppression guard. If the recipient previously reported your mail as
spam, or their address hard-bounced, Dairo refuses the send with a
400instead of letting it damage your sending reputation. Treat that as "stop emailing this person," not an error to retry.
An immediate send completes while the request is open, so the response is the
final outcome — sent with an id, or a precise error, never a "queued, maybe
later":
Store the returned id on the order record. It is how you match the delivery,
bounce, and complaint events that arrive later.
When the recipient has previously complained (or hard-bounced), the send is refused instead:
A suppression refusal means stop, not retry. Overriding it with ignoreComplaints
is a deliberate human decision, never an automation default —
Land in the inbox covers when that is appropriate.
Rich receipts with React
For anything beyond simple markup, send a
React Email component and Dairo renders it — no build
step or render dependency in your app. source accepts up to 64 KiB of
component code and props up to 32 KiB of JSON.
One send carries exactly one body — text, html, react, or a stored
template. Combining two in a single request returns a 400. If every receipt
shares one layout, save it as a template and send it by
reference instead of inlining the source each time.
Confirm delivery
Subscribe once to the delivery events and match each one back to its order by
messageId — no polling.
Handle the events inside a signature-verified endpoint. Build a webhook receiver is the complete endpoint this handler slots into:
If you would rather not run a receiver, poll instead:
dairo.messages.get(messageId) returns the send with its current status, and
dairo.messages.listEvents(messageId) returns its delivery timeline.
Next steps
- Send an email — every request field, plus scheduling, attachments, and contacts.
- Track delivery — the status lifecycle behind
the
message.*events. - Webhooks — every event type, header, and payload.
Build a webhook receiver
A signature-verified endpoint that rejects forged and replayed deliveries, deduplicates by event ID, and processes Dairo events asynchronously.
Build a support inbox agent
Wire an agent to a support inbox that reads incoming mail, replies when confident, and hands everything else to a human.