Templates
One row per template, addressed by the key its author gave the file. A template has versions and nothing else, no switch, because a send resolves the latest version that finished compiling at the moment it sends, so fixing copy reaches runs already in flight. preview renders that version with whatever props you pass, in the sandbox, and answers the subject, HTML and text as they would go out; test-send renders the same way and sends the result to your own address through the organization’s default sender, which walks no consent gate, counts against nothing, and is logged as a test rather than as mail to a recipient. Deleting a key deletes every version of it and is refused while a journey still sends it, naming the journeys. Templates arrive with a push, so there is no create and no update here.
GET /v1/templates
Section titled “GET /v1/templates”List the organization’s email templates
Auth: Session token.
Query parameters
{ "cursor": "string", "limit": 50, "q": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
cursor |
string | no | |
limit |
integer | no | default 50; 1 ≤ x ≤ 100 |
q |
string | no | length 0–200 |
Response 200
{ "data": [ { "key": "string", "appId": "string", "tags": {}, "sendClass": "marketing", "verifyLink": true, "unsubscribeLink": false, "propsSchema": { "key": {} }, "latestVersion": { "id": "string", "status": "compiling", "moduleDigest": "string", "pushId": "string", "appId": "string", "error": "string", "pushedAt": "2026-01-15T09:30:00.000Z" }, "journeys": [ "string" ] } ], "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object[] | yes | |
data[] |
object | yes | |
data[].key |
string | yes | length 0–64 |
data[].appId |
string | yes | |
data[].tags |
any | yes | |
data[].sendClass |
“marketing” | “transactional” | yes | |
data[].verifyLink |
boolean | yes | |
data[].unsubscribeLink |
boolean | yes | default false |
data[].propsSchema |
record<string, any> | yes | |
data[].latestVersion |
object | yes | |
data[].latestVersion.id |
string | yes | |
data[].latestVersion.status |
“compiling” | “ready” | “failed” | yes | |
data[].latestVersion.moduleDigest |
string | null | yes | |
data[].latestVersion.pushId |
string | yes | |
data[].latestVersion.appId |
string | yes | |
data[].latestVersion.error |
string | null | yes | |
data[].latestVersion.pushedAt |
string (date-time) | yes | |
data[].journeys |
string[] | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no |
Errors follow the shared envelope and code table.
GET /v1/templates/{key}
Section titled “GET /v1/templates/{key}”Inspect one email template
Auth: Session token.
Response 200
{ "data": { "key": "string", "appId": "string", "tags": {}, "sendClass": "marketing", "verifyLink": true, "unsubscribeLink": false, "propsSchema": { "key": {} }, "latestVersion": { "id": "string", "status": "compiling", "moduleDigest": "string", "pushId": "string", "appId": "string", "error": "string", "pushedAt": "2026-01-15T09:30:00.000Z" }, "journeys": [ "string" ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.key |
string | yes | length 0–64 |
data.appId |
string | yes | |
data.tags |
any | yes | |
data.sendClass |
“marketing” | “transactional” | yes | |
data.verifyLink |
boolean | yes | |
data.unsubscribeLink |
boolean | yes | default false |
data.propsSchema |
record<string, any> | yes | |
data.latestVersion |
object | yes | |
data.latestVersion.id |
string | yes | |
data.latestVersion.status |
“compiling” | “ready” | “failed” | yes | |
data.latestVersion.moduleDigest |
string | null | yes | |
data.latestVersion.pushId |
string | yes | |
data.latestVersion.appId |
string | yes | |
data.latestVersion.error |
string | null | yes | |
data.latestVersion.pushedAt |
string (date-time) | yes | |
data.journeys |
string[] | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no |
Errors follow the shared envelope and code table.
POST /v1/templates/{key}/preview
Section titled “POST /v1/templates/{key}/preview”Render a template with the props you pass
Auth: Session token.
Request body
{ "data": { "props": {} }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.props |
record<string, any> | no | default {} |
Response 200
{ "data": { "subject": "string", "html": "string", "text": "string", "versionId": "string" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.subject |
string | yes | |
data.html |
string | yes | |
data.text |
string | yes | |
data.versionId |
string | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no |
Errors follow the shared envelope and code table.
POST /v1/templates/{key}/test-send
Section titled “POST /v1/templates/{key}/test-send”Send a rendered template to your own address
Auth: Session token.
Request body
{ "data": { "props": {} }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.props |
record<string, any> | no | default {} |
Response 201
{ "data": { "id": "string", "orgId": "string", "profileId": "string", "journey": "string", "executionId": "string", "kind": "journey", "step": "string", "channel": "email", "status": "sent", "dryRun": true, "recipient": "string", "providerId": "string", "error": "string", "createdAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "sentAt": "2026-01-15T09:30:00.000Z", "settledAt": "2026-01-15T09:30:00.000Z" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.id |
string | yes | |
data.orgId |
string | yes | |
data.profileId |
string | null | yes | |
data.journey |
string | null | yes | |
data.executionId |
string | null | yes | |
data.kind |
“journey” | “test” | “api” | yes | |
data.step |
string | yes | |
data.channel |
“email” | “webhook” | yes | |
data.status |
“sent” | “delivered” | “bounced” | “complained” | “failed” | “skipped_disabled” | “skipped_paused” | “skipped_suppressed” | “skipped_quota” | “skipped_unknown_purpose” | “skipped_frequency_cap” | “skipped_consent” | “skipped_domain” | “skipped_ssrf” | “would_send” | “would_skip_disabled” | “would_skip_paused” | “would_skip_suppressed” | “would_skip_quota” | “would_skip_unknown_purpose” | “would_skip_frequency_cap” | “would_skip_consent” | “would_skip_domain” | “would_skip_ssrf” | yes | |
data.dryRun |
boolean | yes | |
data.recipient |
string | null | yes | |
data.providerId |
string | null | yes | |
data.error |
string | null | yes | |
data.createdAt |
string (date-time) | yes | |
data.updatedAt |
string (date-time) | yes | |
data.sentAt |
string (date-time) | null | yes | |
data.settledAt |
string (date-time) | null | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no |
Errors follow the shared envelope and code table.
DELETE /v1/templates/{key}
Section titled “DELETE /v1/templates/{key}”Delete an email template
Auth: Session token.
Response 200
{ "data": { "deleted": true }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.deleted |
true | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no |
Errors follow the shared envelope and code table.