Sign inGet Started

Submit an email template version

POST
/v1/email/templates/{template_ref}/versions/{version_id}/submit
const templateId = "TEMPLATE_ID";
const draftVersionId = "DRAFT_VERSION_ID";
const check = await bird.email.templates.versions.submit(templateId, draftVersionId, {
  validate_only: true,
});
for (const problem of check.errors) {
  console.log(problem.language, problem.field, problem.message);
}
if (check.valid) {
  const result = await bird.email.templates.versions.submit(templateId, draftVersionId);
  console.log(result.version?.version_number);
}
Response200
{
  "errors": [
    {
      "language": "pt-BR",
      "field": "subject",
      "code": "E04051",
      "message": "A subject is required to submit."
    }
  ],
  "version": {
    "id": "emv_01krdgeqcxet5s7t44vh8rt9mg",
    "template_id": "emt_01krdgeqcxet5s7t44vh8rt9mg",
    "status": "published",
    "variables": [
      {
        "type": "code"
      }
    ],
    "default_language": "pt-BR",
    "updated_by": {
      "id": "usr_01krdgeqcxet5s7t44vh8rt9mg",
      "type": "user"
    }
  },
  "template_ref": "emt_01krdgeqcxet5s7t44vh8rt9mg",
  "version_id": "emv_01krdgeqcxet5s7t44vh8rt9mg",
  "compatibility_severity": "warning",
  "compatibility": [
    {
      "rule_id": "css_display_flex_grid",
      "severity": "warning",
      "language": "pt-BR",
      "field": "html",
      "message": "`display: flex` and `grid` don't work in Outlook (Windows/Windows Mail).",
      "fix": "Use tables for layout.",
      "partial": "Partial support for `display: flex` and `grid` in Gmail (iOS/Android).",
      "line": 14,
      "column": 6,
      "match": "display: flex",
      "unsupported_clients": [
        {
          "family": "outlook",
          "platforms": [
            "windows",
            "windows_mail"
          ]
        }
      ],
      "partial_clients": [
        {
          "family": "outlook",
          "platforms": [
            "windows",
            "windows_mail"
          ]
        }
      ]
    }
  ]
}

Submits the template's draft as a new immutable, numbered version and makes it the live version used by sends. The draft remains editable.

Every language the draft has must have a subject and a body, and the default language must be present. Submission is all or nothing: an incomplete language rejects the request and the response reports every language error. A submit freezes every language the draft carries, so it cannot name a subset. The draft does not have to hold every language you plan to support: add more in a later version.

Set validate_only: true to run the checks without creating a version. Set expected_revision to reject a concurrent edit. An unchanged draft is rejected. Submission is synchronous, and a returned version is live.

Parameters

template_refstring

The template's id (emt_…) or slug. Submit freezes a draft, and a built-in system template has no draft, so its bird_ slug returns 404 not_found_error here.

version_idstring

The draft version to validate or publish.

Request Payload

validate_only
boolean

Check the draft without actually submitting it. Every language gets checked and every problem gets reported back to you, but nothing is frozen and no new version gets created. Give a validation run its own Idempotency-Key, separate from the real submit that follows it. You can also send no key. The validation request and real submit have different bodies, so using the same key for both is rejected as key reuse.

expected_revision
integer

The draft revision you last read (from the template's revision field). A stale value returns a conflict so you can reload and retry.

languages
array of string

Languages to process when submitting an already-published version. Email templates accept submissions only for drafts, so setting this field for an email template is rejected.

Response Payload

valid
boolean
required

Whether the version passed every check.

errors
array of object
required

Every problem found across the draft's languages. Empty when valid is true.

Show child attributes
version
nullable object

The version this submit created, or null when it was only a validation run and nothing got frozen. As soon as this is not null, sends already use that version. No further action is required to make it live.

Show child attributes
version.id
string
required

Template version ID.

version.template_id
string
required

The template this version belongs to.

version.version_number
nullable integer

Sequential published-version number (1, 2, 3…). Null while the version is a draft.

version.status
string
required

Whether this version is still being edited or has been published. It records the version's publication history: a version that a later one replaced stays published. The template's live_version_id names the version a send resolves to now.

archived is reserved and no version carries it yet. Version retirement will produce it, so it is declared here ahead of that feature: a client written against this list today keeps working when the first archived version arrives, rather than the value's arrival being a breaking change.

Possible values: draft, published, archived

version.revision
integer
required

The version's revision counter.

version.variables
array of object
required

Input definitions this version uses, including caller parameters and reserved Bird inputs. An entry with system false is yours to send in template.parameters. An entry with system true names a reserved Bird key; supported paths receive Bird values. A draft can also report unsupported reserved paths, including bare bird, whose constraint explains that no Bird value fills them. Correct these paths before publishing. Naming a reserved Bird key in a send is rejected with a 422.

The list combines all the languages, because languages do not have to use the same inputs: if the English body uses discount_code and the French body uses shipping_date, both appear here. A send requires values only for the caller parameters referenced by its resolved language. Preview each language to see its inputs. Extra parameters are ignored; omitting a caller parameter referenced by the resolved language returns a 422 naming it.

Show child attributes
version.variables.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.

version.variables.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.

Possible values (may grow over time): code, ttl, count, ref, date, date_time, amount, currency, text

version.variables.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.

version.variables.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.

version.variables.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.

version.variables.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.

version.languages
object
required

The content this version holds, keyed by language tag in BCP-47 form such as en or pt-BR. Publishing freezes every language together, so a version shows exactly what it would send in each of them. On a published version this is the send content.

version.default_language
string
required

The language this version treats as its default: the one a send uses when it names none, and the last resort when a requested language is not available.

version.created_at
string
required

When this version was created.

version.published_at
nullable string

When this version was published, or null if it has not been published.

version.updated_by
nullable object

Who last saved this version: a member's own session, an OAuth token delegated from one, or a workspace API key. Publishing freezes a version, so on a published one this is whoever published it. Null means no actor is on record: a built-in template, which is code-defined rather than stored, or a version last saved by an API key before this field existed. Every other version has one, even when its display_name could not be resolved (a member whose account is gone, say).

Show child attributes
template_ref
string
required

The template this submit addressed.

version_id
string
required

The draft this submit addressed: the one it froze, or on a validation run the one it checked and left as it was. The frozen version, when there is one, is version.

compatibility_severity
string
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.

Possible values: problem, warning, none

compatibility
array of object
required

What the draft's HTML uses that mail clients remove, ignore, or render inconsistently, across every language, in alphabetical order of language tag and then the order the patterns appear. Empty when nothing is worth reporting. Advisory, and separate from errors: a finding never fails a submit, so the version froze either way. Each finding names the language it is in, and its line and column count in that language's HTML, which the language read returns as content.html. At most 200 findings come back, the first 200 in that order, so a draft that reaches the cap can omit a later language's findings entirely rather than trimming each language: read a language's own findings from its read or its write. compatibility_severity is derived from every finding the draft produced, including any beyond those 200.

Show child attributes