# List email template versions

`GET /v1/email/templates/{template_ref}/versions`

Returns the template's versions as a cursor page (the current draft plus all published versions), newest first. Each entry names its languages without returning their content. Templates retain every language of every submitted version, so listing their content would grow the response with the template's history. Read a single version for its content.

## Code samples

### CLI

```sh
bird email templates versions list <template-ref>
```

### cURL

```sh
curl -X GET "https://us1.platform.bird.com/v1/email/templates/{template_ref}/versions" \
  -H "Authorization: Bearer $TOKEN" \
  --url-query "limit=25"
```

## Example response `200`

```json
{
  "data": [
    {
      "id": "emv_01krdgeqcxet5s7t44vh8rt9mg",
      "template_id": "emt_01krdgeqcxet5s7t44vh8rt9mg",
      "status": "published",
      "variables": [
        {
          "type": "code"
        }
      ],
      "default_language": "pt-BR",
      "available_languages": [
        "en",
        "pt-BR"
      ],
      "updated_by": {
        "id": "usr_01krdgeqcxet5s7t44vh8rt9mg",
        "type": "user"
      }
    }
  ],
  "next_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9",
  "prev_cursor": null,
  "refresh_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"
}
```

## Path parameters

- `template_ref` (string): The template's id (`emt_…`) or slug. A built-in `system` template's `bird_` slug also resolves here, to its one permanently published version.

## Query parameters

- `limit` (integer): Maximum number of items to return per page.
- `starting_after` (string): Cursor from the `next_cursor` field of a previous list response. Returns items immediately after the cursor position in the current sort order.
- `ending_before` (string): Cursor from the `prev_cursor` or `refresh_cursor` field of a previous list response. Returns items immediately before the cursor position in the current sort order. `prev_cursor` returns the preceding page. `refresh_cursor` anchors at the first row of that response, which on a newest-first sort is how to fetch the items that have appeared since.

## Response body

- `data` (array of object, required): One page of the template's versions, newest first. Each entry describes a version and which languages it holds. Read a single version if you want its actual content.
- `data.id` (string, required): Template version ID.
- `data.template_id` (string, required): The template this version belongs to.
- `data.version_number` (nullable integer): Sequential published-version number (1, 2, 3…). Null while the version is a draft.
- `data.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`
- `data.revision` (integer, required): The version's revision counter.
- `data.variables` (array of object, required)

  Every variable this version's content uses. You supply a value for each of them when you send.

  The list combines all the languages, because languages do not have to use the same variables: if the English body uses `discount_code` and the French body uses `shipping_date`, both appear here. Send a value for every variable in the list rather than only the ones you expect the language you are sending to use. A language that does not use a variable ignores the value you sent for it, and a variable the sent language does use but you left out is rejected with a `422` naming it.

  Variables under the reserved `bird.` namespace are not listed here. We fill those in ourselves from the recipient's contact record.
- `data.variables.key` (string, required): The key this slot is filled by. On email and SMS it is the key you set in the send's `parameters` object. On WhatsApp it is 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.
- `data.variables.type` (string, required)

  The value type this slot accepts. SMS templates use the typed slots (`code`, `amount` and the rest), each of which rejects a value that does not match its `constraint`. Email and WhatsApp templates use `text`, which accepts any value. 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`
- `data.variables.required` (boolean, required): Whether the send must supply this variable. Omitting a required value returns `422` on email, SMS, and WhatsApp sends.
- `data.variables.constraint` (string, required): A plain-language description of what values this variable accepts.
- `data.variables.sensitive` (boolean): Whether this slot's value is kept out of durable storage. A sensitive slot's rendered value never appears in message content read back through the API: a stand-in placeholder is stored instead.
- `data.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.
- `data.available_languages` (array of string, required): The languages this version holds, as BCP-47 tags: the keys its `languages` map would return, without the content itself.
- `data.created_at` (string, required): When this version was created.
- `data.published_at` (nullable string): When this version was published, or null if it has not been published.
- `data.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).
- `data.updated_by.id` (string, required): Actor identifier.
- `data.updated_by.type` (string, required)

  Who or what performed the action: `user` for a member's own session, `oauth_token` for a token issued to a caller on a member's behalf, `api_key` for a workspace API key, `system` for our own automation, `sso` for an organization's SSO connection, and `service_account` for a workspace's connected Integration acting with no member behind it. Open enum: new actor types may be added over time, so treat any unrecognized value as a future type rather than an error.

  Possible values (may grow over time): `user`, `api_key`, `oauth_token`, `system`, `sso`, `service_account`
- `data.updated_by.display_name` (nullable string): The label the actor is shown under: typically a member's name or email address, or the API key's name. Null when it could not be resolved.
- `next_cursor` (nullable string, required): Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.
- `prev_cursor` (nullable string, required): Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.
- `refresh_cursor` (nullable string, required): Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.

## 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)
