> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook-алерты Didit: уведомления команды на ваш endpoint

> Didit отправляет алерты команды на ваш HTTPS endpoint: проверки, AML-мониторинг, биллинг, сбои webhook. Подпись HMAC-SHA256, повторы 24 часа.



Промпт для webhook оповещений
        Скопировать промпт в буфер обмена



      
      

\<pre>

\`Add an endpoint that receives Didit alert webhooks (team alerts, not verification results).

\## What to build
A public HTTPS POST endpoint (e.g. /api/didit/alerts) that:
1\. Reads the raw request body as bytes (do NOT parse and re-serialize the JSON before verifying)
2\. Checks that X-Timestamp is within 300 seconds of now
3\. Recomputes X-Signature-V2 = hex(HMAC-SHA256(secret, X-Timestamp + "." + rawBody)), where secret is the FULL signing secret including its "whsec_" prefix
4\. Compares the two signatures in constant time
5\. Parses the JSON, ignores an alert_id it already handled, answers 2xx within 5 seconds, then processes the alert asynchronously
6\. Answers a failed check with 400, not 401 (401, 403, 404 and 410 mark the destination for reconnection)

\## Headers
\- Content-Type: application/json
\- User-Agent: Didit-Alerts/1.0
\- X-Timestamp: Unix seconds, new on every attempt
\- X-Signature-V2: hex HMAC-SHA256 described above
This is NOT the signature scheme of verification webhooks. Alert webhooks send no X-Signature and no X-Signature-Simple.

\## Body (compact JSON, keys sorted)
\{
"alert_id": "alt_<16 hex characters>",  // the same on every retry: deduplicate on it
"created_at": 1791884460,                // Unix seconds when Didit created the alert
"data": { "event": "session_in_review", "label": "Session", "value": "#1042" },
"organization": { "id": "<uuid>", "name": "<your organization>" },
"severity": "info" | "warning" | "critical",
"subtitle": "<English explanation, can be empty>",
"title": "<English headline>",
"url": "<link to the Didit console>",
"webhook_type": "alert.<type>"
}

\## webhook_type values
\- alert.verification.in_review, alert.business_verification.in_review
\- alert.transaction.held, alert.aml.monitoring_hit
\- alert.billing.low_balance, alert.billing.autorefill_failed (data.paused), alert.billing.backup_card_charged
\- alert.webhook.delivery_failing
\- alert.service.degraded (data.status "degraded" or "down"), alert.service.recovered (data.status "recovered")
\- alert.test (sent from the console's Send test button)
Ignore webhook_type values and data keys you do not recognize.

\## Retries
\- 2xx = delivered. Redirects are not followed.
\- Timeout (5 s), connection failure, 5xx, 408, 425 or 429 = retried 5 times, after 1 min, 5 min, 30 min, 3 h and 20 h 24 min (about 24 h).
\- Any other response is final.
\- Test alerts are never retried.

\## Secret rotation
Rotating the secret in the console replaces it at once. There is no overlap and no header signed with the previous secret: deploy the new secret immediately.

Docs: /integration/alert-webhooks`

\</pre>



      
      

Вставьте в

