API ЭТОЧАТБОТ

Триггерные уведомления: запуск сценария

Как из своей системы запускать сценарии бота: вы передаёте событие и данные переменными, а текст, кнопки и логика остаются в конструкторе — и меняются без правки кода.

Это главный эндпоинт клиентского API. Всё остальное — вспомогательное.

Идея: вы шлёте событие, а не текст

Привычный способ интеграции — «сформировать сообщение в коде и отправить». Он работает, но быстро становится обузой: текст напоминания живёт внутри вашего приложения, и каждая правка формулировки, каждая новая кнопка, каждое «а давайте ещё картинку» — это задача разработчику и деплой.

POST /{channel}/trigger переворачивает схему. Вы сообщаете боту: с этим человеком случилось вот такое событие, вот данные по нему. А что именно ему написать — решает сценарий в конструкторе.

Ваша система                    ЭТОЧАТБОТ
─────────────                   ──────────
«у этого человека               → сценарий «Из другой автоматизации»
 заканчивается тариф,              ├─ сообщение с {{var.plan}}
 plan=Премиум, days_left=3»        ├─ кнопка «Продлить»
                                   └─ ветка: если не ответил за сутки — напомнить

Что это даёт на практике:

  • Текст меняет маркетолог, а не программист. Правка формулировки — это правка блока в конструкторе, код вы не трогаете.
  • Логика тоже живёт в конструкторе. Кнопки, задержки, ветвление по ответу, передача оператору, запись в CRM — всё это блоки сценария. Ваш код об этом ничего не знает.
  • Один вызов — любой канал. Сценарий отработает одинаково для Telegram, чата на сайте, ВКонтакте и МАКС.
  • Данные передаются переменными. Название тарифа, число дней, сумма, номер заказа — всё это подставляется в текст в момент отправки.

Прежде чем начать: кому можно писать

Написать человеку можно только если он раньше сам обратился к боту. До первого обращения адреса доставки просто не существует — ни в одном канале.

Это ограничение самих мессенджеров, а не наше. Telegram позволяет писать только тем, кто хотя бы раз запускал бота; ВКонтакте требует от человека разрешения на сообщения от сообщества; в чате на сайте контакт появляется в момент первого сообщения.

Поэтому сценарий «выгрузим всю базу клиентов из CRM и разошлём напоминания» работать не будет. Работает другой: человек когда-то пришёл в бота, вы связали его с записью в своей системе (см. статью про привязку external_id), и дальше шлёте ему события.

Instagram-боты этот API не поддерживают вообще — вызов вернёт 400 unsupported_channel.

Шаг 1. Сценарий с триггером «Из другой автоматизации»

Кабинет → выберите бота → Автоматизации → создайте сценарий.

В качестве триггера выберите «Из другой автоматизации». Это тот самый вход, который открывает POST /{channel}/trigger.

Дальше соберите сценарий как обычно: блок сообщения, кнопки, задержки, условия — всё доступно.

Обязательно включите сценарий. Выключенный сценарий для API не существует: запрос вернёт 404 no_flow.

Шаг 2. Переменные в текстах

Всё, что вы передадите в поле vars, доступно в блоках сценария как {{var.имя}}.

Передали:

{"plan": "Премиум", "days_left": "3"}

Пишете в блоке сообщения:

Ваш тариф {{var.plan}} заканчивается через {{var.days_left}} дн.
Продлите сейчас — история диалогов и настройки сохранятся.

Человек получит: «Ваш тариф Премиум заканчивается через 3 дн.»

Переменные работают не только в текстах: их можно использовать в условиях сценария. Например, ветка «если {{var.days_left}} равно 1 — текст более настойчивый, кнопка одна и та же».

Ограничения на переменные

ЧтоОграничение
Имя переменнойлатиница, цифры и подчёркивание (days_left — да, дней-осталось — нет)
Количестводо 20 штук за один вызов
Длина значениядо 500 символов
Тип значенияпередавайте строками — так предсказуемее подстановка

Числа лучше слать строкой ("3", а не 3): в текст они всё равно попадут как текст, а строка избавляет от сюрпризов с форматированием дробных.

Шаг 3. Вызов POST /{channel}/trigger

POST https://etochat.bot/api/v2/{channel}/trigger
Authorization: Bearer etchtbt_bot_<ваш ключ>
Content-Type: application/json

Тело:

{
  "chat_id": "9995735656",
  "vars": {
    "plan": "Премиум",
    "days_left": "3"
  }
}

Вместо chat_id можно указать external_id — ваш собственный идентификатор пользователя, если вы предварительно привязали его через POST /{channel}/contacts/link. Это удобнее: в вашей системе не придётся хранить чужие идентификаторы.

Ответ:

{"triggered": true, "outcome": "handled", "chat_id": "9995735656"}

triggered: true означает, что подходящий сценарий найден и запущен. chat_id возвращается всегда — в том числе когда вы обращались по external_id, так что по ответу видно, кому именно ушло.

chat_id в ответах API всегда строка. Это не каприз: у чата на сайте он длиннее 16 цифр, и число в JavaScript теряет точность. Не приводите его к числу.

Пример 1. Тариф заканчивается через 3 дня

Здесь мы обращаемся по chat_id, который взяли из GET /{channel}/chats.

