Skip to main content

Email Builder API

Drag-and-drop email template builder and asset management Base path: /api/v1/messages/email-builder Endpoint count: 5

title: “Errors worth branching on” description: “Per-endpoint failure templates matching the envelope — what actually fires, what to retry, what to surface to the operator.”

Errors worth branching on

These five failures cover the template save (POST /api/v1/messages/email-builder/save), which the visual editor autosaves against. Each block below is a full { error, meta } envelope as the API returns it, and the matrix at the bottom answers retry vs surface for the same five classes. For the platform-wide decision table these branches plug into, see the error handling guide.

401 — Unauthorized

A 401 on this page is the bearer key failing before the route ran — it never means the resource is wrong. Rotate the key or re-mint the scoped token; retrying the same request changes nothing.

403 — Forbidden

A 403 means the key authenticated but the operation is gated by scope — check the key’s scopes on the developer page; a 403 is never a data-not-found shape.

422 — Schema

A 422 means the payload did not match the request schema — branch on error.details.field and resave against the head version after re-fetching the template instead of blind-retrying the same body.

429 — Rate

The Retry-After header and error.details.retry_after are both set on every 429 — resend the SAME request after the lower of the two.

409 — Conflict

Fetch the current template, re-apply the delta, and re-save — a plain retry loops on the same 409.

60-second retry matrix


List saved email-builder templates for the tenant

GET /api/v1/messages/email-builder/list
Returns the tenant’s saved email-builder templates (channel email), freshest-first, each with its editable blocks array, rendered plain-text body, previewText, and metadata — the shape the dashboard’s ‘Load template’ picker re-hydrates the builder canvas from. Cursor-paginated via the opaque cursor value returned in pagination.cursor; limit defaults to 50 (max 50). An empty tenant returns an empty data list.
string
—
any
—
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Transpile MJML to HTML email

POST /api/v1/messages/email-builder/mjml/render
Transpiles an MJML (mjml.io) source document into the same table-based, inline-styled HTML the block renderer produces, so teams can author templates in MJML instead of the drag-and-drop block model. The response returns best-effort html plus an errors list that reports unsupported components or structural problems without failing the request, and the extracted previewText. Nothing is persisted and no email is sent.
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.
string
required
The MJML source document to transpile, as a complete <mjml>…</mjml> markup string (1–262,144 characters / 256 KiB). Unsupported components and structural problems are reported in the response errors array without failing the request, so you still receive best-effort HTML.

Render email template to HTML

POST /api/v1/messages/email-builder/render
Renders a drag-and-drop email template — a name plus an ordered blocks array — into a complete, cross-client HTML email document. The response carries both the static HTML and an AMP-for-Email (text/x-amp-html) alternative built from the same blocks, plus the template id (templateId) so the caller can correlate a render with a later save. Nothing is persisted and no email is sent; pair with /api/v1/messages/email-builder/save to store the definition or /api/v1/messages/email-builder/send-test to deliver a proof.
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Save email template

POST /api/v1/messages/email-builder/save
Persists an email-builder template definition — name, ordered blocks, optional previewText / metadata, and optional id — for later editing in the drag-and-drop builder. The content is stored as a { body, blocks } envelope: the plain-text body serves list snippets and send-time fallbacks, and blocks re-hydrates the builder canvas. Re-saving the same name without an id updates the existing template in place; passing an id that collides with a different template’s name returns 409 TEMPLATE_NAME_ALREADY_EXISTS. Rendering is not part of this call — use /api/v1/messages/email-builder/render for HTML.
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.

Send test email

POST /api/v1/messages/email-builder/send-test
Renders the supplied blocks and sends a test email to to with the subject [Test] <templateName>, and records the send in the tenant’s message history (visible in the dashboard Messages tab) with a test-sent status. The send is BILLED at the channel rate (debited before the provider send — a real paid MT through the shared Resend account), rate-limited per tenant (5/minute), and tenants on the platform’s shared email account must pass the same gates as production sends: the recipient must be a verified org member, the email feature must not be suspended, and a daily test-send cap applies. Requires an explicit, valid to address.
string
Stripe-style idempotency token. Pass a stable, client-generated value (1-255 chars) to dedupe retries on transient timeouts. The same key+credential+path replays the original response for 24h on 2xx (5min on 4xx, 30s on 5xx). Returns 409 if a concurrent request with the same key is already in flight; replayed responses include the Idempotency-Replay: true response header.
string (enum: true|false)
Sandbox opt-in for Clerk-session-authenticated requests. Set to true to route the call through the test-mode pipeline: no real provider delivery, no credits deducted, response meta.test_mode: true. Ignored for live API keys (dv_live_sk_*) — server-to-server clients must use a test-prefixed key (dv_test_sk_*) to exercise sandbox. Test-prefixed keys unconditionally enable sandbox regardless of this header.