Webhooks: обзор¶
После завершения верификации (успешного или по таймауту) мы отправляем
POST-запрос на webhook_url, указанный при создании верификации.
Когда приходят¶
| Событие | type |
Когда |
|---|---|---|
| Успешная верификация | verification.completed |
Пользователь позвонил с правильного номера. Приходит через 1–3 секунды после звонка. |
| Истечение TTL | verification.expired |
За ttl_seconds (по умолчанию 300) звонок не поступил. |
verification.failed
Есть третий событийный тип verification.failed — внутренняя ошибка
обработки. По умолчанию он не отправляется наружу (в большинстве
случаев восстанавливается автоматически). Если для вашего use-case
нужно получать failed-события — напишите в саппорт.
Формат запроса¶
POST /your-endpoint HTTP/1.1
Host: your-app.example.com
Content-Type: application/json
User-Agent: TrueNum/1.0
X-Truenum-Signature: t=1748255418,v1=<hex_hmac_sha256>
{
"type": "verification.completed",
"verification_id": "ver_01HXYZ4K9P2N3M8R7Q5S6T0V1W",
"phone": "+79991234567",
"dial_number": "+74951234567",
"client_ref": "user-1234",
"caller_id": "+79991234567",
"completed_at": "2026-05-26T12:30:18+00:00"
}
Поля тела¶
| Поле | Тип | Описание |
|---|---|---|
type |
string | verification.completed или verification.expired. |
verification_id |
string | ID исходной верификации (тот же, что вернул POST /verifications/). |
phone |
string | Номер из запроса. |
dial_number |
string | Номер, на который пользователь звонил. |
client_ref |
string | Ваш идентификатор из запроса. |
caller_id |
string | Только в completed — реальный номер звонящего. Сравните с phone. |
completed_at |
string (ISO 8601) | Момент терминального события. Для completed — время звонка. Для expired — момент истечения TTL. |
Игнорируйте незнакомые поля
Мы можем добавлять новые поля без выпуска новой версии API. Ваш парсер должен быть толерантен к дополнительным ключам.
Что должен делать ваш эндпоинт¶
- Принять POST, прочитать тело (raw bytes — для верификации подписи!).
- Проверить подпись
X-Truenum-Signature— инструкция. Без проверки подписи — не доверяйте содержимому тела. - Найти у себя верификацию по
verification_idилиclient_ref. - Сравнить
caller_idсphone(дляcompleted). При совпадении — отметьте номер как верифицированный. - Ответить
2xx(рекомендуется200 OK) в течение 10 секунд.
Что считается успехом¶
HTTP 2xx (200–299). Любой другой статус (включая 3xx redirect) или
сетевая ошибка / таймаут — повторим. Подробнее — Ретраи.
Безопасность¶
- Эндпоинт должен быть публичным (наш SSRF-гард на стороне API отклоняет приватные/loopback/link-local адреса в момент создания верификации; см. Безопасность).
- Используйте HTTPS — TrueNum не отправляет webhook'и на HTTP, если только URL явно не помечен как dev.
- Не полагайтесь на IP-источник для аутентификации — мы можем менять egress-адреса. Полагайтесь на HMAC-подпись.
Идемпотентность приёма¶
При ретраях один и тот же verification_id + type может прилететь
несколько раз. Ваш обработчик должен быть идемпотентным:
def handle_webhook(payload: dict) -> None:
# Грубая идемпотентность через UNIQUE constraint.
try:
WebhookEvent.objects.create(
verification_id=payload["verification_id"],
event_type=payload["type"],
)
except IntegrityError:
return # уже обработали
# ... основная логика
Дашборд: журнал доставок¶
В дашборде → Webhooks → Deliveries видны последние попытки доставки:
- HTTP-код ответа вашего эндпоинта.
- Количество попыток.
- Тело запроса/ответа (для дебага).
- Кнопка Retry — ручной перезапуск доставки.
Если webhook никак не доходит и автоматические ретраи исчерпаны
(см. Ретраи), используйте Retry или поллинг через
GET /api/v1/verifications/{id}/.