Ресурс series. Серия писем (автоворонка) отправляет подписчику письма по порядку с паузами между ними. Подписчик входит в серию, когда попадает в группу входа (любым путём: форма, API, импорт, вручную) или когда вы добавляете его в серию сами. Дальше серия идёт сама: письмо, пауза, следующее письмо.
Письмо серии ссылается на макет. Сначала загрузите макеты (POST /mailer/makets), потом создайте серию из них.
Как устроена серия
- Статус:
draft(не запущена),active(идёт),stopped(остановлена: люди ждут на своих шагах, письма не уходят). - Группы входа
list_ids: кто попадает в эти группы после запуска, входит в серию. Кто был в группе до запуска, не входит. - Шаги
steps: письма по порядку. Перед каждым письмом паузаdelay_minв минутах (у первого письма 0: оно уходит в течение минуты после входа). - Повторный вход
reentry_days: не задан, и прошедший серию больше в неё не входит; число дней, и снова входит, если прошлый проход кончился раньше. - Отписавшийся, заблокированный и удалённый адрес выходит из серии сам.
Шаг серии
Элемент массива steps:
| Поле | Значение |
|---|---|
id | Номер письма в серии. Приходит в ответе GET. При правке передайте его у прежних писем, у новых не указывайте: так люди внутри серии останутся на своих местах. |
maket_id | Номер макета письма. Обязательно для запуска. |
subject | Тема письма, до 255 знаков. Обязательно для запуска. |
delay_min | Пауза перед письмом в минутах: от 0 до 262800 (180 дней). По умолчанию 0. |
at | Время отправки ЧЧ:ММ по часовому поясу аккаунта: письмо уйдёт в ближайшее такое время после паузы. Необязательно. |
only | Кому отправлять по предыдущему письму: opened (открыли), not_opened (не открыли), clicked (перешли по ссылке). Остальные пропускают письмо и идут дальше. У первого письма нельзя. |
repeat | Повтор тем, кто не открыл: {"after_min":1440,"max":1,"subject":"Тема повтора","maket_id":0,"quiet":true}. after_min от 60 до 10080 (от часа до 7 дней), max 1 или 2, maket_id другой макет (0 или нет: тот же), quiet не слать ночью с 22:00 до 08:00. |
Других полей у шага нет: незнакомое поле (например, опечатка delay_minutes) даёт ошибку 22003, серия не меняется.
Схема с ветками
Для серий с условиями («открыл письмо», «в группе», «заполнено поле») передайте схему целиком в graph вместо steps: {"entry":[...],"start":"w1","nodes":{...}}. Это тот же формат, что строит конструктор в кабинете; посмотреть его можно в ответе GET /mailer/series/{id} у серии, собранной в кабинете. Шаги nodes: mail (письмо), wait (пауза), cond (условие, ветки yes и no), exit (выход). До 100 шагов, без циклов. graph без entry сохраняет прежние группы входа.
GET/mailer/series.json
Все серии
Серии аккаунта со счётчиками людей. Серий нет: ответ 204.
people: вошли всего, сейчас в серии, прошли до конца, вышли раньше (из них отписались, заблокированы, удалены, убраны вручную), вошли за 30 дней.
curl https://v1.estismail.com/mailer/series.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ"<?php
$ch = curl_init('https://v1.estismail.com/mailer/series.json');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $response;import requests
r = requests.get(
"https://v1.estismail.com/mailer/series.json",
headers={"X-Estis-Auth": "ВАШ_КЛЮЧ"},
)
print(r.status_code, r.text)const res = await fetch("https://v1.estismail.com/mailer/series.json", {
headers: { "X-Estis-Auth": "ВАШ_КЛЮЧ" },
});
console.log(res.status, await res.text());{
"series": [
{
"id": 41,
"title": "Приветственная серия",
"status": "active",
"list_ids": [
12345
],
"mails": 4,
"people": {
"entered": 1520,
"inside": 310,
"done": 1102,
"exited": 108,
"unsubscribed": 41,
"blocked": 3,
"deleted": 2,
"removed": 0,
"entered_30d": 420
},
"created": 1761300000,
"updated": 1761310000
}
]
}GET/mailer/series/{id}.json
Одна серия
steps: письма простым списком. У серии с веткамиnull, схема вgraph.has_draft: в кабинете есть неприменённые правки. Правка через API их заменяет.problems: что мешает запуску (нет группы входа, макета, темы).held: сколько людей стоит и почему:series_stopped(серия остановлена),send_closed(отправка в аккаунте закрыта),no_serversи другие.health: здоровье серии за 7 дней, пересчитывается раз в 10 минут,updatedвремя расчёта;null, если расчёта для серии ещё нет.verdict:good(хорошо),warn(внимание),bad(плохо: письма серии вредят доставке, стоит поправить серию или источник адресов),few(мало писем для вывода: меньше 200 ответов провайдеров);reasonпочему, словами. Цифры: отправлено, доставлено, отказы (из нихno_addressнесуществующие адреса иspamотказы «спам или блок»), жалобы, отписки, открытия, переходы.stats: за 90 дней. По серии: сколько людей получили, открыли, перешли, отписались хотя бы по одному письму. По письмам (stepномер шага вgraph, письма по порядку): основные письма и повторы отдельно.
curl https://v1.estismail.com/mailer/series/41.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ"<?php
$ch = curl_init('https://v1.estismail.com/mailer/series/41.json');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $response;import requests
r = requests.get(
"https://v1.estismail.com/mailer/series/41.json",
headers={"X-Estis-Auth": "ВАШ_КЛЮЧ"},
)
print(r.status_code, r.text)const res = await fetch("https://v1.estismail.com/mailer/series/41.json", {
headers: { "X-Estis-Auth": "ВАШ_КЛЮЧ" },
});
console.log(res.status, await res.text());{
"id": 41,
"title": "Приветственная серия",
"status": "active",
"sender_id": 193690,
"reentry_days": null,
"list_ids": [
12345
],
"steps": [
{
"id": "m1",
"maket_id": 501,
"subject": "Добро пожаловать",
"delay_min": 0,
"at": null,
"repeat": {
"after_min": 1440,
"max": 1,
"subject": "Вы пропустили письмо",
"maket_id": null,
"quiet": true
}
},
{
"id": "m2",
"maket_id": 502,
"subject": "Как начать",
"delay_min": 1440,
"at": "10:00"
}
],
"graph": {
"entry": [
{
"type": "group",
"lisst_id": 12345
}
],
"start": "w1",
"nodes": {
"w1": {
"type": "wait",
"mode": "delay",
"minutes": 0,
"next": "m1"
},
"m1": {
"type": "mail",
"maket_id": 501,
"subject": "Добро пожаловать",
"next": "w2"
},
"w2": {
"type": "wait",
"mode": "delay",
"minutes": 1440,
"at": "10:00",
"next": "m2"
},
"m2": {
"type": "mail",
"maket_id": 502,
"subject": "Как начать",
"next": null
}
}
},
"has_draft": false,
"problems": [],
"people": {
"entered": 1520,
"inside": 310,
"done": 1102,
"exited": 108,
"unsubscribed": 41,
"blocked": 3,
"deleted": 2,
"removed": 0,
"entered_30d": 420
},
"held": {},
"health": {
"verdict": "good",
"reason": "доставлено 98,6%, жалоб и отказов «спам» мало",
"days": 7,
"sent": 940,
"delivered": 921,
"bounced": 13,
"no_address": 9,
"spam": 2,
"complaints": 0,
"opened": 402,
"clicked": 96,
"unsubscribed": 11,
"updated": 1761310200
},
"stats": {
"days": 90,
"sent": 1480,
"opened": 820,
"clicked": 214,
"unsubscribed": 41,
"mails": [
{
"step": "m1",
"sent": 1480,
"opened": 760,
"clicked": 190,
"unsubscribed": 30,
"repeats_sent": 610,
"repeats_opened": 95,
"repeats_clicked": 12,
"repeats_unsubscribed": 4
},
{
"step": "m2",
"sent": 1190,
"opened": 540,
"clicked": 120,
"unsubscribed": 7,
"repeats_sent": 0,
"repeats_opened": 0,
"repeats_clicked": 0,
"repeats_unsubscribed": 0
}
]
},
"created": 1761300000,
"updated": 1761310000
}POST/mailer/series.json
Создать серию
| Параметр | Значение |
|---|---|
title обязательный | Название, до 255 знаков. |
list_ids | Группы входа: массив номеров, до 50. |
steps | Письма по порядку (см. «Шаг серии»). Или graph. |
graph | Схема с ветками вместо steps. |
sender_id | Отправитель: номер подтверждённого адреса из GET /mailer/senderemails. Без него: отправитель аккаунта по умолчанию. |
reentry_days | Повторный вход через N дней, от 1 до 3650. Без него: не входит повторно. |
status | draft (по умолчанию) или active: создать и сразу запустить. |
Запуск проходит те же проверки, что в кабинете: есть группа входа, у каждого письма макет и тема, отправка в аккаунте не закрыта. В форме (application/x-www-form-urlencoded) steps, graph и list_ids передаются строкой JSON.
curl -X POST https://v1.estismail.com/mailer/series.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{
"title": "Приветственная серия",
"list_ids": [12345],
"steps": [
{"maket_id": 501, "subject": "Добро пожаловать", "delay_min": 0},
{"maket_id": 502, "subject": "Как начать", "delay_min": 1440, "at": "10:00"},
{"maket_id": 503, "subject": "Скидка для вас", "delay_min": 4320, "only": "opened"}
],
"status": "active"
}'<?php
$body = [
'title' => 'Приветственная серия',
'list_ids' => [12345],
'steps' => [
['maket_id' => 501, 'subject' => 'Добро пожаловать', 'delay_min' => 0],
['maket_id' => 502, 'subject' => 'Как начать', 'delay_min' => 1440, 'at' => '10:00'],
['maket_id' => 503, 'subject' => 'Скидка для вас', 'delay_min' => 4320, 'only' => 'opened'],
],
'status' => 'active',
];
$ch = curl_init('https://v1.estismail.com/mailer/series.json');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_RETURNTRANSFER => true,
]);
$id = json_decode(curl_exec($ch), true)['id'];
curl_close($ch);import requests
r = requests.post('https://v1.estismail.com/mailer/series.json',
headers={'X-Estis-Auth': 'ВАШ_КЛЮЧ'},
json={
'title': 'Приветственная серия',
'list_ids': [12345],
'steps': [
{'maket_id': 501, 'subject': 'Добро пожаловать', 'delay_min': 0},
{'maket_id': 502, 'subject': 'Как начать', 'delay_min': 1440, 'at': '10:00'},
{'maket_id': 503, 'subject': 'Скидка для вас', 'delay_min': 4320, 'only': 'opened'},
],
'status': 'active',
})
series_id = r.json()['id']const r = await fetch('https://v1.estismail.com/mailer/series.json', {
method: 'POST',
headers: { 'X-Estis-Auth': 'ВАШ_КЛЮЧ', 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'Приветственная серия',
list_ids: [12345],
steps: [
{ maket_id: 501, subject: 'Добро пожаловать', delay_min: 0 },
{ maket_id: 502, subject: 'Как начать', delay_min: 1440, at: '10:00' },
{ maket_id: 503, subject: 'Скидка для вас', delay_min: 4320, only: 'opened' }
],
status: 'active'
})
});
const { id } = await r.json();{"id":41}PUT/mailer/series/{id}.json
Изменить, запустить, остановить
Принимает те же параметры, что создание (title не обязателен), и ещё:
| Параметр | Значение |
|---|---|
status | active: запустить, stopped: остановить. |
apply | Для идущей серии: all (по умолчанию) люди внутри со следующего шага идут по новым шагам; new новые шаги только для новых людей, остальные доходят по прежним. |
- Чтобы изменить шаги, возьмите
stepsизGET, поправьте и отправьте целиком. Письма с прежнимidостаются на своих местах; письмо безidновое; письмо, которого нет в списке, удаляется (кто ждал его, переходит к следующему оставшемуся письму, а если после него писем нет, заканчивает серию). - Список, где ни у одного письма нет
id, у идущей серии с людьми внутри даёт ошибку 22020: иначе все внутри вышли бы из серии. Чтобы начать серию заново только для новых людей, передайтеapply=new. - Новые
stepsилиgraphу запущенной серии применяются сразу. Неготовая схема (нет темы, макета) не применяется: ошибка 22009, серия идёт как шла. - Только
list_idsменяет группы входа, шаги остаются. - Остановка не выводит людей: после запуска они продолжат со своих шагов.
curl -X PUT https://v1.estismail.com/mailer/series/41.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ" \
-d status=stopped<?php
$ch = curl_init('https://v1.estismail.com/mailer/series/41.json');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => http_build_query([
'status' => 'stopped',
]),
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $response;import requests
r = requests.put(
"https://v1.estismail.com/mailer/series/41.json",
headers={"X-Estis-Auth": "ВАШ_КЛЮЧ"},
data={
"status": "stopped",
},
)
print(r.status_code, r.text)const res = await fetch("https://v1.estismail.com/mailer/series/41.json", {
method: "PUT",
headers: { "X-Estis-Auth": "ВАШ_КЛЮЧ" },
body: new URLSearchParams({
status: "stopped",
}),
});
console.log(res.status, await res.text());Ответ 204, без тела
DELETE/mailer/series/{id}.json
Удалить серию
Люди внутри выходят из серии, ждущие повторы отменяются.
curl -X DELETE https://v1.estismail.com/mailer/series/41.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ"<?php
$ch = curl_init('https://v1.estismail.com/mailer/series/41.json');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $response;import requests
r = requests.delete(
"https://v1.estismail.com/mailer/series/41.json",
headers={"X-Estis-Auth": "ВАШ_КЛЮЧ"},
)
print(r.status_code, r.text)const res = await fetch("https://v1.estismail.com/mailer/series/41.json", {
method: "DELETE",
headers: { "X-Estis-Auth": "ВАШ_КЛЮЧ" },
});
console.log(res.status, await res.text());Ответ 204, без тела
GET/mailer/series/{id}/members.json
Люди серии
| Параметр | Значение |
|---|---|
email | Путь одного адреса: все его проходы серии, последние сначала. |
state | inside (сейчас в серии), done (прошли до конца), exited (вышли раньше). Без него: все. |
limit, page | Страницы, как у других списков: до 100 строк. |
step: шаг, на котором человек сейчас (номер шага вgraph),next_at: когда двинется дальше.exit_reason:endиexit_node(прошёл до конца),unsubscribed,blocked,deleted,removed(убран вручную или серия удалена).source: как вошёл. Добавлен в серию:manual(в кабинете),api(этим ресурсом). Попал в группу входа: каким путём адрес пришёл в группу:form(форма подписки),confirm(подтверждение подписки),api,import,manual,move(перенос из другой группы),group(путь не известен).- Пусто: ответ 204.
curl -G https://v1.estismail.com/mailer/series/41/members.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ" \
--data-urlencode email=anna@example.com<?php
$query = http_build_query([
'email' => 'anna@example.com',
]);
$ch = curl_init('https://v1.estismail.com/mailer/series/41/members.json' . '?' . $query);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $response;import requests
r = requests.get(
"https://v1.estismail.com/mailer/series/41/members.json",
headers={"X-Estis-Auth": "ВАШ_КЛЮЧ"},
params={
"email": "anna@example.com",
},
)
print(r.status_code, r.text)const params = new URLSearchParams({
email: "anna@example.com",
});
const res = await fetch("https://v1.estismail.com/mailer/series/41/members.json?" + params, {
headers: { "X-Estis-Auth": "ВАШ_КЛЮЧ" },
});
console.log(res.status, await res.text());{
"members": [
{
"email_id": 9876543,
"email": "anna@example.com",
"state": "inside",
"step": "w2",
"next_at": 1761400000,
"hold_reason": null,
"exit_reason": null,
"source": "form",
"entered": 1761300000,
"finished": null
}
],
"total": 1
}POST/mailer/series/{id}/members.json
Добавить в серию
| Параметр | Значение |
|---|---|
emails | Адреса подписчиков аккаунта: массив или строка через запятую. |
email_ids | Или номера подписчиков. |
До 1 000 адресов за запрос. Серия должна быть запущена. Добавляются только подписчики, которые уже есть в аккаунте; новый адрес добавьте в группу входа (POST /mailer/emails), и серия заберёт его сама.
В ответе added: поставлены в серию, войдут в течение минуты (кто уже в серии или не проходит повторный вход, не войдёт); blocked: отписаны или заблокированы; missing: нет в аккаунте или адрес с ошибкой.
curl -X POST https://v1.estismail.com/mailer/series/41/members.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{"emails": ["anna@example.com", "oleg@example.com"]}'<?php
$ch = curl_init('https://v1.estismail.com/mailer/series/41/members.json');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['emails' => ['anna@example.com', 'oleg@example.com']]),
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);import requests
r = requests.post('https://v1.estismail.com/mailer/series/41/members.json',
headers={'X-Estis-Auth': 'ВАШ_КЛЮЧ'},
json={'emails': ['anna@example.com', 'oleg@example.com']})
result = r.json()const r = await fetch('https://v1.estismail.com/mailer/series/41/members.json', {
method: 'POST',
headers: { 'X-Estis-Auth': 'ВАШ_КЛЮЧ', 'Content-Type': 'application/json' },
body: JSON.stringify({ emails: ['anna@example.com', 'oleg@example.com'] })
});
const result = await r.json();{
"added": 2,
"blocked": 0,
"missing": 0
}DELETE/mailer/series/{id}/members/{email_id}.json
Убрать из серии
Подписчик выходит из серии, ждущие повторы отменяются. В группах и в аккаунте он остаётся.
curl -X DELETE https://v1.estismail.com/mailer/series/41/members/9876543.json \
-H "X-Estis-Auth: ВАШ_КЛЮЧ"<?php
$ch = curl_init('https://v1.estismail.com/mailer/series/41/members/9876543.json');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['X-Estis-Auth: ВАШ_КЛЮЧ'],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $response;import requests
r = requests.delete(
"https://v1.estismail.com/mailer/series/41/members/9876543.json",
headers={"X-Estis-Auth": "ВАШ_КЛЮЧ"},
)
print(r.status_code, r.text)const res = await fetch("https://v1.estismail.com/mailer/series/41/members/9876543.json", {
method: "DELETE",
headers: { "X-Estis-Auth": "ВАШ_КЛЮЧ" },
});
console.log(res.status, await res.text());Ответ 204, без тела
Вопросы по API: support@estismail.com