POST на ваш URL.
Область ключа для управления подписками —
webhooks:manage. Доставка «хотя бы
один раз»: возможны повторы, дедуплицируйте по полю id.Подписка
secret подписки — он показывается один раз. Сохраните его:
им проверяется подпись каждой доставки.
Тонкий payload
Событие несёт тип, идентификатор ресурса и время — но не сами данные:GET /developer/v1/employees/{employee_id}. Так к ним автоматически применяются
область ключа и редакция полей, а персональные данные не уезжают на внешний URL.
Типы событий
Оба типа несут одинаковый payload
{"employee_id": …}. Если у человека несколько
карточек сотрудника, person.changed придёт по каждой из них — перечитайте
GET /employees/{employee_id} и примените upsert так же, как для
employee.changed.
События по документам — document.sent, document.completed, document.rejected
и другие — вынесены в отдельный гайд События документов.
employee.changed объединяет создание и изменение: получив его, перечитайте ресурс
и примените upsert. Отдельного created нет намеренно — источник не всегда может
надёжно отличить первое появление от последующего изменения.Проверка подписи
Каждая доставка несёт заголовок:v1 — это HMAC-SHA256(secret, "{t}.{тело запроса}") в hex. Проверьте на
своей стороне и отклоните запрос, если подпись не сходится или t старше пяти
минут (защита от повторов).
Доставка и надёжность
- Ответьте
2xxбыстро и обрабатывайте асинхронно. Таймаут — 10 секунд. - При неуспехе событие доставляется повторно — до 8 попыток с экспоненциально растущей задержкой, суммарно около суток.
- После 20 подряд окончательно проваленных доставок эндпоинт автоматически отключается, и новые события на него не отправляются.
- Доставка — «хотя бы один раз»: возможны повторы. Дедуплицируйте по
idсобытия: то же значение приходит в заголовкеDoodocs-Event-Id, поэтому проверить повтор можно не разбирая тело.
Управление подпиской
Изменить адрес или набор событий
PATCH /webhook_endpoints/{id} меняет url, event_types или и то и другое.
Не переданные поля сохраняют текущее значение, пустой список событий тоже
означает «не менять». Секрет при этом не меняется. Смена адреса дополнительно
обнуляет счётчик неудачных доставок, поэтому починка не наследует отказы
мёртвого хоста.
Ротация секрета
POST /webhook_endpoints/{id}/_rotate_secret выдаёт новый секрет и возвращает
его один раз. Прежний секрет продолжает работать заданное время — по умолчанию
24 часа, но окно задаётся полем grace_seconds (максимум неделя). Всё это
время каждая доставка несёт две подписи.
Doodocs-Signature.
Уложитесь в окно до previous_secret_expires_at: после него старый секрет
перестаёт подписываться, и проверка по нему начнёт падать.
Если секрет утёк
Плавная ротация оставляет утёкший секрет рабочим на всё окно — этого времени достаточно, чтобы подделать доставки. Передайтеgrace_seconds: 0: прежний
секрет перестаёт действовать немедленно.
Повторное включение
POST /webhook_endpoints/{id}/_reenable снимает автоотключение и обнуляет
счётчик отказов. То же самое можно сделать в приложении: Настройки → Интеграции
→ «Для разработчиков» → Вебхуки.
Журнал доставок
GET /webhook_endpoints/{endpoint_id}/deliveries показывает попытки доставки от
новых к старым: что отправлено, когда и что ответил ваш сервер. Записи хранятся
30 дней и содержат только идентификаторы и исход, без полезной нагрузки.
event_id совпадает с заголовком Doodocs-Event-Id и полем id в теле, поэтому
запись сопоставляется с вашим собственным логом. Фильтр status принимает
WEBHOOK_DELIVERY_STATUS_SUCCEEDED или WEBHOOK_DELIVERY_STATUS_FAILED.
Отправить событие заново
POST /webhook_endpoints/{endpoint_id}/deliveries/{delivery_id}/_redeliver
ставит то же событие в очередь ещё раз — так восстанавливают пропущенное за
время сбоя.
event_id, поэтому получатель, который дедуплицирует
по нему, узнает повтор и не обработает событие дважды. Отключённой подписке
повтор отклоняется кодом DEVELOPER.WEBHOOK_DELIVERY_NOT_REDELIVERABLE: сначала
включите её.
Отладка
Отправьте тестовое событие на существующую подписку:status_code — код, которым ваш сервер ответил на тестовую доставку. Если
соединение не удалось, status_code будет 0, а причина окажется в error.