# Saved templates

> Couryo templates in HTML or MJML with {{name}} variables, versions and preview, and how to send with template + variables.

Source: https://couryo.com/en/docs/templates

Save the email HTML once and send only the data each time. Templates live in the project, accept HTML or [MJML](https://mjml.io) and use variables in double braces.

## Variables

- Write `{{name}}` in the subject, the HTML or the text. For nested values, use a dot: `{{order.number}}`.
- Accepted names: letters, numbers and `_`, starting with a letter or `_`. An invalid name, such as `{{full name}}`, is refused on save.
- In the HTML, values are **escaped** (`<` becomes `&lt;`), so user data never breaks the layout or injects code.
- Values can be text, numbers or true/false. **Every variable used needs a value**: if one is missing, the send is refused with `invalid_field` and `param` = `variables.<name>`.
- Without a text part, Couryo builds one from the HTML (links keep their address in parentheses).

## Create

```bash title="cURL"
curl https://api.couryo.com/v1/templates \
  -H "Authorization: Bearer $COURYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "welcome",
    "format": "html",
    "subject": "Welcome, {{name}}!",
    "html": "<h1>Hi, {{name}}</h1><p>Your order {{order.number}} is confirmed.</p>"
  }'
```

The response has the `id` (`tpl_...`), the `version` (starts at 1) and the `variables` found. The `name` is unique in the project and can be used instead of the `id`. Creating, changing and deleting need an `admin` key; reading and previewing, `read`.

With `"format": "mjml"`, the MJML is compiled on save; on errors the answer is `invalid_field` with `param` = `html` and the MJML message.

## Send with a template

```bash title="cURL"
curl https://api.couryo.com/v1/emails \
  -H "Authorization: Bearer $COURYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Shop <hello@example.com>",
    "to": "ana@example.com",
    "template": "welcome",
    "variables": { "name": "Ana", "order": { "number": 1042 } }
  }'
```

- With `template`, `subject` is optional (the template's is used); if you send one, it wins.
- `template` and `html`/`text` in the same request: `invalid_field` with `param` = `template`.
- The send uses the template's current version, and the email keeps which one: `GET /v1/emails/{id}` returns `"template": { "id": "tpl_...", "version": 2 }`.
- It also works in the [batch](https://couryo.com/en/docs/sending.md#batch-sending) and in `POST /v1/emails/check`, which checks the content with the variables applied.

## Versions

Changing `subject`, `html` or `text` (`PATCH /v1/templates/{id}`) creates a new version; renaming alone does not. Emails already sent do not change. `GET /v1/templates/{id}/versions` lists them all, newest first.

## Preview

```bash title="cURL"
curl https://api.couryo.com/v1/templates/welcome/preview \
  -H "Authorization: Bearer $COURYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "variables": { "name": "Ana" }, "version": 1 }'
```

Returns `subject`, `html` (MJML already compiled), `text` and `missing_variables`, the list of what a send would miss. Nothing is sent. In the dashboard, the **Templates** screen does the same, with the rendered email on the side.

## Endpoints

| Method and path | Scope | What it does |
|---|---|---|
| `GET /v1/templates` | `read` | lists the project's templates |
| `POST /v1/templates` | `admin` | creates (**201**) |
| `GET /v1/templates/{id}` | `read` | one template (by `id` or `name`), current version |
| `PATCH /v1/templates/{id}` | `admin` | changes `name`, `subject`, `html` or `text` |
| `DELETE /v1/templates/{id}` | `admin` | deletes (**204**) |
| `GET /v1/templates/{id}/versions` | `read` | versions |
| `POST /v1/templates/{id}/preview` | `read` | preview with variables |
