# Create an email template

`POST /v1/email/templates`

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.

## Code samples

### CLI

```sh
bird email templates create welcome-email --body-file - <<'JSON'
{
  "slug": "welcome-email",
  "name": "Welcome email",
  "description": "Sent to new customers after signup.",
  "category": "transactional",
  "source": "html",
  "languages": {
    "en": {
      "subject": "Welcome to Acme, {{ bird.contact.first_name }}!",
      "html": "<h1>Hi {{ bird.contact.first_name }}</h1>"
    }
  },
  "default_language": "en",
  "on_missing_language": "fallback"
}
JSON
```

### cURL

```sh
curl -X POST "https://us1.platform.bird.com/v1/email/templates" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "welcome-email",
    "name": "Welcome email",
    "description": "Sent to new customers after signup.",
    "category": "transactional",
    "source": "html",
    "languages": {
      "en": {
        "subject": "Welcome to Acme, {{ bird.contact.first_name }}!",
        "html": "<h1>Hi {{ bird.contact.first_name }}</h1>"
      }
    },
    "default_language": "en",
    "on_missing_language": "fallback"
  }'
```

## Example response `201`

```json
{
  "id": "emt_01krdgeqcxet5s7t44vh8rt9mg",
  "workspace_id": "ws_01krdgeqcxet5s7t44vh8rt9mg",
  "slug": "welcome-email",
  "name": "Welcome email",
  "scope": "workspace",
  "status": "active",
  "category": "transactional",
  "source": "html",
  "theme": null,
  "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"
}
```

## Request body

- `slug` (string, required): The template's workspace-unique handle, and a stable alternative to the template ID when sending by template. It can contain lowercase letters, numbers, hyphens, and underscores. It is fixed at creation, so pick it deliberately. Two prefixes are rejected: `bird_`, reserved for our built-in templates, and `emt_`, the template ID format, which a slug could never be distinguished from.
- `name` (string): 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.
- `description` (string): What the template is for, in your own words.
- `category` (string, required)

  Whether the template is for `transactional` email or `marketing` email.

  Possible values: `transactional`, `marketing`
- `source` (string, required)

  The authoring format the template is written in, fixed at creation.
  `html` is finished markup you provide, optionally personalized with
  Liquid. Liquid supports variables, filters, and control flow such
  as `{% if %}` conditionals and `{% for %}` loops. A few constructs are
  rejected when you submit, and the error names exactly what to change:

  - Partial includes (`{% include %}`, `{% render %}`).
  - The `increment`, `decrement`, and `ifchanged` tags.
  - The `money`, `format_date`, `format_time`, `json`, `inspect`, and `type` filters.
  - Comparing against `empty`/`blank` (use `.size == 0` instead).
  - Blocks nested far deeper than real email markup needs.

  A broadcast's template additionally cannot use a `{% for %}` loop,
  because a broadcast supplies one value per contact property, so there
  is nothing to iterate. Send with the messages API instead if the
  template needs one.
- `languages` (object)

  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.
- `default_language` (string): The language a send uses when it does not name one, and the last resort when a requested language is not available. It has to be one of the languages you supply. If you leave it out, we default to `en`, unless you supply exactly one language, in which case we use that one instead. So if you supply two or more languages and `en` is not among them, you have to set this yourself.
- `on_missing_language` (string): What a send does when it asks for a language this template does not carry. Defaults to `fallback` on email.
- `language_source_required` (boolean): 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 template with this set cannot be used for a broadcast, which has no way to name one. Defaults to false.

## Response body

- `id` (string, required): Template ID.
- `workspace_id` (nullable string, required): The workspace that owns the template. Null for a built-in `system` template, which no workspace owns.
- `slug` (string, required): The name you send the template by. You can use either the slug or the id when you send. It never changes after the template is created. A built-in `system` template's slug always starts with `bird_`.
- `name` (string, 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.
- `description` (nullable string, required): What the template is for, in your own words. Null if you have not set one.
- `scope` (string, required)

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

  Possible values: `system`, `workspace`
- `status` (string, 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.

  Possible values: `draft`, `pending`, `active`, `rejected`, `inactive`
- `category` (string, required)

  Whether the template is for `transactional` email or `marketing` email.

  Possible values: `transactional`, `marketing`
- `source` (string, required)

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

  Possible values (may grow over time): `html`
- `theme` (nullable string, required): The visual theme a built-in template is designed in, or null for a template your workspace authored (which has no theme).
- `draft_version_id` (nullable string, required): The current editable draft version. Null for a built-in `system` template, which has no draft.
- `live_version_id` (nullable string, required): The version a send resolves to, or null if the template has never been published.
- `published_version_id` (nullable string, required): Deprecated: use `live_version_id` instead, which carries the same value.
- `revision` (nullable integer, 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.
- `languages` (object, 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_language` (string, required)

  The language the draft defaults to. This is the language `languages` is
  keyed against while you edit, and the language used by sends once you
  submit this draft.

  Until then sends keep using the live version's default, so this can
  differ from what is being sent right now. `available_languages` describes
  the live version for the same reason; read a version to see the default a
  send currently uses.
- `available_languages` (array of string, 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` (string, required): What a send does when it asks for a language this template does not carry. Defaults to `fallback` on email.
- `language_source_required` (boolean, 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, and the template cannot be used for a broadcast, which has no way to name one.
- `last_submitted_at` (nullable string, 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_at` (nullable string, required): When the template was created. Null for a built-in `system` template.
- `updated_at` (nullable string, required): When the template was last modified. Null for a built-in `system` template.

## Related resources

- [How to build an email template](/learn/email/how-to-build-an-email-template) (video)
- [Email templates](/products/email/templates) (product)
- [Build your first integration](/learn/paths/integration) (course)
- [Email templates](/docs/guides/email/templates) (docs)

[Get an implementation brief](/learn/workspace?topic=email-templates)
