Transactional email
Send an email from a template
Save your email design once. Pass JSON attributes to fill in the subject, HTML, and plain text for each recipient.
1. Save and preview your template
In Templates, create a template with variables such as {{first_name}} and {{order.number}}. Save it, then choose Preview with data. Replace the sample JSON values, enter a subject, and choose Render preview.
<h1>Thanks, {{first_name}}!</h1>
<p>Order {{order.number}} is on its way.</p>
The preview uses the saved template. Save edits before previewing again. To use a shared starter template, customize and save a copy in your workspace first.
2. Send from your application
Create an API key in Settings → API Keys. Copy the template ID or request body from your preview. Call this endpoint from your server; keep the API key out of browser code.
POST /api/v1/transactional/send
curl https://app.getread.com/api/v1/transactional/send \
-H "Authorization: Bearer $GETREAD_API_KEY" \
-H "Idempotency-Key: order-9876-shipped" \
-H "Content-Type: application/json" \
-d '{
"to": "customer@example.com",
"template_id": "YOUR_SAVED_TEMPLATE_ID",
"subject": "Order {{order.number}} has shipped",
"attributes": {
"first_name": "Sam",
"order": { "number": "9876" }
}
}'
Replace YOUR_SAVED_TEMPLATE_ID with your template’s UUID and the recipient with your customer’s email address. The sender defaults to your workspace sender; set from_email to use another verified sender.
Recipients must already have a registered delivery identity in getread. Preview does not register a recipient or change their eligibility. Use an eligible address when testing delivery.
Preview through the API
POST /api/v1/transactional/preview
Use the same API key and send template_id, subject, and attributes. Omit recipient and sender fields. Preview returns the rendered parts without sending an email, creating send history, or reserving sending quota.
{
"template_id": "YOUR_SAVED_TEMPLATE_ID",
"subject": "Order {{order.number}} has shipped",
"attributes": {
"first_name": "Sam",
"order": { "number": "9876" }
}
}
Response 200 OK:
{
"subject": "Order 9876 has shipped",
"html": "<h1>Thanks, Sam!</h1>\n<p>Order 9876 is on its way.</p>",
"text": ""
}
Empty text means the template has no authored plain-text part. HTML-only emails get an automatic text alternative at delivery. A successful preview checks rendering; recipient, sender, quota, and sending-policy checks still apply when sending.
How variables work
- Use
{{name}}or nested paths such as{{order.number}}. Spaces inside the braces are allowed. Each path segment starts with an ASCII letter or underscore, followed by letters, digits, or underscores. Paths are limited to 128 characters. - Referenced values must be strings, numbers, or booleans. Empty strings,
0, andfalseare valid. Missing values and referenced nulls, objects, or arrays return400. - Attributes must be a JSON object, up to 16 KiB. Extra attributes are allowed. They do not change contacts or the saved template. In the UI, use strings for large numeric IDs to keep their exact value.
- HTML values are escaped by context, including attribute and URL values. Unsafe URL schemes become inert. Subject and plain-text values are inserted as written. Values are never evaluated as template code.
- There are no helpers, loops, raw HTML insertion, or automatic campaign fields. Supply every variable yourself. Outlook conditional comments are unsupported; ordinary HTML comments are omitted.
- Subject source and rendered subject are limited to 998 UTF-8 bytes. Saved bodies together, and rendered bodies together, are limited to 256 KiB.
Send request fields
| Field | Type | Use |
|---|---|---|
to | string | Required recipient email. |
subject | string | Required. Supports variables with a template. |
template_id | UUID string | A saved template in the API key’s workspace. Use instead of raw bodies. |
attributes | JSON object | Values for template variables. Requires template_id. |
html, text | string | Alternative to a template. Supply either or both; omit both with template_id. Raw bodies are sent literally. |
from_email, from_name | string | Optional sender address and display name. The address must use a configured sending domain in the API key’s workspace. |
reply_to | string | Optional reply address. |
tags | object | Optional string-to-string metadata. |
analytics | boolean | Optional open tracking, off by default. Requires HTML. |
recipient_timezone | string | Optional IANA time zone, such as America/Chicago. Requires analytics. |
Unknown fields and duplicate JSON keys are rejected. Authentication uses the Authorization: Bearer header. Idempotency uses the Idempotency-Key header, never a body field.
Retries and errors
If a send request times out, retry it with the same Idempotency-Key to avoid sending a second email. Use a unique Idempotency-Key for each intended email, up to 128 printable non-whitespace ASCII characters. Retry with that key and the same rendered email to receive the existing send record. Changing the rendered subject or body under the same key returns 409 Conflict.
The rendered email is saved when it enters the queue. Later template edits do not change queued mail, but can change the result of a retry. Keep the saved template unchanged while retrying. Idempotency compares rendered content, so changing unused attributes alone does not create a different email.
| Status | Meaning |
|---|---|
| 400 | Invalid JSON, fields, variables, or message size. Correct the request before retrying. |
| 401 | Missing or invalid API key. Sending to an unregistered recipient also returns this status; check recipient eligibility if the key is valid. |
| 403 | The workspace cannot send this email. |
| 404 | The template does not exist in this workspace, or is a shared starter. |
| 409 | The idempotency key was used with different rendered content. |
| 413 | The JSON request body exceeds 576 KiB. |
| 429 | Rate limit reached. Send and preview each allow 100 requests per minute per API key. Wait for the Retry-After interval. |
| 422 | The requested from_email is not configured for the API key’s workspace. |
| 503 | Temporarily unavailable. Retry sends with the same idempotency key. |