# Email Templates API

The Email Templates API manages the reusable email templates behind an application's email presets — the same templates you build in the Control Panel's email editor. Each template stores per-locale subjects, sender info, and editor content, and is identified by the code of the email preset it's connected to. Use that code to send the template through [`Notify`](/developer/api-reference/messaging-api-v2/notify/) (email payload [`email_template`](/developer/api-reference/messaging-api-v2/email-payload-reference/#emailpayload)) or a Customer Journey [Send email point](/developer/api-reference/customer-journey-api/point-reference/#messaging-points).

## Base URL

```
https://rpc-api.svc-nue.pushwoosh.com
```

All endpoints are served over HTTPS. Requests and responses use `application/json` unless noted otherwise.

## Authentication

Every request must include an `Authorization` header with your [Server API token](/developer/api-reference/api-access-token/#server-api-token):

```
Authorization: Api YOUR_API_TOKEN
```

## Conventions

* **Field naming:** request bodies and query/path parameters accept `lowerCamelCase` (for example, `previewSettings`, `searchByLabel`, `includeHtml`) — the server unmarshals either casing. Responses are always marshaled using the proto field names, in `snake_case` (`per_page`, `email_template`, `sender_info`, `preview_settings`, and so on). The response examples and the [object reference](#object-reference) below use that casing.
* **`code`:** every template response carries the code of its connected email preset, not an internal template ID. Pass this same code to `Get`, `Update`, `Delete`, and to the messaging/journey APIs above.
* **Unpopulated fields:** responses include all fields, even when empty or zero-valued.

### Error responses

| HTTP status | Meaning |
| :---- | :---- |
| `400 Bad Request` | Invalid argument — a required field is missing or malformed, or a precondition failed (for example, deleting a template still used by a journey). |
| `401 Unauthorized` | Missing or invalid `Authorization` header. |
| `403 Forbidden` | The application or preset does not belong to the caller's account. |
| `404 Not Found` | The template, preset, or application was not found. |
| `500 Internal Server Error` | Unexpected server-side failure. |

## Endpoints

| Method | Path | Description |
| :---- | :---- | :---- |
| `POST` | `/api/email_templates` | Create a new email template |
| `GET` | `/api/email_templates` | List an application's email templates |
| `GET` | `/api/email_templates/{code}` | Get a single email template |
| `PUT` | `/api/email_templates/{code}` | Update an email template |
| `DELETE` | `/api/email_templates/{code}` | Delete an email template |
| `POST` | `/api/email_templates:clone` | Clone an email template into an application |

## Create

Creates a new email template — its editor content plus a connected email preset — in an application, and returns the generated template code.

`POST` `/api/email_templates`

### Request body

| Parameter | Type | Required | Description |
| :---- | :---- | :---- | :---- |
| `application` | string | Yes | The [Pushwoosh application code](/developer/api-reference/api-identifiers/#application-code) to create the template in. |
| `name` | string | Yes | Template name, 1–255 characters. |
| `content` | object | Yes | The [email content object](#email-content-object). |
| `label` | string | No | Free-text label, up to 255 characters. |
| `categories` | array of strings | No | Category names to tag the template with. |
| `previewSettings` | object | No | Arbitrary editor preview settings, stored and returned as-is. |
| `system` | boolean | No | Marks the template as a system template — an internal feature, e.g. a synced-block fragment. System templates are hidden from `List` (see note below), but stay reachable by code. Defaults to `false`. |

##### Request example

```json
{
  "application": "XXXXX-XXXXX",
  "name": "Welcome email",
  "label": "onboarding",
  "categories": ["lifecycle"],
  "content": {
    "senderInfo": {
      "from": { "email": "hello@acme.com", "name": "Acme" },
      "replyTo": { "email": "support@acme.com", "name": "Acme Support" }
    },
    "subject": { "en": "Welcome to Acme!", "default": "Welcome to Acme!" },
    "mjml": {
      "mjml": "<mjml><mj-body><mj-section><mj-column><mj-text>Welcome, {name|string|there}!</mj-text></mj-column></mj-section></mj-body></mjml>",
      "html": "<html>...</html>",
      "localizationData": { "default": { "name": "there" } },
      "settings": {}
    }
  }
}
```

### Response

Returns `{ "email_template": { ... } }` — the created [email template object](#email-template-object), but **without** `content` (this endpoint doesn't echo it back). Call [Get](#get) with the returned `code` if you need to read the content back.

## List

Lists an application's email templates — metadata only, no content — with paging, ordering, and filtering by name, label, or category.

`GET` `/api/email_templates`

### Query parameters

| Parameter | Type | Required | Description |
| :---- | :---- | :---- | :---- |
| `application` | string | Yes | The application code to list templates for. |
| `orderBy` | string | No | `NAME` (default), `CREATED`, or `UPDATED`. |
| `orderDirection` | string | No | `ASC` (default) or `DESC`. |
| `page` | integer | No | Zero-based page index. |
| `perPage` | integer | No | Page size. Defaults to `100` when omitted or `0`. This endpoint doesn't enforce an explicit maximum. |
| `searchByName` | string | No | Substring match (`like %value%`) against the template's name **or** its code — either matching is enough. |
| `searchByLabel` | string | No | Substring match on label (`like %label%`), or exact match when `strictSearchByLabel` is `true`. |
| `strictSearchByLabel` | boolean | No | Use exact-match instead of substring for `searchByLabel`. |
| `searchByCategory` | array of strings | No | Repeat the parameter to filter by any of several categories, e.g. `?searchByCategory=lifecycle&searchByCategory=promo`. |

<Aside type="note">
System templates (created with `system: true`) are never returned by `List`, regardless of filters.
</Aside>

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `email_templates` | array of objects | The current page of [email template objects](#email-template-object). `content` is `null` on every item. |
| `page` | integer | The returned page index. |
| `per_page` | integer | The page size used for this response. |
| `total` | integer | Total number of templates matching the filters, across all pages. |

##### Response example

```json
{
  "email_templates": [
    { "code": "AAAAA-BBBBB", "name": "Welcome email", "label": "onboarding", "categories": ["lifecycle"] }
  ],
  "page": 0,
  "per_page": 100,
  "total": 1
}
```

## Get

Returns a single email template by its code, including sender info, per-locale subjects, and the full editor content.

`GET` `/api/email_templates/{code}`

### Path parameters

| Parameter | Type | Description |
| :---- | :---- | :---- |
| `code` | string | The template's code (its connected email preset code). |

### Query parameters

| Parameter | Type | Required | Description |
| :---- | :---- | :---- | :---- |
| `includeHtml` | boolean | No | Whether to return the rendered `html` alongside the editor content. Defaults to `true`. Set to `false` to skip it — it's typically over half the payload, and the editor content already describes the template. |

### Response

Returns `{ "email_template": { ... } }`, the full [email template object](#email-template-object).

## Update

Updates an existing email template by code, overwriting the supplied fields.

`PUT` `/api/email_templates/{code}`

### Path parameters

| Parameter | Type | Description |
| :---- | :---- | :---- |
| `code` | string | The template's code to update. |

### Request body

| Parameter | Type | Required | Description |
| :---- | :---- | :---- | :---- |
| `name` | string | No | New name, 1–255 characters. Omit to keep the current name. |
| `content` | object | No | New [email content object](#email-content-object), replacing the stored content in full. Omit to leave the content unchanged. |
| `label` | string | No | New label. Always overwritten — omit or send `""` to clear it. |
| `categories` | array of strings | No | New full set of category names. Omit to leave categories unchanged; send `[]` to clear them. |
| `previewSettings` | object | No | New preview settings. Omit to leave unchanged. |

##### Request example

```json
{
  "name": "Welcome email v2",
  "label": "onboarding",
  "content": {
    "senderInfo": { "from": { "email": "hello@acme.com", "name": "Acme" } },
    "subject": { "default": "Welcome to Acme — updated!" },
    "mjml": {
      "mjml": "<mjml>...</mjml>",
      "html": "<html>...</html>",
      "localizationData": {},
      "settings": {}
    }
  }
}
```

### Response

Returns `{ "email_template": { ... } }` — the updated [email template object](#email-template-object), also **without** `content`. Call [Get](#get) if you need to read the content back.

## Delete

Deletes an email template and its connected preset by code, removing the stored content.

`DELETE` `/api/email_templates/{code}`

<Aside type="caution">
Fails with `400 Bad Request` if the template is used by a `send_email` point in a running or paused journey — remove or repoint that point first via [Update](/developer/api-reference/customer-journey-api/create-update/#update). This cannot be undone.
</Aside>

### Path parameters

| Parameter | Type | Description |
| :---- | :---- | :---- |
| `code` | string | The template's code to delete. |

### Response

An empty object on success: `{}`.

## Clone

Clones an email template — its content and preset — into a destination application, optionally under a new name.

`POST` `/api/email_templates:clone`

<Aside type="note">
Only the `default` (or `en`, used as a fallback) locale subject is preserved on the clone; other localized subjects are dropped.
</Aside>

### Request body

| Parameter | Type | Required | Description |
| :---- | :---- | :---- | :---- |
| `emailPresetCode` | string | Yes | The template's `code` (as returned by `Create`, `Get`, `List`, or `Update`) to clone. Named `emailPresetCode` here because it's the code of the connected email preset — see [Conventions](#conventions). |
| `application` | string | Yes | Destination application code. Can be the same application, or a different one owned by the same account. |
| `name` | string | No | Name for the clone, 1–255 characters. Defaults to the source template's name. |

##### Request example

```json
{
  "emailPresetCode": "AAAAA-BBBBB",
  "application": "YYYYY-YYYYY",
  "name": "Welcome email (copy)"
}
```

### Response

| Field | Type | Description |
| :---- | :---- | :---- |
| `email_preset_code` | string | The new template's `code` — the same identifier `Get`/`Update`/`Delete` call `code`. |

## Object reference

Field names below match what `Get`, `List`, `Update`, and `Create` actually return — `snake_case` proto field names (see [Conventions](#conventions)). When you send these same structures back in a request body (`Create`, `Update`), the `lowerCamelCase` form used in the request examples above works too; the server accepts either casing on input.

### Email template object

| Field | Type | Description |
| :---- | :---- | :---- |
| `code` | string | Code of the connected email preset. Identifies this template everywhere else in the API. |
| `name` | string | Template name. |
| `label` | string | Free-text label. |
| `categories` | array of strings | Category names. |
| `content` | object | The [email content object](#email-content-object). Populated only by `Get`; `null` in `Create`, `List`, and `Update` responses. |
| `preview_settings` | object | Arbitrary editor preview settings. |
| `created` | string (RFC 3339) | Creation timestamp. |
| `updated` | string (RFC 3339) | Last update timestamp. |

### Email content object

| Field | Type | Description |
| :---- | :---- | :---- |
| `sender_info` | object | [Sender info object](#sender-info-object) — `from` and `reply_to` addresses. |
| `subject` | object (map) | Per-locale subject, e.g. `{ "en": "Subject", "default": "Subject" }`. |
| `unlayer` / `pushwoosh` / `mjml` / `smartcards` / `vibeblocks` | object | The editor content. **Exactly one** of these must be set — it selects which editor produced (and will render) the template. See [editor kinds](#editor-kinds) below. |

<Aside type="caution">
The proto also defines a `raw` editor kind (per-locale HTML with no editor state). The API currently rejects it on `Create`/`Update` with `400 Bad Request: raw content not supported` — don't use it.
</Aside>

#### Editor kinds

| Kind | Field | Required sub-fields | Description |
| :---- | :---- | :---- | :---- |
| `unlayer` | `html`, `localization_data`, `editor_config` | `editor_config`, `localization_data` | Drag-and-drop block editor (Unlayer). `editor_config` is the Unlayer design JSON. |
| `pushwoosh` | `html`, `localization_data` | `localization_data` | Pushwoosh's own HTML-based editor. |
| `mjml` | `mjml`, `html`, `localization_data`, `settings` | `mjml`, `localization_data`, `settings` | [MJML](https://mjml.io/) source, plus the last-rendered `html`. Recommended for programmatic/API-authored templates. |
| `smartcards` | `html`, `localization_data`, `content` | `content`, `localization_data` | Smart Cards block editor; `content` is its editor-specific JSON. |
| `vibeblocks` | `html`, `localization_data`, `content` | `content`, `localization_data` | Vibeblocks block editor; `content` is its editor-specific JSON. |

In every kind, `html` is the rendered output. `localization_data` is that editor's own per-locale content: an object keyed by locale code (`en`, `es`, `default`, …), where each value is that locale's copy of the editor's fields. Its inner shape is editor-specific and opaque to this API — the API stores and returns it as-is. It's required on `Create`/`Update` for every kind (send `{}` if there's nothing to localize).

<Aside type="note">
Whenever the API returns `content` (currently only [Get](#get), since [Create](#create) and [Update](#update) don't echo it back), it guarantees one locale is exposed as `default`: if you didn't send a `default` key yourself, it promotes `en` (or, failing that, whichever locale key comes first) to `default`, renaming it — every other locale you sent is kept as-is. `subject` gets the same treatment. This is different from [Clone](#clone), which keeps only that one default-locale value and drops the rest.

This rename is only applied when building the response — but it's not undone on write. If you take a `Get` response as-is and send it straight to `Update`, the promoted key (e.g. `en`) is now literally `default`, and `Update` stores exactly what you send: the original `en` key is gone from then on, permanently. If you need to keep the original locale key, rename `default` back to it yourself before calling `Update`.
</Aside>

Text inside `html`, `mjml`, or a `localization_data` value can include [Dynamic Content](/developer/guides/personalization/dynamic-content/) tags, e.g. `{name|string|there}` — those resolve against the recipient's device Tags when the email is actually sent. This API doesn't resolve them; it just stores and returns whatever text you put there.

### Sender info object

| Field | Type | Description |
| :---- | :---- | :---- |
| `from` | object | `{ "email": string, "name": string }` — sender address. |
| `reply_to` | object | `{ "email": string, "name": string }` — reply-to address. |

Both `email` sub-fields, when non-empty, must be valid email addresses.

## Related

<CardGrid>
  <LinkCard title="Notify" href="/developer/api-reference/messaging-api-v2/notify/" />
  <LinkCard title="Email payload reference" href="/developer/api-reference/messaging-api-v2/email-payload-reference/" />
  <LinkCard title="Customer Journey point reference" href="/developer/api-reference/customer-journey-api/point-reference/#messaging-points" />
</CardGrid>