Транзакционные письма
Одиночные письма конкретному получателю: подтверждения заказов, коды входа, уведомления, чеки.
Транзакционное письмо не требует адресной базы и уходит сразу. От массовой рассылки оно отличается ещё и тем, что не нуждается в ссылке отписки — такие письма считаются служебными.
Перед первой отправкой нужно подтвердить домен.
Домен из from_email должен быть добавлен и проверен в
разделе Аккаунт → Домены: без
настроенных DKIM и SPF письма не уйдут, а /transactional/log
и /transactional/stats вернут code: 34.
Статусы письма
Числовой status и его текстовое имя statusname
показывают последнее известное событие письма:
5 — Sent | Отправлено с наших серверов |
3 — Delivered | Принято почтовым сервером получателя |
7 — Opened | Письмо открыли |
6 — Clicked | Кликнули по ссылке |
4 — Bounced | Возврат, причина в errormessage |
9 — Complained | Получатель пожаловался на спам |
8 — Unsubscribed | Получатель отписался |
Свой идентификатор письма
Если не передать message_id, DashaMail сгенерирует его сам и
вернёт в поле transaction_id. Удобнее задавать свой — тогда
вы сможете сопоставить письмо со своей записью в базе, не сохраняя чужой
идентификатор.
- POST Отправить письмо
- GET Статус письма
- GET Журнал отправок
- GET Статистика отправок
Отправить письмо #
POST
/transactional/messages
Ставит письмо в очередь отправки и сразу возвращает его идентификатор. Фактическая доставка происходит асинхронно — следите за ней через проверку статуса или webhooks.
Нужны как минимум три вещи: получатель, отправитель
(from_email либо заголовок From внутри
headers) и текст письма (message
либо plain_text).
В to можно перечислить несколько адресов через
запятую. Тогда каждому уйдёт отдельное письмо, а в
message_id можно передать столько же
идентификаторов, тоже через запятую.
Параметры тела запроса
| Параметр | Тип | Описание |
|---|---|---|
to обязательный | string | Адрес получателя. Допустима форма Иван Петров <ivan@example.com>. Несколько адресов перечисляются через запятую. |
from_email обязательный | string | Адрес отправителя на подтверждённом домене. Можно не передавать, если заголовок From задан внутри headers. |
message обязательный | string | HTML-версия письма. Можно не передавать, если задан plain_text. |
from_name | string | Имя отправителя. Без него в поле «От кого» подставится сам адрес. |
subject | string | Тема письма. |
plain_text | string | Текстовая версия письма. |
message_id | string | Ваш идентификатор письма. Если не задан, генерируется автоматически и возвращается в ответе. |
cc | string | Копия. Несколько адресов — через запятую. |
bcc | string | Скрытая копия. |
headers | JSON-строка | Произвольные заголовки письма объектом — например {"Reply-To":"support@example.ru"}. Заданный здесь From заменяет from_email. |
attachments | JSON-строка | Массив вложений: [{"name":"чек.pdf","filebody":"<base64>"}]. Файлы .php и .exe запрещены, суммарный размер ограничен тарифом. |
inline | JSON-строка | Встроенные в вёрстку изображения: [{"cid":"logo","mime_type":"image/png","filename":"logo.png","body":"<base64>"}]. В HTML на них ссылаются как <img src="cid:logo">. Допустимы только image/jpeg, image/png, image/gif. |
delivery_time | timestamp | Unix-время отложенной отправки. Без него письмо уходит немедленно. |
domain | string | Домен отправки, если в аккаунте их несколько. |
stat_domain | string | Домен для ссылок отслеживания. |
maxdelivertime | integer | Сколько секунд пытаться доставить письмо, прежде чем признать возвратом. |
maxattempts | integer | Максимальное число попыток доставки. |
ignore_delivery_policy | флаг | Игнорировать ограничения политики доставки аккаунта. |
smarthost | string | Промежуточный SMTP-сервер для отправки. |
debug | integer | Уровень отладочного логирования. |
Что возвращается
Идентификатор письма — по нему проверяется статус доставки.
Возможные ошибки
| HTTP | code | Когда возникает |
|---|---|---|
422 |
3 |
Нет получателя, отправителя или текста письма |
422 |
6 |
Некорректный адрес получателя или отправителя |
422 |
52 |
Адрес в from_email не подтверждён как отправитель |
402 |
35 |
Недостаточно средств, функция недоступна на тарифе или аккаунт заблокирован за спам |
500 |
33 |
Недопустимый формат вложения |
413 |
26 |
Превышен лимит на размер вложений |
500 |
40 |
Некорректная структура headers |
curl -X POST 'https://api.dashamail.com/v2/transactional/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"to": "ivan@example.com",
"from_email": "noreply@example.ru",
"from_name": "Интернет-магазин Ромашка",
"subject": "Заказ №1024 оплачен",
"message": "<p>Спасибо за заказ! Мы уже собираем его.</p>",
"plain_text": "Спасибо за заказ! Мы уже собираем его.",
"message_id": "order-1024-paid"
}'
<?php
$ch = curl_init('https://api.dashamail.com/v2/transactional/messages');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'to' => 'ivan@example.com',
'from_email' => 'noreply@example.ru',
'from_name' => 'Интернет-магазин Ромашка',
'subject' => 'Заказ №1024 оплачен',
'message' => '<p>Спасибо за заказ! Мы уже собираем его.</p>',
'plain_text' => 'Спасибо за заказ! Мы уже собираем его.',
'message_id' => 'order-1024-paid',
], JSON_UNESCAPED_UNICODE),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException($response['error']['message']);
}
print_r($response['response']['data']);
import requests
response = requests.post(
'https://api.dashamail.com/v2/transactional/messages',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'to': 'ivan@example.com',
'from_email': 'noreply@example.ru',
'from_name': 'Интернет-магазин Ромашка',
'subject': 'Заказ №1024 оплачен',
'message': '<p>Спасибо за заказ! Мы уже собираем его.</p>',
'plain_text': 'Спасибо за заказ! Мы уже собираем его.',
'message_id': 'order-1024-paid',
},
)
response.raise_for_status()
print(response.json()['response']['data'])
const response = await fetch('https://api.dashamail.com/v2/transactional/messages', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"to": "ivan@example.com",
"from_email": "noreply@example.ru",
"from_name": "Интернет-магазин Ромашка",
"subject": "Заказ №1024 оплачен",
"message": "<p>Спасибо за заказ! Мы уже собираем его.</p>",
"plain_text": "Спасибо за заказ! Мы уже собираем его.",
"message_id": "order-1024-paid"
}),
});
const payload = await response.json();
if (!response.ok) throw new Error(payload.error.message);
console.log(payload.response.data);
{
"response": {
"msg": {
"err_code": 0,
"text": "OK",
"type": "message"
},
"data": {
"transaction_id": "order-1024-paid"
}
}
}
Статус письма #
GET
/transactional/messages/{transaction_id}
Возвращает текущий статус письма и historию его событий. Данные появляются через несколько секунд после отправки.
Поиск по конкретному письму доступен за последние шесть месяцев. За более старыми данными обращайтесь к журналу.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
transaction_id обязательный | string | Идентификатор письма из ответа на отправку. |
Параметры тела запроса
| Параметр | Тип | Описание |
|---|---|---|
transaction_id обязательный | string | Идентификатор письма — продублируйте значение из пути в строке запроса. |
Что возвращается
data — последнее состояние письма,
log — последовательность событий. Даты событий
лежат в полях datesent, dateopened,
dateclicked, datebounced
и подобных.
Возможные ошибки
| HTTP | code | Когда возникает |
|---|---|---|
422 |
3 |
Не передан transaction_id |
500 |
34 |
В аккаунте не настроен домен отправки |
402 |
35 |
Функция недоступна на текущем тарифе |
404 |
4 |
Письма с таким идентификатором не найдено |
curl -X GET 'https://api.dashamail.com/v2/transactional/messages/order-1024-paid?transaction_id=order-1024-paid' \
-H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://api.dashamail.com/v2/transactional/messages/order-1024-paid?transaction_id=order-1024-paid');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException($response['error']['message']);
}
print_r($response['response']['data']);
import requests
response = requests.get(
'https://api.dashamail.com/v2/transactional/messages/order-1024-paid?transaction_id=order-1024-paid',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
response.raise_for_status()
print(response.json()['response']['data'])
const response = await fetch('https://api.dashamail.com/v2/transactional/messages/order-1024-paid?transaction_id=order-1024-paid', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
},
});
const payload = await response.json();
if (!response.ok) throw new Error(payload.error.message);
console.log(payload.response.data);
{
"response": {
"msg": {
"err_code": 0,
"text": "OK",
"type": "message"
},
"data": {
"data": {
"to": "ivan@example.com",
"status": 3,
"statusname": "Delivered",
"date": "2026-04-12T10:15:03Z",
"datesent": "2026-04-12T10:15:01Z",
"delivery_info": "Доставлено: 10:15:03 250 2.0.0 OK",
"statuschangedate": "2026-04-12T10:15:03Z"
}
}
}
}
Журнал отправок #
GET
/transactional/log
События транзакционных писем аккаунта с фильтрами. Основной инструмент для разбора инцидентов: «почему клиент не получил письмо».
Параметры строки запроса
| Параметр | Тип | Описание |
|---|---|---|
start | integerпо умолчанию: 0 | Смещение. |
limit | integerпо умолчанию: 500 | Размер страницы. |
sort | stringпо умолчанию: DESC | Направление сортировки по времени события. Допустимые значения: ASC, DESC |
message_id | string | События одного письма. |
campaign_id | string | События одной кампании. |
event_type | string | Типы событий через запятую либо all.Допустимые значения: SENT, DELIVERED, OPENED, CLICKED, BOUNCED, COMPLAINED, UNSUBSCRIBED, all |
emails | string | Адреса получателей через запятую. |
from | datetime | Начало периода. |
to | datetime | Конец периода. |
url | string | Фильтр по ссылке для событий клика. |
Возможные ошибки
| HTTP | code | Когда возникает |
|---|---|---|
500 |
34 |
В аккаунте не настроен домен отправки |
402 |
35 |
Функция недоступна на текущем тарифе |
404 |
4 |
Событий по фильтру нет |
curl -X GET 'https://api.dashamail.com/v2/transactional/log?event_type=BOUNCED&limit=2' \
-H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://api.dashamail.com/v2/transactional/log?event_type=BOUNCED&limit=2');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException($response['error']['message']);
}
print_r($response['response']['data']);
import requests
response = requests.get(
'https://api.dashamail.com/v2/transactional/log?event_type=BOUNCED&limit=2',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
response.raise_for_status()
print(response.json()['response']['data'])
const response = await fetch('https://api.dashamail.com/v2/transactional/log?event_type=BOUNCED&limit=2', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
},
});
const payload = await response.json();
if (!response.ok) throw new Error(payload.error.message);
console.log(payload.response.data);
{
"response": {
"msg": {
"err_code": 0,
"text": "OK",
"type": "message"
},
"data": [
{
"event_time": "2026-04-12 10:15:40",
"event_type": "BOUNCED",
"email": "nobody@example.com",
"message_id": "order-1025-paid",
"bounce_code": "5.1.1",
"bounce_reason": "Bad destination mailbox address"
},
{
"event_time": "2026-04-12 09:02:11",
"event_type": "BOUNCED",
"email": "old@example.com",
"message_id": "order-1019-paid",
"bounce_code": "5.2.2",
"bounce_reason": "Mailbox full"
}
]
}
}
Статистика отправок #
GET
/transactional/stats
Агрегированные показатели транзакционных писем за период, разложенные по временным интервалам. Годится для построения графиков доставляемости.
Ширина интервала подбирается по периоду автоматически:
пять минут для 1h, час для 24h,
сутки для 7d и 30d.
Параметры строки запроса
| Параметр | Тип | Описание |
|---|---|---|
period | stringпо умолчанию: 1h | Период отчёта. Значение custom включает start_date и finish_date.Допустимые значения: 1h, 24h, 7d, 30d, custom |
start_date | datetime | Начало периода при period=custom. |
finish_date | datetime | Конец периода при period=custom. |
groupby | string | Шаг группировки при period=custom.Допустимые значения: hour, day, week, month |
campaign_id | string | Только письма указанной кампании. |
domain | string | Только письма с указанного домена отправки. |
start | integerпо умолчанию: 0 | Смещение. |
limit | integer | Размер страницы. |
Возможные ошибки
| HTTP | code | Когда возникает |
|---|---|---|
402 |
35 |
Функция недоступна на текущем тарифе |
404 |
4 |
Данных за период нет |
curl -X GET 'https://api.dashamail.com/v2/transactional/stats?period=24h' \
-H 'Authorization: Bearer YOUR_API_KEY'
<?php
$ch = curl_init('https://api.dashamail.com/v2/transactional/stats?period=24h');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException($response['error']['message']);
}
print_r($response['response']['data']);
import requests
response = requests.get(
'https://api.dashamail.com/v2/transactional/stats?period=24h',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
response.raise_for_status()
print(response.json()['response']['data'])
const response = await fetch('https://api.dashamail.com/v2/transactional/stats?period=24h', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
},
});
const payload = await response.json();
if (!response.ok) throw new Error(payload.error.message);
console.log(payload.response.data);
{
"response": {
"msg": {
"err_code": 0,
"text": "OK",
"type": "message"
},
"data": [
{
"time": "2026-04-12 09:00",
"sent": 412,
"delivered": 401,
"opened": 188,
"clicked": 42,
"bounced": 9,
"complained": 0
},
{
"time": "2026-04-12 10:00",
"sent": 388,
"delivered": 380,
"opened": 165,
"clicked": 37,
"bounced": 6,
"complained": 1
}
]
}
}