Вебхуки
Вебхук присылает POST-запрос на ваш сервер о каждом новом посте выбранных каналов MAX — обычно через 1–2 минуты после того, как мы его прочитали. Опрашивать ленту каждые несколько минут не нужно.
К сведению
Вебхуки открыты с тарифа «API.Max Рост»: 1 вебхук и до 500 каналов. «API.Max Профи» — 5 вебхуков и до 5 000 каналов на каждый. Управление вебхуками не расходует запросы.
Ключ «чтение и запись»
Создавать и менять вебхуки может только ключ с отметкой «чтение и запись». Её ставят при создании ключа на странице «API-ключи» в кабинете, потом она не меняется. Ключ без отметки получит ответ 403 с кодом read_only_key.
Создайте вебхук
# создать вебхук: адрес вашего сервера и номера каналов datool.ru
# нужен ключ с отметкой «чтение и запись»; в ответе — секрет подписи (один раз)
curl -X POST https://api.datool.ru/v1/max/webhooks/ \
-H "Authorization: Bearer dtk_ваш_ключ" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/hooks/datool", "channels": [4821, 5310]}'
url— адресhttps://вашего сервера (адреса внутренних сетей иlocalhostне подходят);channels— номера открытых каналов MAX в datool.ru (полеidв каталоге и карточке канала).
В ответе — поле secret. Сохраните его: он показывается один раз, им подписано каждое событие. Новый секрет — POST /v1/max/webhooks/{id}/rotate-secret/.
Проверить адрес: POST /v1/max/webhooks/{id}/test/ пришлёт событие ping без постов — одна попытка без повторов, не чаще раза в минуту на вебхук (чаще — 429 throttled). Итог — в полях last_success_at и last_error.
Создавать, менять и удалять вебхуки может ключ «чтение и запись» владельца или администратора организации; ключ обычного участника получит 403 manager_required. Адрес может быть и на кириллическом домене (.рф).
Что приходит
{
"event": "post.created",
"webhook": 12,
"created_at": "2026-10-18T12:00:00+03:00",
"posts": [
{
"id": 9876543,
"channel": 4821,
"text": "Мэрия открыла новый парк на набережной…",
"views": 18450
}
]
}
| Поле | Что в нём |
|---|---|
event |
post.created — новые посты, ping — пробная доставка |
webhook |
Номер вебхука |
created_at |
Когда событие собрано (ISO 8601, по Москве) |
posts |
Посты — у ping пустой список |
В posts — до 100 постов, каждый в том же виде, что отдаёт GET /v1/max/posts/{id}/. Присылаем только свежие посты (опубликованные не раньше чем за двое суток); правки и удаление постов пока не присылаем.
Заголовки запроса:
| Заголовок | Что в нём |
|---|---|
X-Datool-Event |
post.created или ping |
X-Datool-Delivery |
номер доставки; при повторе тот же |
X-Datool-Signature |
t=<время>,v1=<подпись> |
Проверьте подпись
Подпись — HMAC-SHA256 строки <t>.<тело запроса> вашим секретом. Сверяйте её до разбора JSON и отклоняйте события старше 5 минут — так чужой не подделает событие и не повторит старое.
import hashlib
import hmac
import time
def is_valid(secret: str, body: bytes, header: str) -> bool:
"""Проверить подпись события: secret — секрет вебхука, body — сырое тело запроса."""
# заголовок X-Datool-Signature: «t=<время>,v1=<подпись>»
try:
parts = dict(item.split("=", 1) for item in header.split(","))
timestamp = int(parts["t"])
signature = parts["v1"]
except (ValueError, KeyError):
# заголовка нет или он испорчен — событие не от datool.ru
return False
# событие старше 5 минут — отклоняем: так старое событие не повторить
if abs(time.time() - timestamp) > 300:
return False
# подпись — HMAC-SHA256 строки «<t>.<тело>» вашим секретом
expected = hmac.new(
secret.encode(), f"{timestamp}.".encode() + body, hashlib.sha256
).hexdigest()
# сравнение за постоянное время — по времени ответа подпись не подобрать
return hmac.compare_digest(expected, signature)
Пример приёмника
# приёмник событий на Flask: pip install flask
from flask import Flask, abort, request
app = Flask(__name__)
# секрет из ответа на создание вебхука (храните в настройках, не в коде)
SECRET = "whsec_…"
@app.post("/hooks/datool")
def datool_hook():
# сырое тело — подпись считается по нему, до разбора JSON
body = request.get_data()
if not is_valid(SECRET, body, request.headers.get("X-Datool-Signature", "")):
# подпись не сошлась — событие не от datool.ru
abort(401)
event = request.get_json()
if event["event"] == "post.created":
for post in event["posts"]:
# сохраните пост у себя; одно событие может прийти дважды — сверяйте post["id"]
print(post["id"], post["text"][:80])
# отвечаем 2xx быстро, тяжёлую обработку — после ответа
return "", 204
Ответ и повторы
Ответьте кодом 2xx за 10 секунд — тяжёлую обработку делайте после ответа. Переадресации мы не выполняем.
Если ответа нет или код другой, повторяем через 1, 5, 15 минут, 1, 3, 6 и 12 часов — последняя попытка примерно через 22 часа. Событие, которое не прошло за сутки, не доставляется. Тело вашего ответа мы не читаем — важен только код. Одно событие может прийти дважды — отличайте посты по id.
Если за сутки не прошло ни одной доставки, вебхук выключается, а владельцам и администраторам организации приходит письмо. Почините сервер и включите вебхук: PATCH /v1/max/webhooks/{id}/ с {"is_active": true} или кнопкой «Включить» на странице «API-ключи». Посты пойдут с момента включения. Вебхук, выключенный сотрудником datool.ru (disabled_reason: staff), так не включить — ответ 409 conflict; напишите в поддержку.
Если тариф закончился
Вебхуки не удаляются, но события перестают приходить. После продления — снова приходят, с текущего момента.
Все методы — в справочнике (группа «MAX: вебхуки»).