Transactional emails
A transactional email is sent by your app to one person, because of something they just did: a password reset, a sign-in link, a receipt, an invitation. It needs no list and has no unsubscribe link. It travels on its own stream, so it keeps flowing when marketing is paused.
Two ways to send
| Template | Raw content | |
|---|---|---|
| Content lives in | Kula: anyone on the team edits it in the dashboard | Your code |
| You send | The template alias and variables | The subject and the HTML or Markdown |
| Best for | Most emails | Emails your app already renders |
Create a template
Open Transactional › New template:
- a Name, internal;
- an Alias, the key your code sends as
template, such aspassword-reset; - the Subject, Preview text, sender and content, written in the editor.
Use data.* variables for the values your app sends: {{ data.reset_url }}, {{ data.order.total }}. In Sample data, type example values as JSON to fill the preview and test emails:
{
"name": "Ada",
"reset_url": "https://acme.com/reset?token=abc123"
}
If the recipient is also a contact of your space, {{ contact.first_name }} and the contact's attributes work too.
Draft and published versions
A template has a draft and a published version. The API always sends the published version: editing the draft does not change emails in production.
| Status | Meaning |
|---|---|
| Draft | Never published: the API refuses to send it |
| Published | The draft and the published version are the same |
| Unpublished changes | You edited the draft since the last publication |
Preview and Send a test use the draft. Click Publish to make the draft live, or Discard draft to go back to the published version.
AI assistants can write a template draft but never publish it: they ask a member, who clicks Approve and publish.
Send it from your app
Create an API key in Settings › API keys. A key with sending access is enough, and safer to store in an app. Then call POST /v1/emails:
curl https://api.kulamail.com/v1/emails \
-H "Authorization: Bearer $KULA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reset-8f6c2a1e" \
-d '{
"to": "ada@example.com",
"template": "password-reset",
"variables": {
"name": "Ada",
"reset_url": "https://acme.com/reset?token=abc123"
}
}'
The email is queued and leaves within seconds. The template page shows the same snippet, in cURL and Node, with your alias and sample data.
- Sender: the sender of the template, or the default sender of the space. Override it with
from, on a verified domain. - Idempotency: send an
Idempotency-Keyheader. If your app retries after a timeout, Kula returns the first email instead of sending a second one. - Batches: send up to 100 emails in one call with
POST /v1/emails/batch. - Tags: attach key/value
tagsto find the email in webhooks.
Every field and error: Emails API.
A separate stream
| Marketing | Transactional | |
|---|---|---|
| Priority in the queue | Normal | First |
| Paused on a reputation alert | Yes | No |
| Warm-up of a new domain | Applies | Skipped |
| Contact review | Applies | Skipped |
| Unsubscribed contacts | Skipped | Still receive it |
| Hard-bounced addresses | Skipped | Skipped |
A hard-bounced address is blocked on both streams: the email comes back with the status SUPPRESSED and is not sent.
Follow what happens
- Activity lists every email, with its status and events. Filter on the transactional stream.
- Each template page shows its sends, deliveries, opens and clicks over 30 days.
- Webhooks tell your app when an email is delivered, bounces, is opened or clicked.
Raw content you send (HTML or Markdown, without a template) is kept 30 days, then deleted.