Перейти к основному содержимому

Вебхуки

Вместо периодического опроса платформа сама вызывает ваш адрес при наступлении события. Это основной способ получать статусы доставки.

8.1 Подключение

Вебхук регистрирует администратор компании в веб-интерфейсе. Через API-ключ подписаться нельзя — это сделано намеренно, чтобы владелец ключа не мог перенаправить данные компании на посторонний адрес.

Требования к адресу:

  • только HTTPS;
  • только публичный адрес — внутренние и локальные адреса отклоняются;
  • адрес проверяется не только при регистрации, но и при каждой отправке.

8.2 События

СобытиеКогда происходит
order.status_changedзаказ сменил статус (из любого источника)
route.dispatchedмаршрут выдан водителю
route.completedмаршрут завершён
planning_job.finishedпланирование завершилось (успешно или с ошибкой)
vehicle_check.completedзавершён осмотр транспорта

Пустой список событий означает, что не придёт ничего. Это противоположно логике разрешений у ключей, где пустой список даёт полный доступ. Перечисляйте события явно.

В отличие от разрешений ключа, названия событий проверяются при регистрации: опечатка вернёт ошибку со списком допустимых значений, а не создаст молчащую подписку.

8.3 Формат доставки

Платформа отправляет POST с заголовками:

ЗаголовокСодержимое
X-DP-Eventназвание события
X-DP-Delivery-Idидентификатор доставки, неизменный при повторах
X-DP-Timestampвремя отправки, Unix-секунды
X-DP-Signaturesha256=<подпись>

Примеры тел:

{"order_id":118234,"external_id":"РН-000123","old_status":"in_route","new_status":"confirmed"}
{"job_id":4471,"status":"done","route_ids":[9042,9043],
"summary":{"route_count":2,"unassigned_count":1},"error":null}

В событии planning_job.finished приходит только количество нераспределённых заказов. Причины запрашивайте отдельно через GET /planning/jobs/{job_id}/.

8.4 Проверка подписи — обязательно

Без проверки подписи кто угодно сможет прислать вам ложный статус доставки.

Алгоритм:

  1. Взять заголовки X-DP-Timestamp и X-DP-Signature.
  2. Убедиться, что метка времени отличается от текущей не более чем на 5 минут (защита от повторной отправки перехваченного запроса — платформа её не проверяет, это ваша ответственность).
  3. Составить строку: <метка времени> + . + <тело запроса как есть>.
  4. Вычислить HMAC-SHA256 этой строки с секретом вебхука.
  5. Сравнить с подписью из заголовка (без префикса sha256=), обязательно используя сравнение, устойчивое к атаке по времени.

Ключевой момент: подписывается исходное тело запроса. Не разбирайте JSON и не собирайте его заново — порядок ключей и пробелы изменятся, и подпись не сойдётся. Берите байты ровно в том виде, в каком они пришли.

Пример на Python:

import hashlib, hmac, time

TOLERANCE = 300 # 5 минут

def verify(raw_body: bytes, headers, secret: str) -> bool:
ts = headers["X-DP-Timestamp"]
if abs(int(time.time()) - int(ts)) > TOLERANCE:
return False
signed = ts.encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
presented = headers["X-DP-Signature"].removeprefix("sha256=")
return hmac.compare_digest(expected, presented)

8.5 Повторные попытки

  • Успехом считается ответ с кодом 200–299. Любой другой код, включая перенаправления, считается неудачей.
  • Перенаправления не выполняются — указывайте конечный адрес.
  • Таймаут ответа — 10 секунд.
  • Всего 5 попыток с интервалами 30 с, 60 с, 120 с, 240 с (около 7,5 минут).
  • Неудачи не отключают вебхук автоматически.

Исключение из правила о пяти попытках. Если адрес не прошёл проверку безопасности, доставка помечается неудачной сразу, без единого повтора. Проверка выполняется перед каждой отправкой, и в неё входит разрешение доменного имени — поэтому кратковременный сбой DNS приводит к безвозвратной потере события. В журнале это выглядит как blocked destination без кода ответа, одинаково для недоступного DNS, внутреннего адреса и неверной схемы. Следите за надёжностью DNS принимающего домена.

Журнал доставок доступен администратору компании в интерфейсе: видно статус каждой доставки, число попыток и код ответа. Оттуда же можно отправить событие повторно вручную — это штатный способ восстановиться после аварии на вашей стороне.

Что нужно учесть в приёмнике:

  • Событие может прийти повторно. Отсекайте дубликаты по X-DP-Delivery-Id — при повторах он не меняется (в отличие от подписи и метки времени).
  • Порядок событий не гарантирован. Статусы могут прийти не по порядку — опирайтесь на поля old_status / new_status, а не на очерёдность получения.
  • Отвечайте быстро, до истечения таймаута: примите запрос, поставьте в очередь, обработку выполняйте отдельно.

При ротации секрета переходного периода нет. Сначала научите приёмник проверять подпись двумя секретами сразу, и только потом меняйте секрет.