email-templates

Create an email template

Creates a template and its first editable draft.

Send the template's slug (the name you send the template by), an optional display name that defaults to the slug, a category, the authoring format (source), and the draft's content in one or more languages. Leave the content out to start from an empty draft. A slug already used in the workspace returns a conflict.

post/v1/email/templates

Request

  • Base URL: https://us1.platform.bird.com
  • URL: https://us1.platform.bird.com/v1/email/templates
  • Auth: one of:
    • HTTP bearer
    • API key in cookie bird_session

Headers

Idempotency-Keystring

Client-supplied key. On operations supporting request deduplication, a retained response is replayed for duplicate requests with the same key within the idempotency window (3 hours by default). This protection requires a workspace, organization, or staff-account scope. User-only and unscoped unauthenticated operations, streams, and operations with a separate replay contract do not use this response replay.

On a supported operation, if idempotency protection is unavailable before execution, the API returns 503 IdempotencyUnavailable (E01033) without executing this attempt. Retry with backoff using the same key and request. An operation that takes effect before its response is retained can still execute again on retry.

Two distinct 409 errors signal misuse:

  • request_in_progress (E01004): The same key is currently being processed by a concurrent request. Wait briefly and retry. The lock expires within 30 seconds.
  • idempotency_key_reuse (E01005): The same key has already completed against a different request body or method. Generate a new key.

Recommended key format is <event-type>/<entity-id> (for example welcome-user/usr_abc123).

X-Workspace-Idstring

Workspace context for the request. Required for dashboard authentication and master keys. Workspace keys and access tokens already identify their workspace; send that workspace or omit the header. A different one is rejected.

X-Organization-Idstring

Organization context for the request. Required for dashboard authentication. An API key or access token carries its own organization, so send either that organization or no header at all; a different one is rejected.

Request body

slugstring required

A template's slug: what you send it by, for example welcome-email. Email and SMS slugs stay fixed after creation. WhatsApp slugs can change only before the first submission. A slug can contain lowercase letters, numbers, hyphens, and underscores, has to start and end with a letter or a number, and can be up to 63 characters long.

namestring

The template's display name, shown wherever the template is listed. You can change it any time. It defaults to the slug if you do not set one.

descriptionstring

What the template is for, in your own words.

category'transactional' | 'marketing' required

Whether the template is for transactional email or marketing email.

source'html' required

The authoring format to create the template in. html is finished markup you provide, optionally personalized with Liquid; a format the API cannot author yet is refused.

languagesobject

The initial draft's content, keyed by language tag in BCP-47 form such as en or pt-BR. A template holds up to 25 languages, and a send picks one of them.

Omit this to create an empty draft and add content later.

The example's {{ first_name }} is a parameter, filled from template.parameters at send time. A {{ bird.contact.<attribute> }} placeholder reads a contact record instead. A send to an email address has no contact record, so it refuses such a template. A broadcast fills it from each recipient's contact, and a preview from the contact or parameters you supply.

default_languagestring

A language tag in BCP-47 form, for example en or pt-BR.

on_missing_language'fallback' | 'fail'

What a send or a preview does when it asks for a language the template cannot serve.

fallback serves the closest match instead. It tries a broader form of the same language first, so a request for pt-BR can be served by a stocked pt, and then the template's default language. A send never fails because a language is missing.

fail rejects the send rather than serving a different language, for content where sending the wrong language is worse than not sending at all. It matches the requested tag or a broader form of it and refuses a sibling variant, so pt-BR is never served by pt-PT. A send that names no language still uses the default language.

The default is per channel and stated on each channel's own field, because what a wrong-language send costs differs. Where every language is separately reviewed and separately priced, falling back silently would send content the recipient did not expect at a rate the sender did not choose.

language_source_requiredboolean

Whether a send has to name a language. Set it to true to reject a send that names none instead of serving the default language. Pair it with on_missing_language: fail when every send must pick a language deliberately: on its own, fail is bypassed by naming no language at all. A broadcast must select a template language when this is set. Defaults to false.

Example request

{
  "slug": "welcome-email",
  "name": "Welcome email",
  "description": "Sent to new customers after signup.",
  "languages": {
    "en": {
      "subject": "Welcome, {{ first_name }}",
      "preview_text": "Your account is ready",
      "html": "<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n"
    }
  },
  "default_language": "pt-BR",
  "on_missing_language": "fallback"
}

Response

Email template created.

editor_urlstring uri

Optional link to open the saved draft in the browser editor, in its default language. Absent for built-in templates.

idstring required
workspace_idstring required
slugstring required

