ДАРА
Разделы документации

ДокументацияРуководства

Вебхуки

Уведомления о готовности анализа на ваш адрес: события, заголовки и тело, проверка подписи на PHP и Python, повторы и журнал доставок.

Вебхук — запрос ДАРА на ваш адрес, когда анализ объекта завершился. Он избавляет от опроса GET /items/{id}: результаты можно забирать сразу после уведомления.

Зачем

  • Не опрашивать ДАРА по расписанию: анализ длинного видео занимает минуты, а опрос каждую минуту всё равно даёт задержку.
  • Обновлять карточку на сайте сразу, как появились теги, сводка и возрастная отметка — см. Результаты анализа.
  • Видеть объекты, у которых анализ не удался, и решать, переобрабатывать их или показывать как есть.

Вебхук — сигнал, а не источник данных: подробности (транскрипт, кадры) забирайте GET /items/{id}?include=….

Подключение в кабинете

Проект → «Настройки» → «Вебхуки».

  1. Адрес. Только https, публичный адрес вашего сервера. Адрес во внутреннюю сеть, на localhost или на серверы ДАРА не принимается — ни при сохранении, ни при отправке.
  2. События. Отметьте хотя бы одно: «Обработка завершена» и «Обработка завершилась с ошибкой».
  3. Секрет. ДАРА выдаёт секрет подписи вида whsec_XXXXXXXX… и показывает его один раз — при создании и после «Сменить секрет». Сохраните его в конфигурации своего сервера, а не в репозитории. Дальше в кабинете видно только начало секрета.
  4. Проверка. Кнопка «Отправить тест» шлёт на адрес событие webhook.test — одну попытку без повторов, результат виден сразу и в журнале. Не больше 10 проверок за 10 минут на проект.
  5. Журнал доставок хранит 30 дней: событие, объект, статус, номер попытки, код ответа, текст ошибки, время следующей попытки и тело запроса.

Требования к адресу при сохранении: схема https, длина до 2048 символов, имя хоста обязательно и записывается латиницей (домен на кириллице — в виде xn--…), без имени и пароля в адресе (user:pass@), без якоря #, без знака подчёркивания в имени хоста. Порт — любой, строка запроса допускается. У проекта не больше 5 вебхуков.

События

Событие Когда приходит item.status
item.processed объект перешёл в ready: все применимые шаги анализа завершены ready
item.failed объект перешёл в partial (часть шагов с ошибкой, объект участвует в выдаче) или в failed (векторов нет) partial или failed
webhook.test кнопка «Отправить тест» в кабинете; item.idwebhook-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. Повторная сериализация ответа даёт другие байты и другую подпись.

  1. Возьмите тело запроса как есть.
  2. Соберите строку: X-Core-Timestamp, точка, тело.
  3. Посчитайте HMAC-SHA256 с секретом вебхука целиком (whsec_ и всё, что за ним) и переведите в hex нижнего регистра.
  4. Сравните sha256= и результат с X-Core-Signature в постоянное время.
  5. Отбросьте запрос, если отметка времени старше 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-адресу и не отключайте проверку «на время отладки». Для отладки есть «Отправить тест».