Skip to main content
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 complaint guard. If the recipient previously reported your mail as spam, Dairo refuses the send with a 400 instead 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, the send is refused instead:
A complaint 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.