Skip to main content
Transactional emails send one real-time message, such as a password reset or an order confirmation. They reach unsubscribed contacts because they carry essential account information, and they skip the unsubscribe footer. A suppressed address, such as one that hard-bounced, is still skipped. You build the automation in Brew, in the app or from an agent. Your product’s code fires it, once per reset or receipt. The code half is at the end of this page.

The Domain Decides

In Brew, a transactional email is an automation. There is no separate transactional email object to create. What makes an automation transactional is the sending domain. A domain whose purpose is Transactional removes the unsubscribe requirement and delivers to unsubscribed contacts. The same automation on a marketing-purpose domain is a marketing send that respects unsubscribes. You pick the class by picking the domain. Automations on marketing-purpose domains still cover the marketing side of event-driven email: welcome flows, onboarding, abandoned cart, win-back. Those respect unsubscribes, which is what you want for marketing, and not what you want for a password reset. That is why a reset flow’s send step belongs on a transactional-purpose domain.

Set It Up

These steps build a transactional flow. Build an Automation covers flow building in general.
1

Add a transactional domain

Add a sending domain with the Transactional purpose in the Brew app. The MCP create_domain tool adds marketing domains only; the API can add either, see From an Agent or Code.Verify Your Sending Domain covers the DNS records and both purposes. Run marketing and transactional mail on separate subdomains.
2

Create the trigger

Open Automations, click Triggers, then New custom trigger, or use a trigger an integration provisions. Declare the payload fields the email needs, mark required fields, and include a required email field for routing. Then click Create trigger. Custom Events is the reference for payload contracts.
3

Build the automation

Build a one-step automation on the trigger. Open its Send Email node and pick a verified transactional-purpose domain.
4

Test it

Open the flow and click Test automation. Enter your address under Send test to and fill in the trigger payload, such as the reset link.Click Run test. Brew sends every email to the test address only. Over MCP, test_automation with a testRecipient runs it to one address.
5

Publish it

Click Publish in the canvas header to make the flow Live. A fire before any attached automation is published returns 422 NO_PUBLISHED_AUTOMATION.
6

Fire it from your product

Your backend sends the event with the moment’s data, such as the reset link or the order. The email reads those fields as {{ trigger.* }} variables, and the email field picks the contact. The request shape is in From an Agent or Code.
Marketing consent is not the test for transactional mail. The test is whether the email genuinely answers something the person did. See What You Can Send.

What to Know Before You Ship One

  • Suppressed addresses are skipped. A fire to a hard-bounced address starts no run, and the events feed records why.
  • A fire for a new address creates a contact. It’s subscribed, like a contact added with only an email. Send that person marketing only if they opted in.
  • Keep one-time codes off the contact. Nest them in the payload, such as { "verification": { "code": "482913" } }, and write {{ trigger.verification.code }}. A nested value renders but isn’t saved on the contact.
  • The subject line is stored. The events feed keeps each email’s subject, so put a code in the body, not the subject.
  • Tracking still applies. Click and open tracking follow the domain’s settings, so links go through the tracking domain and an open pixel is added.
  • Retries don’t send twice. Your backend sends a stable key with each event, such as the reset request’s ID, and a retry with the same key returns the original runs instead of sending again. The header is in From an Agent or Code.
  • Keep codes and reset links out of storage. Mark those fields as secret in the trigger’s payload schema. Brew then uses the value only to render the email and never stores it on the contact, in logs, or in analytics. See From an Agent or Code for the flag.
  • Encode values inside links. A token with + or & breaks in a raw link. Write {{ trigger.reset.token | url_encode }}, or pass the whole link already encoded. See Merge Tags.
  • MCP won’t fire to a contact who opted out of marketing. The email itself has no unsubscribe link, but an agent still can’t send it to that contact. fire_trigger_event refuses with an unsubscribed-recipient error even for a transactional automation. Fire receipts and resets from your backend over the API.

Change a Domain’s Purpose

Change a domain’s purpose from its page in the app, or over the API. Published automations on that domain follow the new purpose on their next send. After a switch to transactional, they send with no unsubscribe link and reach unsubscribed contacts. Emails can’t send from a transactional domain. If one still points at the domain, it’s blocked at send time.

From an Agent or Code

Add a transactional domain. POST /v1/domains with sendingPurpose: "transactional". MCP’s create_domain adds marketing domains only. Fire the trigger. Send a JSON payload (nested objects and arrays supported). The email reads its fields as {{ trigger.* }}, and email is the contact the run is for:
The email design references {{ trigger.resetUrl }}. Merge Tags covers the variable syntax.
  • Idempotency. Send a stable Idempotency-Key per event, such as the reset request’s ID. Idempotency covers replays and conflicts.
  • Suppressed address. The fire answers 202 with counts.skipped: 1 and starts no run.
  • Secret fields. In the trigger’s payload schema, set secret: true on codes and reset links. pii: "high" on its own only masks a value in execution logs and the inbound log. See Typed Payload Contracts.
  • Over MCP. list_triggers reads the trigger, check_trigger runs a ready check without firing, and fire_trigger_event fires it.
  • Opted-out recipient over MCP. fire_trigger_event answers RECIPIENT_UNSUBSCRIBED.
  • Change a domain’s purpose. PATCH /v1/domains/{domainId} with sendingPurpose.

Need Help?

Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:

Search Documentation

Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.

Ask ChatGPT or Claude

Click here to open the Copy page menu at the top of this page, then send the page to ChatGPT, Claude, or your editor.