Skip to main content

Endpoints

Email templates are reusable layouts that seed new emails. A template carries HTML plus optional default sender headers (default_subject, default_from_name, and so on). Once a template is approved, POST /v1/emails with template_id copies its HTML into a new email and inherits those defaults. Like emails, templates start as a draft and accumulate versions as you iterate.
AI generation is asynchronous. Creating with a prompt or posting a message returns 202 Accepted with a generation_id, and you poll the generation endpoint to track progress. Only one generation can run per template at a time.

Idempotency

POST /v1/email-templates, POST /v1/email-templates/{templateId}/messages, and POST /v1/email-templates/{templateId}/save-code accept an optional Idempotency-Key header. Retrying with the same key replays the stored result instead of creating a duplicate. If a request with the same key is still being processed, the API returns 409 Conflict with error_code: "idempotency_in_progress".

Create a Template

Request Body

Provide code to upload your own HTML, or prompt to generate it with AI (202 Accepted). With neither, you get a blank scaffold branded from your workspace. Both code-based and blank creates return 201 Created with the template object.

Request Example

Response Example

202 Accepted

Poll a Generation

Generation status moves through generating to completed or failed, with the same shape as email generations: completed generations include the version they produced plus content_path and plain_text_path under /v1/email-templates/...; failed ones return error_code: "generation_failed" and an error_message.

Retrieve a Template

200 OK
status is draft or approved. source_type is upload for API-created templates, or email for templates saved from an existing email (then source_email_id points to it).

Read Content

Returns the HTML of the latest completed version (or of one specific generation) with a text/html content type — not JSON.

Plain Text

Works exactly like email plain text, keyed by template_id instead of email_id: GET returns the auto-derived text, PUT overrides it (body: {"content": "..."}, up to 1 MB), and DELETE reverts to the auto-derived version. PUT and DELETE require a completed generation, otherwise they return 400 Bad Request. Iterating the HTML after an override flips stale_after_html_iteration to true.

Iterate With AI

Takes the same request body as iterating an email: a required prompt (1-10000 characters) with the edit instruction, plus optional asset_ids naming template assets to make available to the generation. Returns 202 Accepted with a generation_id to poll. Starting a second generation while one is running returns 409 Conflict with error_code: "generation_in_progress"; an unknown asset returns 409 with error_code: "asset_not_found".

Save Code Directly

Works like saving email code: send {"code": "..."} with the full replacement HTML (up to 2 MB) to create a new version synchronously — 200 OK with the new generation_id and version. Saving code does not unapprove an approved template. Blocked while a generation is running.

Update Metadata

Accepts the same optional fields as create except code and prompt: name, default_subject, default_preview_text, default_from_name, default_from_address, default_reply_to_email. Omit a field to leave it unchanged; pass null to clear nullable fields. Returns the updated template object.

Clone a Template

Duplicate a template — its content, versions, and assets — as a fresh draft attributed to the API principal. The clone is never approved. Optionally send {"name": "..."}; the name defaults to "{original name} (copy)". Returns 201 Created with the new template object. Accepts an optional Idempotency-Key header.

Approve and Unapprove

Approving marks the template’s newest completed version usable for seeding new emails. Unlike emails, there is no required-header gate — the default_* fields are optional — but the template must have at least one completed generation, otherwise the API returns 409 Conflict with error_code: "template_not_ready". Optionally send {"version": <n>} as an optimistic-concurrency precondition — a mismatch with the newest completed version (normally equal to current_version) returns 409 Conflict with error_code: "approval_version_stale". While a generation is running, an approve without version is refused with 409 Conflict and error_code: "generation_in_progress"; passing version explicitly still approves that already-completed version mid-generation. Unapproving is never blocked: emails already seeded from the template are unaffected, but new emails can no longer be created from it until it is approved again. Both endpoints return the updated template object.

List Templates

Takes the same query parameters as listing emails: status (any, draft, approved), sort_by, sort_order, limit, and cursor. Responses contain templates and next_cursor. List items are the template object minus latest_generation_status and the content_path / plain_text_path fields — fetch HTML per template via GET /v1/email-templates/{templateId}/content.

Delete a Template

Returns 200 OK with {"template_id": "...", "deleted": true}. Unlike emails, deletion is never blocked — emails already seeded from the template keep their own copy of the content.

Common Errors

Next: Create Emails

Seed a new email from your approved template and send it with blasts and flows.