--- name: pombo-transactional description: Wire up transactional email in Pombo. Create a keyed template, then send it by API call, by event binding, or by agent, and test it. Use for must-send mail tied to an action such as receipts, confirmations, password resets, and shipping notices. --- # Transactional email in Pombo Transactional mail is the must-send kind: a receipt, a confirmation, a notice. It reaches contacts who unsubscribed from marketing, because a receipt is not marketing. Hard-bounced and complained addresses are still refused. It counts toward the weekly send cap. Run `pombo-setup` first. A transactional send still needs a verified sending domain and an approved account. ## 1. Create the template `create_template` with: - `category: "transactional"` - a `key`, lowercase, digits, underscores, unique in the organization: `donation_receipt`, `password_reset`, `order_shipped` - a subject, and a body with merge tags for the variable parts: `Thank you for your {{ amount }} gift.` or `Your order {{ order_number }} shipped.` `validate_template`, then `preview_template` to see it with `[tag]` placeholders where variables go. ## 2. Choose how it sends ### a. By API call, one send from your backend ``` POST /api/v1/transactional_emails scope: transactional:send Authorization: Bearer { "template": "donation_receipt", "to": "dana@example.com", "from": "receipts@mail.acme.org", "variables": { "amount": "$50", "receipt_number": "1042" } } ``` - `to` does not need to be a contact. - `from` must be an address on a verified sending domain. - `202` means queued to the outbox, not delivered. An unapproved account or an over-cap week still refuses it at the outbox. - Create the key with `create_api_key` (scope `transactional:send`), store it server-side, `revoke_api_key` if it leaks. ### b. Bound to an event, no API call per send Give the template a trigger event name and a from address. Every `POST /api/v1/events` with that name sends the email once, to the event's contact, with the payload available as `{{ event.* }}`. One template per event name. A sequence can bind to the same event and both will run. Use this for the receipt, the sequence for follow-up. ### c. By agent, a one-off `send_transactional_email` with `template_key`, `to`, `from`, and `variables`. Same rules as the API call. Useful when you are doing a single send on the user's behalf, not wiring a system. ## 3. Add the call to the backend (for path a) - Fire it after the state change commits (after-commit hook or queued job), not inline in the request. - Make retries safe. A network retry that re-posts sends the receipt twice. Dedupe on your side, or accept the rare double. - A 5xx from Pombo must not fail the user's action. Log and move on. The outbox, not the request path, is the source of truth. ## 4. Test - `send_transactional_email` (or a real API call) to an address inside the organization, with representative `variables`. - Confirm every merge tag resolved. A leftover `{{ order_number }}` in a real inbox is the classic transactional bug — but also test with an optional variable left blank: a value that's present but empty renders as nothing rather than `[tag]`, so check `punctuation_warnings` on the send_test result for copy that reads broken without one (or wrap the optional part in `{% if %}`). - `list_recent_sends` to see it with its delivery status. ## Done when The keyed template exists, the backend (or event binding) sends it on the real action, retries are safe, and a test send arrived with every variable filled in.