A template's slug: what you send it by, for example welcome-email. Email and SMS slugs stay fixed after creation. WhatsApp slugs can change only before the first submission. A slug can contain lowercase letters, numbers, hyphens, and underscores, has to start and end with a letter or a number, and can be up to 63 characters long.

namestring required

The template's display name, shown wherever the template is listed. You can change it any time. It defaults to the slug if you do not set one.

descriptionstring nullable required

What the template is for, in your own words. Null if you have not set one.

scope'system' | 'workspace' required

Whether the template is one of our built-in templates (system) or one your workspace created (workspace).

status'draft' | 'pending' | 'active' | 'rejected' | 'inactive' required

Where the template stands as a whole. The same five states on every channel.

  • draft: nothing has ever gone live.
  • pending: nothing is live and at least one language is in review.
  • active: at least one language is live, so something can be sent.
  • rejected: it was reviewed and every language was refused.
  • inactive: nothing is live and nothing is in review, so content was withdrawn or was blocked before anything went live.

A template with one language live is active even while another is still drafted or refused. Read languages for the state of each language and its reason.

Which values a channel reports follows its review model. A channel whose content a third party reviews uses all five. On email and SMS, where content goes live on publish, a template is draft, active or inactive, and pending and rejected are reserved for the review stage coming to both, so a template reaching either is not a breaking change.

category'transactional' | 'marketing' required

Whether the template is for transactional email or marketing email.

sourcestring required

The authoring format the template is written in, fixed at creation. html is finished markup you provide, optionally personalized with Liquid.

themestring required

The visual theme a built-in template is designed in. Each of the catalog's five themes ships its own set of eight emails, and the sets overlap only partly, so the theme is what you choose between once you know which email you want. Only our built-in system templates have one.

draft_version_idstring required
live_version_idstring required
published_version_idstring required
revisioninteger nullable required

The draft's revision counter. Send it back on the next update to detect concurrent edits. Null for a built-in system template, which is unversioned.

languagesobject required

Every language this template has, keyed by language tag in BCP-47 form such as en or pt-BR, each with its state. One read tells you which languages are live and which have unpublished edits, without fetching any content.

Content is not here: read a version's languages for that, one language at a time.

default_languagestring required

A language tag in BCP-47 form, for example en or pt-BR.

available_languagesLanguageTag[] required

The languages this template currently supports for sending, as BCP-47 tags. Empty until the template is published, because sends serve published content. The set may shrink for reasons other than editing, so read it rather than assuming it matches what was published. A built-in system template has no publish step and always reports its one language.

on_missing_language'fallback' | 'fail' required

What a send or a preview does when it asks for a language the template cannot serve.

fallback serves the closest match instead. It tries a broader form of the same language first, so a request for pt-BR can be served by a stocked pt, and then the template's default language. A send never fails because a language is missing.

fail rejects the send rather than serving a different language, for content where sending the wrong language is worse than not sending at all. It matches the requested tag or a broader form of it and refuses a sibling variant, so pt-BR is never served by pt-PT. A send that names no language still uses the default language.

The default is per channel and stated on each channel's own field, because what a wrong-language send costs differs. Where every language is separately reviewed and separately priced, falling back silently would send content the recipient did not expect at a rate the sender did not choose.

language_source_requiredboolean required

Whether a send has to name a language. When true, a send that names none is rejected instead of being served the default language. A broadcast must select a template language when this is set.

last_submitted_atstring date-time nullable required

When this template was last submitted. Null if it never has been. Submitting is the only thing that moves this timestamp: rolling back changes which version is live without counting as a submit, so this keeps reporting the last real submit. Read it alongside languages, which says where each language stands.

created_atstring date-time nullable required

When the template was created. Null for a built-in system template.

updated_atstring date-time nullable required

When the template was last modified. Null for a built-in system template.

Example response

{
  "id": "emt_01krdgeqcxet5s7t44vh8rt9mg",
  "workspace_id": "ws_01krdgeqcxet5s7t44vh8rt9mg",
  "slug": "welcome-email",
  "name": "Welcome email",
  "status": "active",
  "draft_version_id": "emv_01krdgeqcxet5s7t44vh8rt9mg",
  "live_version_id": "emv_01krdgeqcxet5s7t44vh8rt9mg",
  "published_version_id": "emv_01krdgeqcxet5s7t44vh8rt9mg",
  "languages": {
    "en": {
      "status": "live"
    },
    "pt-BR": {
      "status": "live",
      "draft": true
    },
    "de": {
      "status": "draft"
    }
  },
  "default_language": "pt-BR",
  "available_languages": [
    "en"
  ],
  "on_missing_language": "fallback"
}

Changes

No recorded changes to this operation across all 2 revisions of this API.