curl -X POST https://etochat.bot/api/v2/{channel}/trigger \
  -H "Authorization: Bearer etchtbt_bot_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "chat_id": "9995735656",
    "vars": {
      "event": "plan_expiring",
      "plan": "Премиум",
      "days_left": "3",
      "expires_at": "24 июля"
    }
  }'

Обратите внимание на переменную event. Это удобный приём: один сценарий на все события, а внутри — условие по {{var.event}}, которое разводит ветки. Так вам не придётся плодить десяток сценариев и решать, какой из них дёрнуть.

Сообщение в конструкторе может выглядеть так:

Ваш тариф {{var.plan}} действует до {{var.expires_at}} — осталось {{var.days_left}} дн.
Продлите заранее, чтобы боты не остановились.

И кнопка-ссылка на оплату рядом с ним — её вы добавляете в конструкторе, в коде о ней знать не нужно.

Пример 2. Тариф активирован (PHP)

Здесь обращаемся по external_id — идентификатору пользователя в вашей системе. Предполагается, что вы один раз вызвали POST /{channel}/contacts/link и связали user-42 с его чатом.

<?php

function etochatTrigger(string $recipientExternalId, array $vars): array
{
    $apiKey = getenv('ETOCHAT_API_KEY'); // etchtbt_bot_...

    $payload = json_encode([
        'external_id' => $recipientExternalId,
        'vars'        => $vars,
    ], JSON_UNESCAPED_UNICODE);

    $ch = curl_init('https://etochat.bot/api/v2/{channel}/trigger');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 15,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json',
        ],
    ]);

    $body = curl_exec($ch);
    $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($body === false) {
        return ['ok' => false, 'code' => 0, 'error' => 'network_error'];
    }

    $data = json_decode($body, true) ?: [];

    if ($code >= 200 && $code < 300) {
        return ['ok' => true, 'code' => $code, 'chat_id' => $data['chat_id'] ?? null];
    }

    return [
        'ok'    => false,
        'code'  => $code,
        'error' => $data['error']['code'] ?? 'unknown',
        'message' => $data['error']['message'] ?? '',
    ];
}

// Вызываем после успешной оплаты
$result = etochatTrigger('user-42', [
    'event'      => 'plan_activated',
    'plan'       => 'Премиум',
    'valid_till' => '21 августа',
]);

if (!$result['ok']) {
    // Пишем в лог, но не роняем оплату из-за недоставленного уведомления
    error_log('etochat trigger failed: ' . $result['code'] . ' ' . $result['error']);
}

Ключевая мысль последнего блока: уведомление не должно ломать бизнес-операцию. Оплата прошла — значит, прошла, даже если человек успел заблокировать бота. Логируйте ошибку и идите дальше.

Ошибка no_flow: что она значит и что делать

{"error": {"code": "no_flow", "message": "…"}}

HTTP-статус — 404. Означает ровно одно: у этого бота нет включённого сценария с триггером «Из другой автоматизации». Запрос дошёл, ключ верный, человек найден — просто запускать нечего.

Проверьте по порядку:

  1. Сценарий вообще создан? Кабинет → бот → Автоматизации.
  2. В нём выбран триггер именно «Из другой автоматизации», а не «Ключевое слово», «Старт» и т. п.
  3. Сценарий включён. Черновик и выключенный сценарий для API не существуют.
  4. Вы используете ключ того самого бота. Ключ привязан к одному боту и не видит сценарии других ботов того же владельца — даже ваших собственных.

Четвёртый пункт — самая частая причина. Если ботов несколько, легко взять ключ не от того.

Остальные ошибки

Все ошибки приходят в одном формате: {"error":{"code":"…","message":"…"}} с осмысленным HTTP-статусом.

СтатусКодЧто делать
401unauthorizedНе передан заголовок Authorization
401invalid_keyКлюч неверен, отозван или бот отключён — выпустите новый
400unsupported_channelКанал бота не поддерживается (Instagram)
400recipient_requiredНе указан ни chat_id, ни external_id
400chat_id_invalidНекорректный chat_id
404contact_not_foundУ этого бота нет такого контакта
404external_id_not_linkedСначала вызовите POST /{channel}/contacts/link
404no_flowНет включённого сценария с нужным триггером (см. выше)
402subscription_inactiveПодписка неактивна, отправка невозможна
409recipient_blocked_botЧеловек остановил бота — это нормальная ситуация, не ошибка интеграции

409 recipient_blocked_bot стоит обрабатывать отдельно: имеет смысл пометить у себя, что этому пользователю уведомления больше не доходят, и не долбить его каждый день.

Когда лучше POST /{channel}/messages, а когда /trigger

POST /{channel}/messages шлёт готовый текст, который вы собрали сами. Это уместно для сугубо технических вещей: одноразовый код, «ваш отчёт готов», строго форматированная выписка — то, что маркетолог никогда не будет переписывать.

POST /{channel}/trigger — для всего остального. Как только у уведомления появляется кнопка, ветвление, продолжение диалога или желание переформулировать текст — это сценарий.

Проверка перед запуском

  1. GET /{channel}/me — ключ рабочий, can_send: true.
  2. GET /{channel}/chats — нашли себя в списке (напишите боту сами, если ещё не писали).
  3. Вызвали /trigger со своим chat_id и тестовыми переменными — сообщение пришло, подстановки на месте.
  4. Только после этого подключаете реальные события своей системы.

Дальше логично посмотреть статью про привязку external_id — она избавляет вас от необходимости хранить chat_id на своей стороне и делает вызовы /trigger заметно чище.