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

Вебхуки

Bona Fide отправляет HTTPS-запрос POST на вашу конечную точку для каждого события конверта, на которое вы подписаны. Доставки соответствуют спецификации Standard Webhooks, поэтому проверить их может любая из её библиотек.

В разделе Настройки › Вебхуки или с помощью ключа API с областью доступа org:manage:

Окно терминала
curl https://api.bonafidesign.com/v1/webhook-endpoints \
-H "Authorization: Bearer $BONA_FIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/hooks/bona-fide",
"description": "Production",
"events": ["envelope.completed", "envelope.sealed", "envelope.declined"]
}'

Ответ содержит secret (whsec_…) — только в этот раз. Пустой список events подписывает на все события. URL должен быть публичным адресом https://. Конечная точка относится к одному режиму: конечные точки, созданные тестовым ключом, получают только события конвертов в тестовом режиме, а рабочие — только рабочие события.

PATCH /v1/webhook-endpoints/{id} меняет URL, события или описание, а также приостанавливает или возобновляет работу конечной точки (enabled). POST /v1/webhook-endpoints/{id}/test отправляет тестовый запрос webhook.test.

Тип Когда
envelope.created Конверт создан
envelope.ready Документ сформирован, и конверт готов для получателей
recipient.sent Наступила очередь получателя (а при доставке по почте ему отправлена ссылка)
recipient.viewed Получатель впервые открыл страницу подписания
recipient.signed Получатель отправил подпись (или контрподпись была поставлена автоматически)
recipient.declined Получатель отказался
envelope.completed Все получатели завершили работу. data.values содержит переменные и все введённые значения
envelope.sealed Запечатанный PDF готов к скачиванию
envelope.declined Конверт завершён, потому что получатель отказался
envelope.voided Вы аннулировали конверт
envelope.expired expiresAt наступило до завершения
signing_link.used Посетитель создал конверт по постоянной ссылке для подписания

acceptance.created зарезервировано в схеме и пока не генерируется.

{
"id": "evt_9sKq2Lm4Tz",
"type": "envelope.completed",
"createdAt": "2026-10-13T14:09:52.004Z",
"testMode": false,
"data": {
"envelope": {
"id": "env_8Gm2kQ4rT1",
"status": "completed",
"seal": "pending",
"recipients": [{ "id": "rcp_5Jw0pLs9Xe", "role": "client", "status": "completed", "…": "…" }],
"externalId": "user_8841",
"metadata": {},
"…": "…"
},
"values": {
"variables": { "client": { "name": "Acme Robotics, Inc." }, "effective_date": "2026-10-13" },
"recipients": {
"client": { "client_title": "CEO", "start_date": "2026-11-01" }
}
}
}
}

data.envelope — полный конверт на момент события. data.values есть только в envelope.completed: отправленные вами переменные и значения полей каждой роли по идентификатору поля.

Каждая доставка содержит:

Заголовок Значение
webhook-id Идентификатор события (evt_…); одинаков при каждом повторе. Используйте его для дедупликации
webhook-timestamp Время подписания этой попытки в секундах Unix
webhook-signature v1,<base64 HMAC-SHA256>, через пробел, если подписывает несколько секретов
user-agent BonaFide-Webhooks/1

Подпись — это HMAC-SHA256 от {webhook-id}.{webhook-timestamp}.{raw body} с ключом, полученным декодированием из base64 части секрета после whsec_. Проверяйте необработанное тело (до разбора JSON), сравнивайте за постоянное время и отклоняйте метки времени, отличающиеся от ваших часов более чем на пять минут.

import express from 'express';
import { Webhook } from 'standardwebhooks'; // npm i standardwebhooks
const wh = new Webhook(process.env.BONA_FIDE_WEBHOOK_SECRET!); // whsec_…
const app = express();
app.post('/hooks/bona-fide', express.raw({ type: 'application/json' }), (req, res) => {
let event;
try {
event = wh.verify(req.body, req.headers as Record<string, string>); // throws if forged or too old
} catch {
return res.sendStatus(400);
}
queue.add(event); // do the work asynchronously and answer quickly
res.sendStatus(204);
});

Доставка считается успешной при любом ответе 2xx в течение 10 секунд. Перенаправления не выполняются. Во всех остальных случаях доставка повторяется с увеличивающимися интервалами: через 5 секунд, 5 минут, 30 минут, 2 часа, 5 часов, 10 часов, 14 часов, 20 часов и 24 часа, но не дольше трёх дней после события. После этого доставка помечается как неудачная.

  • Отвечайте быстро и обрабатывайте асинхронно; выполняйте дедупликацию по webhook-id.
  • Доставки могут приходить не по порядку: проверяйте data.envelope.status, а не рассчитывайте на определённую последовательность.
  • GET /v1/webhook-endpoints/{id}/deliveries выводит последние попытки с кодами статуса, ошибками и временем, а для каждой конечной точки показывается доля успешных доставок за 24 часа (health).
  • POST /v1/events/{id}/redeliver повторно отправляет событие на конечную точку, например после сбоя.

POST /v1/webhook-endpoints/{id}/secret/rotate сразу возвращает новый секрет. В течение следующих 24 часов каждая доставка подписывается и новым, и старым секретом (две записи в webhook-signature), поэтому вы можете развернуть новый секрет, не теряя событий.