webhook оповещений отправляют оповещения вашей команды на HTTPS endpoint, которым вы управляете.
Используйте их, чтобы передавать оповещения в инструмент для дежурных, очередь тикетов или собственную панель операций.webhook оповещений отделены от [webhook верификации](https://docs.didit.me/ru/integration/webhooks.md).
webhook верификации передают в ваше приложение результаты сессий, сущностей и транзакций.
webhook оповещений сообщают вашей команде, что требуется внимание: верификация ждёт проверки, платёж не прошёл или возник сбой сервиса.
У них есть собственный payload, секрет для подписи, подпись и расписание повторных попыток.## Настройка адреса назначенияwebhook оповещений настраиваются в Business Console, а не через API.
Участники, которые могут управлять настройками организации, могут добавлять, изменять и тестировать адреса назначения.**1. Добавьте адрес назначения**В разделе **Настройки организации** > **Уведомления команды** добавьте адрес назначения типа **webhook оповещений** с URL вашего endpoint.
URL должен использовать `https://` и резолвиться в публичный адрес.**2. Сохраните секрет для подписи**Didit показывает полный секрет для подписи, который начинается с `whsec_`, только один раз.
Сохраните его сразу: после этого консоль показывает только последние четыре символа.**3. Направьте оповещения на адрес**Добавление адреса назначения пока ничего на него не отправляет.
Добавьте адрес назначения в правила маршрутизации тех оповещений, которые он должен получать.**4. Отправьте тест**Нажмите **Отправить тест**, чтобы отправить оповещение `alert.test`, и проверьте, что ваш endpoint его принимает.В одной организации в разделе **Уведомления команды** может быть до 25 адресов назначения.На адрес назначения попадает только активность из продакшн-среды.
Оповещения о верификациях в песочнице, верификациях бизнеса, транзакциях и срабатываниях AML-мониторинга на него не отправляются никогда.## Типы оповещений| `webhook_type` | `severity` | Когда Didit отправляет оповещение |
| - | - | - |
| `alert.verification.in_review` | `warning` | Верификация завершена и помечена для ручной проверки. |
| `alert.business_verification.in_review` | `warning` | Верификация бизнеса требует ручной проверки. |
| `alert.transaction.held` | `warning` | Ваши правила мониторинга транзакций пометили транзакцию для проверки. |
| `alert.aml.monitoring_hit` | `warning` | Постоянный AML-мониторинг нашёл новое совпадение. |
| `alert.billing.low_balance` | `warning` | Баланс вашей организации упал ниже порога, который вы задали. |
| `alert.billing.autorefill_failed` | `critical` | Автоматическое пополнение баланса не прошло, потому что карта была отклонена. |
| `alert.billing.backup_card_charged` | `warning` | Автоматическое списание с одной карты было отклонено, и платёж был выполнен с резервной карты. |
| `alert.webhook.delivery_failing` | `warning` | Доставка в один из ваших endpoint'ов [webhook верификации](https://docs.didit.me/ru/integration/webhooks.md) завершается ошибками. |
| `alert.service.degraded` | `critical` | Сервис, который использует ваша организация, работает со сбоями или недоступен. |
| `alert.service.recovered` | `info` | Этот сервис снова работает в штатном режиме. |
| `alert.test` | `info` | Кто-то нажал **Отправить тест** на адресе назначения. |## payloadКаждое оповещение отправляется методом `POST` с телом в формате JSON.
Didit отправляет тело как компактный JSON с отсортированными ключами.
Для удобства чтения этот пример отформатирован:```json theme={null}
{
  "alert_id": "alt_9f2c4e1a7b3d5c80",
  "created_at": 1791884460,
  "data": {
    "event": "session_in_review",
    "label": "Session",
    "value": "#1042"
  },
  "organization": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name": "Example Org"
  },
  "severity": "warning",
  "subtitle": "A KYC or KYB verification was completed and flagged for manual review.",
  "title": "Verification in review",
  "url": "https://business.didit.me/console/3fa85f64-5717-4562-b3fc-2c963f66afa6/9b2d1c4e-8f3a-4e6b-a1d2-5c7e9f0b3a14/verifications?session-id=1b4e28ba-2fa1-41d2-883f-0016d3cca427",
  "webhook_type": "alert.verification.in_review"
}
```| Поле | Тип | Описание |
| - | - | - |
| `webhook_type` | string | `alert.` и тип оповещения. См. [Типы оповещений](#типы-оповещений). |
| `alert_id` | string | `alt_` и 16 шестнадцатеричных символов. Каждая повторная отправка оповещения содержит тот же `alert_id`, поэтому используйте его, чтобы игнорировать дубликаты. |
| `created_at` | integer | Unix-время в секундах, когда Didit создал оповещение. При повторных отправках не меняется. |
| `severity` | string | `info`, `warning` или `critical`. |
| `organization` | object | `id` (UUID) и `name` вашей организации. |
| `title` | string | Короткий заголовок на английском языке. |
| `subtitle` | string | Пояснение на английском языке. Может быть пустым. |
| `data` | object | Сведения о том, к чему относится оповещение. См. ниже. |
| `url` | string | Ссылка на связанную страницу в консоли Didit. |`data` всегда содержит:- `event`: ключ события, например `session_in_review`.
- `label` и `value`: сведения о том, к чему относится оповещение, например `Session` и `#1042`.Некоторые типы оповещений добавляют ключи в `data`:- `alert.billing.autorefill_failed` добавляет `paused`, логическое значение, которое равно `true`, если автопополнение приостановлено до обновления карты.
- `alert.service.degraded` и `alert.service.recovered` добавляют `service`, `status`, `incident_id`, `service_name`, `state`, `affected`, `country_name`, `started_at`, `resolved_at`, `duration_minutes`, `title` и `body`.
  `status` принимает значение `degraded` или `down` для `alert.service.degraded` и значение `recovered` для `alert.service.recovered`.
  `service` использует ключи сервисов из [`GET /v3/status/`](https://docs.didit.me/ru/api-reference/service-status), а `incident_id` совпадает с `id` инцидента в том же ответе.Оповещения никогда не содержат персональных данных конечного пользователя или данных документов.
Они идентифицируют сессию, транзакцию или другой объект по его номеру и содержат ссылку на него в консоли.
Игнорируйте значения `webhook_type` и ключи `data`, которые вы не распознаёте, чтобы новые значения и ключи не ломали ваш endpoint.## Заголовки запроса| Заголовок | Пример | Значение |
| - | - | - |
| `Content-Type` | `application/json` | Тело в формате JSON. |
| `User-Agent` | `Didit-Alerts/1.0` | Идентифицирует отправителя оповещений Didit. |
| `X-Timestamp` | `1791884462` | Unix-время в секундах, когда Didit подписал эту попытку. При каждой попытке значение новое. |
| `X-Signature-V2` | 64 шестнадцатеричных символа | Подпись HMAC-SHA256 этой попытки. |## Проверка подписи`X-Signature-V2` представляет собой HMAC-SHA256 строки `<X-Timestamp>.<raw body>`, записанный в шестнадцатеричном виде и вычисленный с использованием секрета для подписи адреса назначения.* Используйте полный секрет в том виде, в котором Didit его показал, включая префикс `whsec_`.
* Подписывайте необработанные байты запроса в том виде, в котором вы их получили. Разбор JSON и его повторная сериализация могут изменить байты и сломать проверку.
* Сравнивайте подписи за постоянное время.
* Отклоняйте запрос, если значение `X-Timestamp` отличается от текущего времени более чем на 5 минут. Didit подписывает каждую попытку, включая каждую повторную, новой меткой времени, поэтому это окно никогда не отклонит настоящую повторную попытку.
* На неудачную проверку отвечайте кодом `400`. Ответы `401`, `403`, `404` или `410` помечают адрес назначения для переподключения (см. [Повторные попытки](#повторные-попытки)).> [!WARNING]
>
> Webhook уведомлений подписываются не так, как webhook верификации.
> Заголовок тоже называется `X-Signature-V2`, но подписываемая строка другая, а webhook уведомлений не отправляют `X-Signature` или `X-Signature-Simple`.
> Не используйте повторно ваш верификатор webhook верификации.### Node.js (Express)```javascript theme={null}
const express = require("express");
const crypto = require("crypto");

const app = express();
// The full secret Didit showed when you added the destination, including "whsec_".
const ALERT_SECRET = process.env.DIDIT_ALERT_SECRET;
const TOLERANCE_SECONDS = 300;

// express.raw keeps the body as the exact bytes Didit sent.
app.post("/api/didit/alerts", express.raw({ type: "application/json" }), (req, res) => {
  const timestamp = req.get("X-Timestamp") || "";
  const signature = Buffer.from(req.get("X-Signature-V2") || "", "utf8");

  const now = Math.floor(Date.now() / 1000);
  if (!/^\d+$/.test(timestamp) || Math.abs(now - Number(timestamp)) > TOLERANCE_SECONDS) {
    return res.status(400).send("Stale or missing timestamp");
  }

  const expected = Buffer.from(
    crypto.createHmac("sha256", ALERT_SECRET).update(`${timestamp}.`).update(req.body).digest("hex"),
    "utf8",
  );
  if (signature.length !== expected.length || !crypto.timingSafeEqual(signature, expected)) {
    return res.status(400).send("Invalid signature");
  }

  const alert = JSON.parse(req.body.toString("utf8"));
  res.sendStatus(204); // answer within 5 seconds, then process
  handleAlert(alert); // skip an alert_id you already handled
});
```### Python (Flask)```python theme={null}
import hashlib
import hmac
import json
import os
import time

from flask import Flask, request

app = Flask(__name__)
# The full secret Didit showed when you added the destination, including "whsec_".
ALERT_SECRET = os.environ["DIDIT_ALERT_SECRET"].encode("utf-8")
TOLERANCE_SECONDS = 300


@app.post("/api/didit/alerts")
def didit_alert():
    raw_body = request.get_data()  # the exact bytes Didit sent
    timestamp = request.headers.get("X-Timestamp", "")
    signature = request.headers.get("X-Signature-V2", "")

    try:
        too_old = abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS
    except ValueError:
        too_old = True
    if too_old:
        return "Stale or missing timestamp", 400

    expected = hmac.new(ALERT_SECRET, timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected.encode(), signature.encode()):
        return "Invalid signature", 400

    alert = json.loads(raw_body)
    handle_alert(alert)  # skip an alert_id you already handled; keep this fast
    return "", 204
```### Ротация секрета для подписиРотацию секрета для адреса назначения выполняйте в разделе **Уведомления команды**.
Didit показывает новый секрет один раз и подписывает им каждую последующую попытку, включая повторные попытки уведомлений, созданных до ротации.
Старый секрет перестаёт работать сразу.
Периода перехода нет, и ни один заголовок не содержит подпись, сделанную предыдущим секретом.
Сразу после ротации разверните новый секрет на вашем endpoint.## Повторные попыткиDidit считает доставленным любой ответ `2xx`.
Каждая попытка ждёт ваш ответ до 5 секунд и не следует перенаправлениям.Didit повторяет отправку уведомления после тайм-аута, ошибки соединения, ответа `5xx` или ответов `408`, `425` и `429`.
Он делает до 5 повторных попыток в течение примерно 24 часов:| Повторная попытка | Задержка после предыдущей попытки |
| - | - |
| 1-й | 1 минута |
| 2-й | 5 минут |
| 3-й | 30 минут |
| 4-й | 3 часа |
| 5-й | 20 часов 24 минуты |Любой другой ответ окончательный, включая `3xx` и любой другой `4xx`.
Тестовое сообщение, отправленное через **Отправить тест**, делает одну попытку и никогда не повторяется.Ответы `401`, `403`, `404` или `410` также помечают адрес назначения для переподключения: в консоли он отображается со статусом **Переподключить**.
То же происходит, если URL при отправке Didit разрешается в частный или иным образом заблокированный адрес.
Didit продолжает отправлять новые уведомления на адрес назначения и снова делает его активным после следующей успешной доставки или после обновления его URL.В разделе **Уведомления команды** в **Последние уведомления** показан каждый результат доставки, и оттуда можно повторить неудачную доставку.## Связанные материалы- [Webhook верификации](https://docs.didit.me/ru/integration/webhooks.md): результаты сессий, сущностей и транзакций для вашего приложения.
- [Статус сервиса](https://docs.didit.me/ru/api-reference/service-status.md): публичная лента, на которой основаны `alert.service.degraded` и `alert.service.recovered`.
- [Ручная проверка](https://docs.didit.me/ru/console/manual-review.md): что делать с верификацией, которая находится на проверке.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.