Перейти к основному содержимому

Шаблоны писем

Шаблон хранит тему и тело письма на сервере. В запросе отправки достаточно передать slug и переменные — рендеринг выполняется через Jinja2 в безопасной песочнице (без доступа к файловой системе и внешним I/O).

Шаблоны создаются и редактируются в дашборде: Проект → Шаблоны. У каждого шаблона есть уникальный в проекте slug и автоинкрементный version — он повышается при каждом обновлении.

Отправка по шаблону

curl -X POST https://api.mailinfra.ru/v1/emails \
-H "Authorization: Bearer mi_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"from": "no-reply@ваш-домен.ru",
"to": ["user@example.com"],
"template": "welcome",
"variables": {
"user_name": "Анна",
"app_name": "MyApp"
}
}'

При использовании template поля subject, html, text берутся из шаблона — их можно не передавать. Можно при желании переопределить любое из них прямо в запросе.

Slug

  • только строчные буквы, цифры, - и _
  • начинается с буквы или цифры
  • до 100 символов
  • уникален в пределах проекта

Примеры: welcome, password-reset, order_confirmation, email-verification.

Синтаксис Jinja2

Подстановка

{{ variable_name }}
{{ order.total | round(2) }}
{{ code | upper }}

Условия

{% if promo_code %}
<p>Промокод: <strong>{{ promo_code }}</strong></p>
{% endif %}

Циклы

<ul>
{% for item in order_items %}
<li>{{ item.name }} — {{ item.qty }} шт. × {{ item.price }} ₽</li>
{% endfor %}
</ul>

Экранирование HTML

В HTML-шаблонах переменные автоматически экранируются — это защита от XSS:

{# user_input = "<script>alert(1)</script>" #}
<p>{{ user_input }}</p>
{# рендерится как: <p>&lt;script&gt;alert(1)&lt;/script&gt;</p> #}

Если значение действительно содержит доверенный HTML и нужно его вывести как есть — используйте фильтр | safe. Применяйте только к контенту, который контролируете сами.

{{ trusted_html | safe }}

Готовые шаблоны

Подтверждение email

subject: Подтвердите email — {{ app_name }}
html: <p>Ваш код: <strong>{{ code }}</strong>. Действителен {{ expires_min }} мин.</p>
text: Код: {{ code }}\nДействителен {{ expires_min }} мин.

Сброс пароля

subject: Сброс пароля
html: <p><a href="{{ reset_url }}">Сбросить пароль</a></p><p>Ссылка живёт {{ expires_hours }} ч.</p>
text: Сброс пароля: {{ reset_url }}\nСсылка живёт {{ expires_hours }} ч.

Подтверждение заказа

subject: Заказ #{{ order_number }} подтверждён
html: <h2>Заказ #{{ order_number }}</h2>
<table>
{% for item in items %}
<tr><td>{{ item.name }}</td><td>{{ item.qty }} шт.</td><td>{{ item.price }} ₽</td></tr>
{% endfor %}
</table>
<p><strong>Итого: {{ total }} ₽</strong></p>

Возможные ошибки при отправке

СитуацияHTTPКод
Slug не найден при отправке422template_not_found
Не передана переменная, использованная в шаблоне422template_render_error
Синтаксическая ошибка в Jinja2422template_invalid

Управление шаблонами

CRUD шаблонов — часть Management API: операции идут на /v1/organizations/{organization_id}/projects/{project_id}/templates с сессионным токеном.

МетодПутьМинимальная рольОписание
GET/templatesVIEWERСписок шаблонов проекта
POST/templatesDEVELOPERСоздать шаблон
GET/templates/{id}VIEWERОдин шаблон со всеми полями
PATCH/templates/{id}DEVELOPERИзменить шаблон, поднимает version
DELETE/templates/{id}DEVELOPERУдалить шаблон

Создание

POST /v1/organizations/{organization_id}/projects/{project_id}/templates
Authorization: Bearer <session_token>
Content-Type: application/json
{
"name": "Приветственное письмо",
"slug": "welcome",
"subject_template": "Добро пожаловать, {{ user_name }}!",
"html_template": "<h1>Привет, {{ user_name }}!</h1><p>Аккаунт в {{ app_name }} создан.</p>",
"text_template": "Привет, {{ user_name }}!\n\nАккаунт в {{ app_name }} создан.",
"variables": {
"user_name": "Иван",
"app_name": "MyApp"
}
}
ПолеТипОбязательноОписание
namestringдаЧеловекочитаемое имя (1–200 символов)
slugstringдаСм. правила slug; уникален в проекте
subject_templatestringдаТема, без переводов строк
html_templatestring*HTML-тело
text_templatestring*Текстовое тело
variablesobjectПодсказки и примеры значений переменных, видны в дашборде

* Хотя бы одно из html_template / text_template должно быть указано.

Поле variables — это документация, не значения по умолчанию

Это пример того, какие переменные ждёт шаблон, и какие значения они принимают. При отправке передавайте все нужные переменные в теле запроса — содержимое variables шаблона не подставляется автоматически.

Ответ 201 Created:

{
"data": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"project_id": "...",
"name": "Приветственное письмо",
"slug": "welcome",
"subject_template": "Добро пожаловать, {{ user_name }}!",
"html_template": "...",
"text_template": "...",
"variables": { "user_name": "Иван", "app_name": "MyApp" },
"version": 1,
"created_at": "2026-05-12T09:00:00Z",
"updated_at": "2026-05-12T09:00:00Z"
}
}

Обновление и версионирование

PATCH /v1/.../templates/{template_id}
Authorization: Bearer <session_token>
Content-Type: application/json
{
"subject_template": "Привет, {{ user_name }} 👋",
"html_template": "<p>Новая верстка</p>"
}

При каждом успешном PATCH поле version увеличивается на 1 — это нужно, чтобы вы могли видеть в логах писем, какой версией шаблона отрендерено каждое письмо. Slug изменить нельзя — для смены slug создайте новый шаблон.

Ошибки управления

СитуацияHTTPКод
Slug уже занят в проекте409conflict
Синтаксическая ошибка в Jinja2 при создании/обновлении422template_invalid
Шаблон не найден или не принадлежит проекту404not_found