Guide
Build a Transactional Email Agent
A receipt, an order confirmation, a password-reset alert — transactional email is triggered by an event and has to arrive, exactly once, looking trustworthy. An agent account sends it without an SMTP relay or a third-party sender: every message is DKIM-signed on the managed domain, rate-capped by a workspace policy, and rendered from a stored template. This guide builds the sender, makes its sends idempotent so a retried event never double-charges a customer's inbox, and contains it so a bug can't flood anyone.
Written by Aaron de Mello Senior Engineering Manager
What is a transactional email agent?
A transactional email agent sends event-triggered mail — receipts, order confirmations, shipping updates, password-reset alerts — from a dedicated sender identity. Unlike marketing mail, each message is a direct consequence of one action a user took, so it has to arrive, arrive once, and look like it genuinely came from you. Built on an agent account, the sender is a managed identity that signs and delivers without any mail server of your own.
The two hard requirements are deliverability and exactly-once. A transactional message that lands in spam is a support ticket; a duplicate receipt is a confused customer. An agent account handles the first with DKIM signing on the managed domain, and this guide handles the second with an idempotency check in code, since the same event can fire twice when an upstream system retries.
Why send transactional mail from an agent account?
Transactional sending usually means standing up an SMTP relay or paying a third-party sender, then managing API keys, sender domains, and reputation. An agent account collapses that: every outbound message is signed with DKIM (RFC 6376) on the *.nylas.email domain that Nylas manages, so you get authenticated mail in one command with no DNS records to publish yourself.
It also gives you a control a raw relay doesn't: a policy on the workspace caps how much the sender can send. A bug that loops over your customer list can't turn into a million-message incident, because the daily send limit is enforced at the workspace layer, outside the sending code. That guardrail is the difference between a contained bug and a deliverability catastrophe.
How do I set up the sender?
Create the sender identity with one command. The nylas agent account create call returns a grant in under 2 seconds, ready to send. Use a name that reads correctly in a customer's inbox, like notifications@:
nylas agent account create notifications@yourapp.nylas.emailThe sender address comes from the active grant automatically, so a send needs no --from flag. For production branding on your own domain instead of *.nylas.email, configure a custom domain through the dashboard — the managed domain works out of the box for everything else.
How does the agent send a receipt?
A transactional message should render from a stored template, not a string built in code, so tone and layout stay identical across every send. The nylas email send command renders a hosted template by id and merges per-send data with --template-data:
nylas email send \
--to customer@example.com \
--subject "Your receipt for order #4821" \
--template-id tpl_receipt \
--template-data '{"order_id": "4821", "total": "USD 24.00"}'The --template-strict flag defaults to true, so a send fails loudly if the template references a variable you didn't supply — better a caught error than a receipt that reads "Hello {{name}}." For the template authoring workflow, see the email templates guide.
How do I make sends idempotent?
The same event can fire twice — a webhook retries, a job reruns — and a customer should never get two receipts for one order. There's no idempotency flag on the send command, so the dedupe lives in your code: key each send on the event id, record it in a sent-log, and skip the send if the key is already there. A small file is enough for a single worker; a shared store for many.
#!/usr/bin/env bash
set -euo pipefail
[ $# -ge 2 ] || { echo "usage: $0 <event_id> <to>" >&2; exit 1; }
event_id="$1" # e.g. the order/payment event id
to="$2"
sentlog="${SENTLOG:-./sent.log}"
# Already sent? Skip — this is the idempotency guard.
if grep -qxF "$event_id" "$sentlog" 2>/dev/null; then
echo "skip: $event_id already sent"
exit 0
fi
nylas email send --to "$to" \
--subject "Your receipt for order #$event_id" \
--template-id tpl_receipt \
--template-data "{\"order_id\": \"$event_id\"}"
# Record only after a successful send (set -e aborts before this on failure)
echo "$event_id" >> "$sentlog"Recording the key only after the send succeeds is the important detail: under set -e, a failed send aborts the script before the append, so the event stays un-recorded and a retry will send it. The opposite order — record then send — would drop a receipt on any send error. A flat file is enough to stop duplicate sends for a single worker once the key is recorded, but it isn't true exactly-once: a crash in the window between a successful send and the append can still re-send on retry. For stronger guarantees, swap the file for a durable atomic store — a database unique constraint or a Redis SETNX on the event id. This is the same idempotency discipline covered in Build Reliable Email Automation, applied to a transactional sender.
How do I rate-limit and contain the sender?
A transactional sender's worst failure mode is volume: a loop that sends to every customer. Cap it at the workspace with a policy daily send limit, so the platform refuses the excess before it ships — a hard ceiling the sending code can't exceed even with a bug. Create the policy once and apply it to the sender:
nylas agent policy create --data '{
"name": "Transactional sender cap",
"limits": { "limit_count_daily_message_per_grant": 5000 }
}'Set the cap above your real peak but below a runaway — if you send 1,200 receipts on a busy day, a 5,000 ceiling absorbs spikes while still stopping an infinite loop cold. Pair it with an outbound rule that blocks any unexpected recipient domain. The full policy and rule reference is in Agent Rules and Policies, and the containment pattern is in Stop Your AI Agent From Going Rogue.
How do I schedule sends and should I track them?
Some transactional mail is better delayed — a "your trial ends tomorrow" notice, a follow-up an hour after signup. The --schedule flag accepts a relative or absolute time, so the agent queues the send instead of firing it now:
nylas email send \
--to customer@example.com \
--subject "Your trial ends tomorrow" \
--template-id tpl_trial_ending \
--schedule "tomorrow 9am"On tracking: the --track-opens flag exists, but think before using it on transactional mail. Open tracking embeds a pixel, which can trip spam filters and raises privacy questions on messages a user expects to be purely functional. Reserve tracking for mail where engagement is the point, and keep receipts and confirmations clean — the deliverability win usually beats the analytics.
Transactional agent account vs an SMTP relay
A transactional agent account and an SMTP relay both send mail, but the operational surface differs. A relay is a server or a third-party account you authenticate to and manage; an agent account is a managed identity you provision with one command. For an agent or a small service, the second removes most of the moving parts.
| Concern | SMTP relay / third-party sender | Transactional agent account |
|---|---|---|
| Setup | Account, API key, sender domain, DNS records | One CLI command |
| Authentication | Publish and rotate your own DKIM and SPF | DKIM-signed on the managed domain |
| Rate limiting | Your own throttle, or a plan tier | Workspace policy daily cap |
| Inbound replies | Separate inbound setup | Same grant also receives |
The last row is the quiet advantage: the same grant that sends a receipt also receives the customer's reply, so a "reply to this email" flow works without a second system. When you need a high-volume marketing relay, a dedicated provider still fits; for an agent's transactional mail, the account it already has is enough.
Next steps
- Getting Started with Agent Accounts — the architecture behind the sender, DKIM, and the workspace policy
- Email Templates from the CLI — author and version the hosted templates this sender renders
- Build an Email Digest Agent — the same sender patterns for a scheduled opt-in digest to a subscriber group
- Custom Domains for Agent Accounts — send receipts from your own brand domain instead of *.nylas.email
- Build Reliable Email Automation — exit codes, retries, and idempotency for unattended sending
- Agent Rules and Policies — the send caps and outbound rules that contain the sender
- Manage the Agent Account Lifecycle — rotate, pause, and retire the sender identity
- Full command reference — every
nylas email sendandnylas agentflag - Nylas v3 API documentation — the transactional send API behind these commands