openapi: 3.2.0 info: title: Bird Email Templates API version: 1.0.0 description: 'The Bird API: one REST API for email, SMS, WhatsApp, verification, and Realtime.' servers: - url: https://{region}.platform.bird.com description: 'Regional API endpoint. Use the host for the region your organization is hosted in. Official Bird SDKs and the CLI select it automatically from your API key, so you rarely need to set it by hand. ' variables: region: default: us1 enum: - us1 - eu1 description: The region your organization's data is hosted in. - url: https://platform.bird.com description: Region-independent endpoint for authentication and account administration. - url: http://localhost:8080 description: Local development. security: - BearerAuth: [] tags: - name: Email Templates description: Reusable email templates and their versions, with stored subject, HTML, and plain-text content you manage and reference when sending. paths: /v1/email/templates: post: operationId: createEmailTemplate summary: Create an email template description: '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.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EmailTemplateCreate' responses: '201': description: Email template created. headers: Idempotency-Replay: $ref: '#/components/headers/IdempotencyReplay' content: application/json: schema: $ref: '#/components/schemas/EmailTemplate' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk get: operationId: listEmailTemplates summary: List email templates description: 'Returns a paginated list of email templates, newest first. The list covers both the workspace''s own templates and our built-in `system` templates. Use `scope` to get only one of the two, or leave it out to get both. When you leave it out, the workspace''s own templates come first, newest first, and our built-in templates fill the rest of the list once the workspace''s templates run out. Filter further by category or authoring format, or search with `q`, a case-insensitive substring match against the template''s slug, name, and description. Our built-in templates come in five visual themes. Each theme ships its own set of eight emails, and the sets overlap only partly. Use `theme` to see one of them. A template your workspace authored has no theme, so naming one returns our built-ins alone.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: email.templates.list parameters: - name: scope in: query required: false description: Filter by who owns the template. Use `system` for our built-in templates and `workspace` for the ones your workspace created. Leave it out to get both. schema: $ref: '#/components/schemas/TemplateScope' - name: category in: query required: false description: Return only `transactional` or `marketing` templates; omit to return both categories. schema: $ref: '#/components/schemas/EmailTemplateCategory' - name: source in: query required: false description: Return only templates authored in this format. schema: $ref: '#/components/schemas/EmailTemplateSource' - name: theme in: query required: false description: Filter by the visual theme a built-in template is designed in. Only our built-in templates have a theme, so naming one returns built-ins alone. schema: $ref: '#/components/schemas/EmailTemplateThemeFilter' - name: q in: query required: false description: A case-insensitive substring search across the template's slug, name, and description. schema: type: string minLength: 1 - $ref: '#/components/parameters/PaginationLimit' - $ref: '#/components/parameters/StartingAfter' - $ref: '#/components/parameters/EndingBefore' responses: '200': description: Paginated list of email templates. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateList' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - attio - cli - make - mcp - n8n - sdk /v1/email/templates/{template_ref}: parameters: - name: template_ref in: path required: true description: 'The template''s id (`emt_…`) or slug. On read, a built-in `system` template''s `bird_` slug also resolves here. Write operations (update, delete) accept a workspace template only, because a `system` template is immutable. ' schema: type: string minLength: 1 example: bird_welcome get: operationId: getEmailTemplate summary: Get an email template description: 'Returns a template''s metadata, language states, sendable languages, draft revision, and draft and published version IDs. Read a version''s language to retrieve content. Accepts a workspace template ID (`emt_…`) or a built-in `system` template''s `bird_` slug. A `system` template has `null` for the workspace, draft, revision, and timestamp fields.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none responses: '200': description: The requested email template metadata. content: application/json: schema: $ref: '#/components/schemas/EmailTemplate' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk patch: operationId: updateEmailTemplate summary: Update an email template description: 'Updates a template''s metadata and draft settings, such as its name or default language. Only the fields you send are changed. Content is not edited here: save a language on the draft version instead. Send the draft `revision` you last read. If someone else changed the draft first, including by saving a language, the revision is stale and the request returns a conflict so you can reload and retry.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EmailTemplateUpdate' responses: '200': description: The template with the updated metadata and draft settings. content: application/json: schema: $ref: '#/components/schemas/EmailTemplate' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk delete: operationId: deleteEmailTemplate summary: Delete an email template description: 'Deletes the template and all its versions. The slug becomes available for reuse in the workspace, and the deletion cannot be undone. A template can''t be deleted while a broadcast that has not started sending still uses it, because a `scheduled` or `accepted` broadcast has not pinned the content it will send yet. List the broadcasts blocking a template delete to see which ones those are. A broadcast that has already started sending does not block the delete: it pinned its version when it started, so it keeps sending the content it froze.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' responses: '204': description: Email template deleted. '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/duplicate: parameters: - name: template_ref in: path required: true description: 'The source template to copy: a workspace template''s id (`emt_…`) or slug, or a built-in `system` template''s `bird_` slug. The copy is always a new workspace template. ' schema: type: string minLength: 1 example: welcome-email post: operationId: duplicateEmailTemplate summary: Duplicate an email template description: 'Creates a new template by copying an existing one: one of your workspace templates, or a built-in `system` template (by its `bird_` slug). The copy is a new template with its own id and a single editable draft seeded from the source''s current content. It inherits the source''s category, authoring format (`source`), and description. Copying a workspace template also carries over its default language and its missing-language policy, so the copy behaves like what it was copied from. The copy starts unpublished. By default the copy''s slug derives from the source''s slug, for example `welcome-email-copy`, with a numeric suffix added if that slug is already taken. Supply `slug` to choose your own. A slug already in use in the workspace returns a conflict.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/EmailTemplateDuplicate' responses: '201': description: The newly created template copy. headers: Idempotency-Replay: $ref: '#/components/headers/IdempotencyReplay' content: application/json: schema: $ref: '#/components/schemas/EmailTemplate' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/preview: parameters: - name: template_ref in: path required: true description: 'The template to preview: a workspace template''s id (`emt_…`) or slug, or a built-in `system` template''s `bird_` slug. ' schema: type: string minLength: 1 example: welcome-email post: operationId: getEmailTemplatePreview summary: Get an email template preview description: 'Renders a template with the sample values you supply and returns the resulting subject, HTML, and plain-text bodies: the personalized email as it will look once sent. 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.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/EmailTemplatePreviewRequest' responses: '200': description: The rendered preview. content: application/json: schema: $ref: '#/components/schemas/EmailTemplatePreview' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/versions: parameters: - name: template_ref in: path required: true description: 'The template''s id (`emt_…`) or slug. A built-in `system` template''s `bird_` slug also resolves here, to its one permanently published version. ' schema: type: string minLength: 1 example: emt_01krdgeqcxet5s7t44vh8rt9mg get: operationId: listEmailTemplateVersions summary: List email template versions description: 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. tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/PaginationLimit' - $ref: '#/components/parameters/StartingAfter' - $ref: '#/components/parameters/EndingBefore' responses: '200': description: The template's versions. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateVersionList' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/broadcasts: parameters: - name: template_ref in: path required: true description: 'The template''s id (`emt_…`) or slug. A built-in `system` template''s `bird_` slug also resolves here, but a system template can never be referenced by a broadcast, so this always returns an empty page for one. ' schema: type: string minLength: 1 example: emt_01krdgeqcxet5s7t44vh8rt9mg get: operationId: listEmailTemplateBroadcasts summary: List the broadcasts blocking a template delete description: 'Returns the broadcasts that block deleting this template, as a cursor page, newest first. Those are the ones that have not started sending, so they have not pinned the content they will send: `scheduled` and `accepted`. Clear every one of them and the delete goes through. Canceling clears either status; repointing at another template only works while the broadcast is `scheduled`, because an `accepted` broadcast is committed to send and an edit returns a conflict. A broadcast that is already sending is not listed and does not block the delete, because it froze its version when it started.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/PaginationLimit' - $ref: '#/components/parameters/StartingAfter' - $ref: '#/components/parameters/EndingBefore' responses: '200': description: The broadcasts blocking a delete of the template. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateBroadcastList' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/versions/{version_id}: parameters: - name: template_ref in: path required: true description: 'The template''s id (`emt_…`) or slug. On read, a built-in `system` template''s `bird_` slug also resolves here, to its one permanently published version. Discarding a draft requires a workspace template, because a `system` template has no draft and returns `404` `not_found_error`. ' schema: type: string minLength: 1 example: emt_01krdgeqcxet5s7t44vh8rt9mg - name: version_id in: path required: true schema: $ref: '#/components/schemas/EmailTemplateVersionID' example: emv_01krdgeqcxet5s7t44vh8rt9mg get: operationId: getEmailTemplateVersion summary: Get an email template version description: 'Returns a single version of an email template: - Its lifecycle metadata (`status`, `version_number`, `published_at`). - The content it froze in every language. - The `variables` that content expects at send time. Use List email template versions to enumerate the draft and published versions. Roll back an email template makes an earlier published version live again. Returns a `404 Not Found` error if the template or version does not exist in the workspace.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none responses: '200': description: Email template version object. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk delete: operationId: deleteEmailTemplateVersion summary: Delete an email template draft description: 'Discards the draft''s unsaved work: its content resets to what is currently published (or to a single empty language when nothing has been published yet), so the draft matches what sends actually deliver again. Compare each language''s `content_hash` against the published version''s to see what a discard would throw away. The draft itself remains, ready for new edits at a bumped `revision`, and what is published never changes. Only the draft can be discarded. Addressing a published version returns a `422` `validation_error`, because published versions are permanent history.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' responses: '204': description: The draft was reset. '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/versions/{version_id}/languages: parameters: - name: template_ref in: path required: true description: 'The template''s id (`emt_…`) or slug. A built-in `system` template''s `bird_` slug also resolves here, to its one permanently published version. ' schema: type: string minLength: 1 example: emt_01krdgeqcxet5s7t44vh8rt9mg - name: version_id in: path required: true schema: $ref: '#/components/schemas/EmailTemplateVersionID' example: emv_01krdgeqcxet5s7t44vh8rt9mg get: operationId: listEmailTemplateVersionLanguages summary: List a version's languages description: 'Returns every language the version holds, ordered by language tag, without the content itself, so listing a template with twenty-five languages stays small. Each entry has the language''s `revision` (send it back when you save that language) and its `content_hash`, so you can tell which languages changed since you last read them and fetch only those. Read a single language for its subject and bodies.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none responses: '200': description: The version's languages. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateLanguageList' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/versions/{version_id}/languages/{language}: parameters: - name: template_ref in: path required: true description: 'The template''s id (`emt_…`) or slug. On read, a built-in `system` template''s `bird_` slug also resolves here. Writing or removing a language requires a workspace template, because a `system` template has no draft to edit and returns `404` `not_found_error`. ' schema: type: string minLength: 1 example: emt_01krdgeqcxet5s7t44vh8rt9mg - name: version_id in: path required: true schema: $ref: '#/components/schemas/EmailTemplateVersionID' example: emv_01krdgeqcxet5s7t44vh8rt9mg - name: language in: path required: true schema: $ref: '#/components/schemas/LanguageTag' example: pt-BR get: operationId: getEmailTemplateLanguage summary: Get one language of a version description: 'Returns one language''s content from a version (its subject and bodies) plus the `revision` to send back when saving it. Works on any version, including a published version whose content is frozen. Returns a `404 Not Found` error if the version does not have the language.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none responses: '200': description: The version's content for this language. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateLanguage' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' x-surfaces: - cli - make - mcp - sdk put: operationId: upsertEmailTemplateLanguage summary: Upsert one language of a draft description: 'Saves one language''s content on the template''s draft, creating that language if the draft does not have it yet and replacing it in full if it does. Send the same content twice and the draft ends up the same way, so a sync job or CI run needs one call rather than a create-or-edit decision. Saving one language leaves every other language untouched, so a template with many languages can be edited a language at a time. Send the `revision` you last read to have a concurrent edit rejected with a `409` `conflict_error` instead of silently overwritten. Only a draft can be saved: addressing a published version returns a `422` `validation_error`, because a published version never changes. A template holds at most 25 languages.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EmailTemplateLanguageUpsert' responses: '200': description: The saved language's new revisions and fingerprint. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateLanguageSaved' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk patch: operationId: updateEmailTemplateLanguage summary: Update one language of a draft description: 'Changes part of one language''s content on the template''s draft: send only the fields you are changing and the rest keep their current values. Use this to fix a subject line without resending a megabyte of HTML. The language must already exist on the draft. A language it does not have returns a `404` `not_found_error`. Save the language''s full content instead to create it. Send the `revision` you last read to have a concurrent edit rejected with a `409` `conflict_error`. Only a draft can be edited. Addressing a published version returns a `422` `validation_error`.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EmailTemplateLanguageUpdate' responses: '200': description: The edited language's new revisions and fingerprint. content: application/json: schema: $ref: '#/components/schemas/EmailTemplateLanguageSaved' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk delete: operationId: deleteEmailTemplateLanguage summary: Delete one language from a draft description: 'Removes one language from the template''s draft, along with its content. Every other language is untouched, and the change takes effect for sends when you next submit. The draft''s current default language cannot be removed on its own. Point the default at a different language first, then remove the old one; doing it in the other order returns a `422` `validation_error`. Removing a language the draft does not have returns a `404` `not_found_error`, and only a draft can be changed, so addressing a published version also returns a `422` `validation_error`.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' responses: '204': description: The language was removed. '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/versions/{version_id}/rollback: parameters: - name: template_ref in: path required: true description: 'The template''s id (`emt_…`) or slug. Rollback resets a draft, and a built-in `system` template has no draft, so its `bird_` slug returns `404` `not_found_error` here. ' schema: type: string minLength: 1 example: emt_01krdgeqcxet5s7t44vh8rt9mg - name: version_id in: path required: true schema: $ref: '#/components/schemas/EmailTemplateVersionID' example: emv_01krdgeqcxet5s7t44vh8rt9mh post: operationId: rollbackEmailTemplate summary: Roll back an email template description: Makes an earlier published version the live version used by sends, and replaces the draft with that version's content so editing continues from it. The change is immediate. No new version is created, and the version history is unchanged. Include the draft `revision` you last read, so the request is rejected with a conflict if someone else changed the draft first. The draft's current content is replaced. Rolling back to the currently live version is allowed and resets the draft to it. Only published versions can be rolled back to. tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EmailTemplateRollback' responses: '200': description: The template, with its draft reset to the restored version's content. content: application/json: schema: $ref: '#/components/schemas/EmailTemplate' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk /v1/email/templates/{template_ref}/versions/{version_id}/submit: parameters: - name: template_ref in: path required: true description: '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. ' schema: type: string minLength: 1 example: emt_01krdgeqcxet5s7t44vh8rt9mg - name: version_id in: path required: true schema: $ref: '#/components/schemas/EmailTemplateVersionID' example: emv_01krdgeqcxet5s7t44vh8rt9mh post: operationId: submitEmailTemplateVersion summary: Submit an email template version description: '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.' tags: - Email Templates security: - BearerAuth: [] - CookieAuth: [] x-audiences: - public - command x-snippet-key: none parameters: - $ref: '#/components/parameters/IdempotencyKey' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/EmailTemplateSubmit' responses: '200': description: The submit's outcome, including the frozen version unless this was a validation run. headers: Idempotency-Replay: $ref: '#/components/headers/IdempotencyReplay' content: application/json: schema: $ref: '#/components/schemas/EmailTemplateSubmitResult' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - sdk components: schemas: EmailTemplateLanguageList: type: object additionalProperties: false required: - data properties: data: type: array description: 'Every language the version holds, ordered by language tag, without their content. Read a single language to get its content. ' items: $ref: '#/components/schemas/EmailTemplateLanguageSummary' LanguageTag: type: string minLength: 2 maxLength: 35 description: A language tag in BCP-47 form, for example `en` or `pt-BR`. example: pt-BR EmailTemplateSummary: type: object additionalProperties: false required: - id - workspace_id - slug - name - scope - status - category - source - theme - draft_version_id - live_version_id - published_version_id - live_version_number - languages - default_language - available_languages - description - last_submitted_at - created_at - updated_at properties: id: readOnly: true description: Template ID. $ref: '#/components/schemas/EmailTemplateID' workspace_id: readOnly: true description: The workspace that owns the template. Null for a built-in `system` template, which no workspace owns. oneOf: - $ref: '#/components/schemas/WorkspaceID' - type: 'null' slug: allOf: - $ref: '#/components/schemas/TemplateSlug' readOnly: true description: 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_`. example: welcome-email name: type: string minLength: 1 maxLength: 255 description: 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. example: Welcome email description: type: - string - 'null' description: What the template is for, in your own words. Null if you have not set one. scope: example: workspace $ref: '#/components/schemas/TemplateScope' status: $ref: '#/components/schemas/TemplateStatus' category: $ref: '#/components/schemas/EmailTemplateCategory' source: $ref: '#/components/schemas/EmailTemplateSource' theme: readOnly: true description: The visual theme a built-in template is designed in, or null for a template your workspace authored (which has no theme). example: null oneOf: - $ref: '#/components/schemas/EmailTemplateTheme' - type: 'null' draft_version_id: readOnly: true description: The current editable draft version. Null for a built-in `system` template, which has no draft. oneOf: - $ref: '#/components/schemas/EmailTemplateVersionID' - type: 'null' live_version_id: readOnly: true description: The version a send resolves to, or null if the template has never been published. oneOf: - $ref: '#/components/schemas/EmailTemplateVersionID' - type: 'null' published_version_id: readOnly: true deprecated: true description: 'Deprecated: use `live_version_id` instead, which carries the same value. ' oneOf: - $ref: '#/components/schemas/EmailTemplateVersionID' - type: 'null' live_version_number: type: - integer - 'null' minimum: 1 readOnly: true description: 'The live version''s sequential number (1, 2, 3…), the same one version history reports, or null if the template has never been published. A built-in `system` template is permanently published as version 1. A rollback moves it backwards, because it names the version that is live rather than how many exist. ' available_languages: type: array readOnly: true items: $ref: '#/components/schemas/LanguageTag' description: 'The languages this template currently supports for sending, as BCP-47 tags. Empty until the template is published, because sends serve published content. This set may shrink for reasons other than editing, so read it rather than assuming it matches what was published. ' example: - en default_language: $ref: '#/components/schemas/LanguageTag' readOnly: true description: 'The language the draft defaults to. Read this language''s content when you have no particular preference for which one you want. ' languages: type: object readOnly: true propertyNames: $ref: '#/components/schemas/LanguageTag' additionalProperties: $ref: '#/components/schemas/EmailTemplateLanguageState' description: 'Every language this template has, keyed by language tag, each with its state. Enough to show which templates need attention in a list without a request per row. ' example: en: status: live de: status: draft last_submitted_at: type: - string - 'null' format: date-time readOnly: true description: 'When this template was last submitted. Null if it never has been. Only submitting moves this timestamp, so a rollback keeps reporting the last real submit. ' created_at: type: - string - 'null' format: date-time readOnly: true description: When the template was created. Null for a built-in `system` template. updated_at: type: - string - 'null' format: date-time readOnly: true description: When the template was last modified. Null for a built-in `system` template. EmailTemplateSubmitProblem: type: object additionalProperties: false description: One problem found while checking whether a version can be submitted. required: - code - message properties: language: readOnly: true description: 'The language this problem is about. Null when the problem is about the whole version rather than one language, for example an empty draft, or a default language the draft does not have. ' oneOf: - $ref: '#/components/schemas/LanguageTag' - type: 'null' field: type: - string - 'null' minLength: 1 readOnly: true description: 'Which field within that language has the problem, such as `subject` or `html`. Null when the problem is not about one particular field. ' example: subject code: type: string minLength: 1 pattern: ^E\d{5}$ readOnly: true description: 'The error code a real submit would fail with. Look it up in the error catalog to see what it means and what to do about it. ' example: E04051 message: type: string minLength: 1 readOnly: true description: 'What is wrong, worded so you can show it directly to whoever is authoring the template. ' example: A subject is required to submit. EmailTemplateLanguageState: type: object additionalProperties: false description: 'Where one of the template''s languages stands: whether sends are using it, and whether its draft contains an unpublished edit. ' required: - status properties: status: $ref: '#/components/schemas/TemplateLanguageStatus' draft: type: boolean readOnly: true description: 'Whether the draft holds an edit to this language that has not been published. If this is true and the status is `live`, sends are still using the older content, and your edit goes out the next time you submit. ' EmailTemplatePreviewContent: type: object additionalProperties: false description: 'Content to render instead of the template''s stored draft. Give it the subject and bodies you have in hand and they are rendered exactly as the draft would be, so an editor can show what a change looks like before it is saved. ' properties: subject: type: string maxLength: 998 description: The subject line to render. example: Welcome to Acme, {{ bird.contact.first_name }}! preview_text: type: string maxLength: 255 description: 'The preview text to render. It is folded into the top of the HTML the same way publishing folds it, so the rendered body carries the hidden preheader a recipient''s inbox would read. ' example: '{{ bird.contact.first_name }}, your order is on its way' html: type: string maxLength: 524288 description: The HTML body to render. example: