---
title: "Get an email template preview"
method: POST
path: "/v1/email/templates/{template_ref}/preview"
tags: ["email-templates"]
---

# Get an email template preview

`POST /v1/email/templates/{template_ref}/preview`

## Request

- Base URL: `https://us1.platform.bird.com`
- URL: `https://us1.platform.bird.com/v1/email/templates/{template_ref}/preview`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `bird_session`

Renders a template with the sample values you supply and returns the resulting subject, HTML, and plain-text bodies. Bird does not issue a recipient-specific unsubscribe URL in a preview. If you supply `bird.unsubscribe_url` in `parameters`, the preview renders your value; an empty link is not live. Bird adds unsubscribe content to marketing email at send time; transactional email gets no automatic unsubscribe footer. By default it renders the current draft, so you can check your changes before you submit it. Pass `version` to preview a specific published version instead; built-in `system` templates have no versions, so `version` on a `bird_` template returns a validation error. Pass `contact` to see the email the way one of your contacts would receive it. Works for your workspace templates and built-in `system` templates. Sample `parameters` are capped at 16 KB once serialized, and personalization that is not valid or not supported returns a validation error naming what to fix.
The response also reports what the HTML uses that mail clients remove, ignore, or render inconsistently. Each finding in `compatibility` names the pattern, the line and column it sits on, and what to use instead, and `compatibility_severity` reduces them to one word for the whole body. It is advisory: the preview renders either way.

## Headers

- `Idempotency-Key` string
- `X-Workspace-Id` string
- `X-Organization-Id` string

## Request body

- EmailTemplatePreviewRequest: Sample values and options for rendering a template preview. Omit the body entirely to preview the current draft: `bird.contact.` tokens then fill from stand-in values, and a parameter you give no value for renders as empty.
  - `content` EmailTemplatePreviewContent: Unsaved email template content. Preview renders these fields instead of a stored draft; input analysis identifies their references without rendering.
    - `subject` string: The subject line, including any template expressions.
    - `preview_text` string: Inbox preview text. Preview and publication fold it into the top of the HTML as a hidden preheader. Input analysis includes its references.
    - `html` string: The HTML body, including any template expressions.
    - `text` string: The plain-text body. When omitted, a plain-text alternative is derived from the HTML for preview, input analysis, and publication.
  - `parameters` object: Sample values for the variables the template uses, for this one preview only. A variable takes its value under its own name. A `bird.` value nests to match the token, so `{"bird": {"contact": {"first_name": "Ada"}}}` fills `{{ bird.contact.first_name }}`. A preview is more forgiving than a send: a parameter you leave out renders as empty here rather than being rejected. `parameters` is capped at 16 KB once serialized.
  - `contact` string
  - `language` string: A language tag in BCP-47 form, for example `en` or `pt-BR`.
  - `version` string

## Response `200`

The rendered preview.

