Lesson 3 8 min read Completed
Transactional email
Send password resets, login codes and receipts through the Broadcast API, with templates, safe retries and the right unsubscribe rules.
Transactional email is the mail your app sends to one person because of something they did: a password reset, a login code, a receipt. In this lesson you send one through the API, keep the content in a template, make retries safe, and learn how it differs from marketing email.
How it differs from marketing
Broadcasts and sequences respect unsubscribe status. Transactional email does not. A password reset has to arrive whether or not the person wants your newsletter. Broadcast delivers transactional email even to someone who unsubscribed.
That power comes with a duty. Send only real service messages this way. Using the transactional route for promotions is how sending reputations get damaged.
A few addresses are still blocked. Broadcast does not send to a redacted subscriber or to an address on the global suppression list. By default, a hard bounce on the channel’s own suppression list blocks marketing only. To block transactional mail too, turn on Hard bounces also block transactional email in the channel settings.
A transactional email carries no unsubscribe link and no List-Unsubscribe header, unless you send it with include_unsubscribe_link: true.
Broadcasts and sequences
- Respect unsubscribe status
- Carry an unsubscribe link
- Sent to a list
Transactional email
- Reaches people who unsubscribed
- No unsubscribe link by default
- Sent to one person, through the API
Step 1: Get a token
Every channel starts with an access token that has transactional permissions. Find it under Access Tokens. Or create one with only Transactional Emails read and write, so a leak cannot touch your campaigns.
Before you write code, open Transactional in the sidebar. The Try it now card sends a test to your own address and shows a ready-to-copy cURL command.

Step 2: Send one
curl -X POST https://broadcast.example.com/api/v1/transactionals.json \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "to": "ada@example.com", "subject": "Your sign-in code", "body": "Your code is 123456. It expires in 10 minutes.", "reply_to": "support@example.com" }'
to is required. subject and body are required unless you use a template. The body can be plain text or HTML, and Broadcast wraps it in the HTML document for you.
The response includes an id and a status_url. GET /api/v1/transactionals/:id.json returns the current state. If the recipient’s address matches a subscriber, the email is linked to that subscriber’s record.
Step 3: Move the content into a template
Email copy in application code is hard to change. Put it in a Broadcast template instead, and send it by id:
{ "to": "ada@example.com", "template_id": 123 }
The template’s subject, body and preheader are the defaults, and any subject or body you send overrides them. The template must belong to the token’s channel. Now a marketer can fix a typo in the receipt without a deploy.
To send from a different From address, pass sender_identity_id. The email marketing course explains sender identities.
Step 4: Make retries safe
Networks fail. If your client times out and retries, the person may get two receipts. Prevent that with an Idempotency-Key header, unique per logical send:
Idempotency-Key: order-12345-receipt
The rules:
- The first request with a key is processed and its response is stored.
- A retry with the same key and the same body does not send again. Broadcast replays the stored response with
Idempotency-Replayed: true. - The same key with a different body fails with
422. - A retry while the first is still processing gets
409. Wait and retry.
Keys are scoped to the token and expire after 24 hours.
Same key, same body
- Nothing is sent again
- Stored response comes back
Idempotency-Replayed: true
Same key, different body
- Fails with
422
First request still processing
- Gets
409 - Wait and retry
Statuses, and when mail waits
A transactional email moves through Pending, Queued and Sent, or ends as Failed. The Transactional page lists every send, with filters by recipient and status, and lets you resend one.
- Pending
- Queued
- Sent

Transactional email has the highest priority in Broadcast, and one worker thread is kept for it alone. But it still respects the email server’s hourly limit. If the server is full, the email stays Queued and Broadcast retries every minute. After 30 minutes, every page of the channel shows a notice. After 24 hours it is marked Failed: “Email server at hourly limit: no room for 24 hours, giving up”.
The fix is in your setup, not your code. Send transactional email through its own email server, one that does not send broadcasts. Then a large newsletter can never delay a login code. See How sending works.
Double opt-in from your own email
If you want your own confirmation email design, send a transactional with double_opt_in: true and put {{ confirmation_url }} in the body. Broadcast creates the confirmation and fills in the link. The Transactional Email API lists the other Liquid variables, such as {{ first_name }}.
What you learned
- Transactional email bypasses unsubscribe status. Use it only for real service messages.
POST /api/v1/transactionals.jsonwithto, andsubjectandbodyor atemplate_id.- An
Idempotency-Keymakes retries safe for 24 hours. - Give transactional email its own email server, so broadcasts never delay it.
Go deeper in the docs
Check what you learned
PassedAnswer all 3 questions correctly to complete this lesson.