ДокументацияРуководства
Вебхуки
Уведомления о готовности анализа на ваш адрес: события, заголовки и тело, проверка подписи на PHP и Python, повторы и журнал доставок.
Вебхук — запрос ДАРА на ваш адрес, когда анализ объекта завершился. Он избавляет от опроса GET /items/{id}: результаты можно забирать сразу после уведомления.
Зачем
- Не опрашивать ДАРА по расписанию: анализ длинного видео занимает минуты, а опрос каждую минуту всё равно даёт задержку.
- Обновлять карточку на сайте сразу, как появились теги, сводка и возрастная отметка — см. Результаты анализа.
- Видеть объекты, у которых анализ не удался, и решать, переобрабатывать их или показывать как есть.
Вебхук — сигнал, а не источник данных: подробности (транскрипт, кадры) забирайте GET /items/{id}?include=….
Подключение в кабинете
Проект → «Настройки» → «Вебхуки».
- Адрес. Только
https, публичный адрес вашего сервера. Адрес во внутреннюю сеть, наlocalhostили на серверы ДАРА не принимается — ни при сохранении, ни при отправке. - События. Отметьте хотя бы одно: «Обработка завершена» и «Обработка завершилась с ошибкой».
- Секрет. ДАРА выдаёт секрет подписи вида
whsec_XXXXXXXX…и показывает его один раз — при создании и после «Сменить секрет». Сохраните его в конфигурации своего сервера, а не в репозитории. Дальше в кабинете видно только начало секрета. - Проверка. Кнопка «Отправить тест» шлёт на адрес событие
webhook.test— одну попытку без повторов, результат виден сразу и в журнале. Не больше 10 проверок за 10 минут на проект. - Журнал доставок хранит 30 дней: событие, объект, статус, номер попытки, код ответа, текст ошибки, время следующей попытки и тело запроса.
Требования к адресу при сохранении: схема https, длина до 2048 символов, имя хоста обязательно и записывается латиницей (домен на кириллице — в виде xn--…), без имени и пароля в адресе (user:pass@), без якоря #, без знака подчёркивания в имени хоста. Порт — любой, строка запроса допускается. У проекта не больше 5 вебхуков.
События
| Событие | Когда приходит | item.status |
|---|---|---|
item.processed |
объект перешёл в ready: все применимые шаги анализа завершены |
ready |
item.failed |
объект перешёл в partial (часть шагов с ошибкой, объект участвует в выдаче) или в failed (векторов нет) |
partial или failed |
webhook.test |
кнопка «Отправить тест» в кабинете; item.id — webhook-test |
ready |
- Одно событие на смену статуса, а не на каждый упавший шаг.
- Повторная обработка —
POST /items/{id}/reprocess, изменение медиа или текста, смена модели — даёт новое событие, когда объект снова придёт к итоговому статусу. PUT, который меняет толькоavailable,properties,url,published_atилиauthor, анализ не перезапускает — и события не даёт, см. Объекты → Что переобрабатывается.- Не приходят: у удалённых объектов; у товаров из заказов, которых не было в каталоге (
origin: order); когда проект в архиве или доступ клиента приостановлен; когда у проекта нет включённого вебхука, подписанного на событие. - Массовая загрузка каталога — Импорт файлов или
POST /items/batch— даёт по событию на объект. Тысяча объектов означает тысячу запросов на ваш адрес: отвечайте быстро и обрабатывайте в фоне.
Запрос
POST на ваш адрес, тело — JSON в UTF-8.
| Заголовок | Значение |
|---|---|
Content-Type |
application/json; charset=utf-8 |
X-Core-Event |
имя события — то же, что поле event тела |
X-Core-Timestamp |
время начала этой попытки, unix-секунды; входит в подпись |
X-Core-Signature |
sha256= и hex HMAC-SHA256 — см. Проверка подписи |
X-Core-Delivery |
идентификатор доставки; одинаков у всех попыток одного события |
User-Agent |
core.daratech.ru/1.0 (+https://core.daratech.ru) |
{"event": "item.processed",
"project_id": "XXXXXXXXXXXX",
"sent_at": "2026-09-21T14:00:00.000Z",
"delivery_id": "Dl1very0000A",
"item": {
"id": "v-1042",
"status": "ready",
"processing": {"prepare": "done", "speech": "done", "vision": "done", "summary": "done", "embedding": "done"},
"summary": {
"summary": "Как выбрать горный велосипед: рама, колёса и тормоза",
"topics": ["велоспорт", "снаряжение"],
"tags": ["велосипед", "горный велосипед", "тормоза"],
"age_rating": "0+",
"moderation": {"level": "none", "categories": {"adult": 0.0, "violence": 0.0}, "evidence": []}
}
}}
Поля тела:
| Поле тела | Значение |
|---|---|
event |
имя события; совпадает с X-Core-Event |
project_id |
идентификатор проекта — тот же, что в кабинете, 12 символов |
sent_at |
время начала этой попытки, ISO-8601 UTC с миллисекундами; у каждой попытки своё |
delivery_id |
идентификатор доставки; равен X-Core-Delivery и подписан вместе с телом — по нему отбрасывайте повторы |
item |
объект: id (ваш id), status, processing — как в GET /items/{id}, и summary, когда сводка построена |
Ключа summary нет, пока сводки нет. Транскрипты и кадры в тело не входят — забирайте их GET /items/{id}?include=transcript,frames.
Проверка подписи
Подпись считается по сырым байтам тела — до разбора JSON. Повторная сериализация ответа даёт другие байты и другую подпись.
- Возьмите тело запроса как есть.
- Соберите строку:
X-Core-Timestamp, точка, тело. - Посчитайте HMAC-SHA256 с секретом вебхука целиком (
whsec_и всё, что за ним) и переведите в hex нижнего регистра. - Сравните
sha256=и результат сX-Core-Signatureв постоянное время. - Отбросьте запрос, если отметка времени старше 5 минут: подпись и
sent_atсоздаются заново на каждую попытку, поэтому повтор через 6 часов приходит со свежей отметкой, а перехваченный запрос повторить нельзя.
Проверка на PHP
<?php
/** Подпись вебхука ДАРА верна? Считайте по сырому телу, до json_decode. */
function dara_webhook_signature_ok(string $secret, string $timestamp, string $body, string $signature): bool
{
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
return hash_equals($expected, $signature);
}
Обработчик:
<?php
$body = (string) file_get_contents('php://input');
$timestamp = (string) ($_SERVER['HTTP_X_CORE_TIMESTAMP'] ?? '');
$signature = (string) ($_SERVER['HTTP_X_CORE_SIGNATURE'] ?? '');
$secret = (string) getenv('DARA_WEBHOOK_SECRET');
if (!ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300
|| !dara_webhook_signature_ok($secret, $timestamp, $body, $signature)) {
http_response_code(403);
return;
}
$payload = json_decode($body, true);
enqueue_item_update($payload['delivery_id'], $payload['item']['id']); // работа — в фоне
http_response_code(200);
Проверка на Python
import hashlib
import hmac
def webhook_signature_ok(secret: str, timestamp: str, body: bytes, signature: str) -> bool:
"""Подпись вебхука ДАРА верна? body — сырые байты тела, до json.loads."""
message = timestamp.encode("ascii") + b"." + body
expected = "sha256=" + hmac.new(secret.encode("utf-8"), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
Сырые байты тела: Flask — request.get_data(), Django — request.body, FastAPI — await request.body(). Разбирайте JSON только после успешной проверки.
Ответ и повторы
- Успех — любой ответ 2xx не позже 10 секунд от начала попытки. Решает код ответа: тело ответа ДАРА отбрасывает, поэтому отвечать можно чем угодно — хоть пустым телом.
- Отвечайте сразу и работайте в фоне. Обработка внутри запроса рано или поздно упрётся в 10 секунд и превратит успешную доставку в повтор.
- Редиректы не выполняются: ответ 3xx считается неудачей. Указывайте конечный адрес.
- Любой другой ответ, обрыв связи, ошибка TLS, неразрешимое имя и таймаут — неудача.
Расписание повторов после неудачи: через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов и 12 часов. Всего попыток семь, весь цикл — около двадцати часов; после последней доставка помечается неудачной и остаётся в журнале.
Доставка «не меньше одного раза». Если ваш сервер ответил, но ответ не дошёл до ДАРА, событие придёт ещё раз. Поэтому:
- отбрасывайте повторы по
delivery_id— он одинаков у всех попыток одного события; - считайте обработку идемпотентной: порядок событий не гарантирован, и
item.failedпосле переобработки может прийти раньше более старогоitem.processed; - текущее состояние объекта всегда доступно в
GET /items/{id}— при сомнениях запрашивайте его, а не полагайтесь на порядок.
Ошибки доставки
В журнале и в результате «Отправить тест» причина видна кодом:
| Код | Что случилось |
|---|---|
timeout |
адрес не ответил за 10 секунд |
connection |
не удалось соединиться с адресом |
tls |
сертификат не прошёл проверку или защищённое соединение не установилось |
dns |
имя хоста не разрешается |
http_status |
адрес ответил кодом, отличным от 2xx |
redirect |
адрес ответил перенаправлением, которое не выполняется |
forbidden_address |
адрес ведёт во внутреннюю сеть или записан недопустимо |
Несколько неудач подряд (таймауты, отказ соединения, TLS, DNS, ответы 5xx) откладывают срочные доставки этого адреса до следующего срока повтора — без расхода попыток. Как только адрес ответит 2xx, доставки идут в обычном темпе.
Очередь адреса. Если у вебхука накопится больше 50 000 недоставленных событий, новые события в очередь не ставятся, а в кабинете появляется баннер «Очередь доставок переполнена». Почините адрес: когда недоставленных станет заметно меньше предела, приём возобновится сам.
Смена секрета
«Сменить секрет» выдаёт новый секрет и показывает его один раз. Новым секретом подписываются все запросы, начиная со следующего, включая повторы уже стоящих в очереди доставок. Обновите секрет у себя сразу: запросы, которые вы отклоните до обновления, повторятся по расписанию и дойдут, но с задержкой до нескольких часов. Двух подписей в заголовке нет.
Если секрет утёк, меняйте его: старый перестаёт действовать немедленно.
Безопасность
- Только
https; сертификат проверяется. - Адрес не может вести во внутреннюю сеть, на loopback или на серверы ДАРА. Проверка идёт и при сохранении, и при каждом соединении — подмена ответа DNS не помогает.
- Перенаправления не выполняются.
- Секрет хранится у ДАРА, чтобы подписывать запросы; в журнале, аудите и логах он не появляется.
- Подпись — единственное доказательство отправителя: не доверяйте IP-адресу и не отключайте проверку «на время отладки». Для отладки есть «Отправить тест».