- EmailTemplatePreview: A rendered preview of an email template: its subject, HTML, and plain-text bodies with the supplied sample values filled in, ready to display.
  - `editor_url` string, uri: Optional link to open the saved draft in the browser editor, in the language this preview rendered. Absent for built-in templates, published-version previews, and previews of unsaved `content`.
  - `subject` string, nullable, required: The rendered subject line. Null when the template has no subject.
  - `html` string, nullable, required: The rendered HTML body. Null when the template has no HTML body.
  - `text` string, nullable, required: The rendered plain-text body. Derived from the HTML when the template has no separate plain-text body, and null when it has neither.
  - `language` string, required: A language tag in BCP-47 form, for example `en` or `pt-BR`.
  - `variables` TemplateVariable[], required: Every input definition this content uses, in one list: the parameters you fill in with `parameters`, and the values Bird fills in for each recipient. Read `system` to tell them apart. This list covers only the language named by `language`. A version read combines the inputs from every language the version holds. Preview each language separately to see its own. You can nest sample values under `bird` in preview `parameters`; sends reject that reserved namespace.
    - `key` string, required: The key this slot is filled by. When `system` is true it is the reserved `bird` key or a dotted path beneath it, such as `bird.contact.first_name`, and naming it in a send is rejected. Otherwise, on email and SMS it is the key you set in the send's `parameters` object, and on WhatsApp the `name` you repeat on the matching parameter inside `components`, or, for a template whose placeholders are positional, the position itself as `1`, `2` and so on.
    - `type` string, required: The value type this slot accepts. Built-in SMS templates use typed slots (`code`, `amount` and the rest), each of which rejects a value that does not match its `constraint`. Email, WhatsApp and workspace SMS templates use `text`. Workspace SMS parameters must be scalar values. Open enum: treat an unrecognized value as a future type rather than an error.
    - `required` boolean, required: Whether the send must supply this variable. Omitting a required value returns `422` on email, SMS, and WhatsApp sends. Always false when `system` is true, because you do not supply that slot's value.
    - `constraint` string, required: A plain-language description of what values this variable accepts. When `system` is true it names where Bird takes the value from instead, because there is no value for you to send.
    - `sensitive` boolean: Whether this slot's value is redacted from stored message content. A placeholder replaces the sensitive value in message history; transport queues can still carry the text needed for delivery.
    - `system` boolean: Whether the value comes from Bird rather than from the send. Absent means false. Only email templates have system slots, identified by the reserved `bird` key or a dotted path beneath it; every SMS and WhatsApp slot is yours to fill. A draft can also name a reserved key no Bird value fills, including `bird` itself: `constraint` says so, and publishing that draft is rejected.
  - `compatibility_severity` 'problem' | 'warning' | 'none', required: The worst severity across every finding the response was computed from, which is the authoritative reading: a response that caps how many findings it lists still accounts here for the ones it left out. Each response's `compatibility` says which content it covered. - `problem`: at least one finding is a `problem`. - `warning`: every finding is a `warning`. - `none`: there are no findings.
  - `compatibility` EmailCompatibilityFinding[], required: What the previewed HTML uses that mail clients remove, ignore, or render inconsistently, in the order the patterns appear. Empty when nothing is worth reporting. Line and column count in the `content.html` you supplied, or in the template's own HTML when you supplied none, so they address the source rather than the rendered output. Previewing a published `version` is the exception: where the stored version keeps no authored copy of a language the publish step rewrote, the positions count in that rewritten body, which no response returns. A preview renders either way. At most 200 findings come back, the first 200 in source order; `compatibility_severity` is derived from every finding the HTML produced, including any beyond those 200.
    - `rule_id` string, required: Which rule produced a finding. - `html_script`: a `<script>` tag. - `html_event_handlers`: a JavaScript event-handler attribute such as `onclick`. - `html_embedded_content`: an `<iframe>`, `<embed>`, or `<object>`. - `html_linked_stylesheet`: a `<link rel="stylesheet">`. - `css_at_import`: an `@import` rule. - `html_form`: a `<form>`, `<input>`, `<select>`, or `<textarea>`. - `html_svg`: an inline `<svg>`. - `html_media`: a `<video>` or `<audio>` element. - `css_display_flex_grid`: `display: flex` or `display: grid`, and their `inline-` forms. - `css_position_fixed_sticky`: `position: fixed` or `position: sticky`. - `css_variables_no_fallback`: a `var()` with no fallback value. - `css_viewport_units`: a `vh` or `vw` length. - `html_button`: a `<button>` element. - `css_math_functions`: `clamp()`, `min()`, or `max()`. - `css_modern_color`: `oklch()`, `oklab()`, `lch()`, or `lab()`. - `html_web_page_markup`: markup a web framework left behind, such as a `data-reactroot` attribute or a `__next` element id.
    - `severity` 'problem' | 'warning', required: What a finding costs you. - `problem`: the pattern does nothing at all. The client removes the markup, never loads the stylesheet carrying it, or will not operate the control. Where a finding names clients, that is what happens in those clients. - `warning`: it does something, but not what you wrote. Neither one refuses a save, a submit, or a send.
    - `language` string, required: A language tag in BCP-47 form, for example `en` or `pt-BR`.
    - `field` string, required: Which field of that language the finding is in. Always `html`; the subject and the plain-text body are not checked.
    - `message` string, required: What is wrong and which clients it affects, worded to show to whoever is authoring the template. It covers the rule's whole category rather than the exact text that matched, so a rule covering `<video>` and `<audio>` names both whichever one is on the line. Show `fix` and then `partial` after it.
    - `fix` string, nullable, required: What to use instead. Null when there is no drop-in alternative and the fix is a restructure.
    - `partial` string, nullable, required: Which clients support the feature only partly. Null when no client's support is partial.
    - `line` integer, required: The 1-based line the pattern is on, in the HTML the containing response's `compatibility` says it covered.
    - `column` integer, required: The 1-based column the pattern starts at, on that line.
    - `match` string, required: The source text that matched, starting at `line` and `column`: the smallest span that identifies what is wrong. Never the enclosing line. For a finding on a whole element, the span runs from the opening tag through the close tag, because the client drops the element's content along with its markup. Cut at 256 characters, so an element holding a long body is quoted from its start rather than in full.
    - `unsupported_clients` EmailClientSupport[], required: Every client family that does not support the feature at all, in alphabetical order by each entry's `family`. Empty on a finding whose `message`, `fix`, and `partial` name no client. `message` names at most four families; this names all of them.
      - `family` string, required: Which mail client a finding applies to. A finding's `message` names at most Apple Mail, Gmail, Outlook, and Yahoo; its `unsupported_clients` and `partial_clients` name every client affected. - `gmail`: Gmail - `outlook`: Outlook - `yahoo`: Yahoo - `apple_mail`: Apple Mail - `aol`: AOL - `thunderbird`: Mozilla Thunderbird - `samsung_email`: Samsung Email - `sfr`: SFR - `orange`: Orange - `protonmail`: ProtonMail - `hey`: HEY - `mail_ru`: Mail.ru - `fastmail`: Fastmail - `laposte`: LaPoste.net - `gmx`: GMX - `web_de`: WEB.DE - `ionos_1and1`: 1&1 - `wp_pl`: WP.pl
      - `platforms` EmailClientPlatform[], required: Which of the family's platforms this applies to, in alphabetical order.
    - `partial_clients` EmailClientSupport[], required: Every client family that renders something other than what you wrote, in alphabetical order by each entry's `family`. Empty when no client's support is partial, which is also when `partial` is null. `partial` names at most four families; this names all of them.
      - `family` string, required: Which mail client a finding applies to. A finding's `message` names at most Apple Mail, Gmail, Outlook, and Yahoo; its `unsupported_clients` and `partial_clients` name every client affected. - `gmail`: Gmail - `outlook`: Outlook - `yahoo`: Yahoo - `apple_mail`: Apple Mail - `aol`: AOL - `thunderbird`: Mozilla Thunderbird - `samsung_email`: Samsung Email - `sfr`: SFR - `orange`: Orange - `protonmail`: ProtonMail - `hey`: HEY - `mail_ru`: Mail.ru - `fastmail`: Fastmail - `laposte`: LaPoste.net - `gmx`: GMX - `web_de`: WEB.DE - `ionos_1and1`: 1&1 - `wp_pl`: WP.pl
      - `platforms` EmailClientPlatform[], required: Which of the family's platforms this applies to, in alphabetical order.

## Other responses

- `400`: Bad request
- `401`: Authentication required
- `403`: Insufficient permissions
- `404`: Resource not found
- `422`: The request has invalid field values, violates a business rule, or carries a query parameter the endpoint does not declare. Field validation errors use `type: validation_error` and include the affected fields in `details`. Business-rule errors identify the failed rule in `type`.
- `429`: Rate limit exceeded
- `500`: Internal server error
- `503`: The service is temporarily unavailable. If `Retry-After` is present, wait for that delay before retrying; otherwise, retry with exponential backoff. Reuse the same idempotency key and request when retrying a mutation.

---

[API](https://skmtc.dev/messagebird/apis/bird-api.md) · [All operations](https://skmtc.dev/messagebird/apis/bird-api/llms.txt) · [OpenAPI document](https://skmtc.dev/messagebird/apis/bird-api/revisions/6dfce1179799?raw)
