# Вебхуки

> Подписка на события платформы и проверка подписи доставки.

LegalStack доставляет события платформы на ваши HTTPS-эндпоинты. Подписками управляют через раздел API [«Безопасность и доступ» — вебхуки](https://docs.legalstack.ru/api/security/webhooks), нужен scope `webhooks:write`.

## Регистрация эндпоинта

Запрос:

```http
POST /api/v1/webhooks HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{ "url": "https://example.com/hooks/legalstack", "events": ["task.created", "signature.signed"] }
```

В ответе придёт секрет эндпоинта с префиксом `whsec_` — он нужен для проверки подписи. Все операции с подписками — в [справочнике API](https://docs.legalstack.ru/api/security/webhooks).

> **Секрет показывается один раз**
>
> Сохраните секрет сразу после создания эндпоинта. Платформа хранит его зашифрованным и повторно не покажет, можно только сгенерировать новый.

## Доставка

Каждое событие приходит POST-запросом с JSON-телом `{ id, type, teamId, timestamp, data }` и четырьмя заголовками:

| Заголовок | Значение |
| --- | --- |
| `x-legalstack-signature` | подпись `v1=<hex>` |
| `x-legalstack-timestamp` | unix-время отправки в секундах |
| `x-legalstack-event-id` | идентификатор события |
| `x-legalstack-event-type` | тип события |

Пример реальной доставки (подпись вычислена боевым кодом платформы):

```http
POST /ваш-endpoint HTTP/1.1
content-type: application/json
x-legalstack-signature: v1=29e7075a454efa9d2fdb5b5d15e1e4e93bfd2b635717bda86931b63566f71c16
x-legalstack-timestamp: 1752307200
x-legalstack-event-id: evt_01hzxamp1e
x-legalstack-event-type: task.created

{"id":"evt_01hzxamp1e","type":"task.created","teamId":"team_01hzxamp1e","timestamp":"2026-07-12T08:00:00.000Z","data":{"taskId":"task_01hzxamp1e","teamId":"team_01hzxamp1e","data":{"title":"Проверить договор"}}}
```

## Проверка подписи

Подпись — HMAC-SHA256 в hex от строки `timestamp + "." + payload`, ключ — секрет эндпоинта как обычная UTF-8-строка (без декодирования):

```text
secret    = "whsec_2b7e151628aed2a6abf7158809cf4f3c2b7e151628aed2a6abf7158809cf4f3c"
timestamp = "1752307200"  // значение заголовка x-legalstack-timestamp
payload   = тело запроса (сырые байты)

signature = "v1=" + hex(hmac_sha256(secret, timestamp + "." + payload))
          = "v1=29e7075a454efa9d2fdb5b5d15e1e4e93bfd2b635717bda86931b63566f71c16"
```

Сравнивайте подписи константным по времени сравнением и отклоняйте доставки со старым `timestamp` (например, старше 5 минут) для защиты от replay.

## Повторные доставки

Неуспешные доставки (не-2xx ответ) повторяются с экспоненциальной задержкой. Обрабатывайте события идемпотентно по `x-legalstack-event-id` — одно событие может прийти более одного раза.

Каталог всех типов событий — на странице [События вебхуков](https://docs.legalstack.ru/webhooks).

## Вебхук формы [#interface-webhook]

Форма может сама отправлять каждый ответ на ваш сервер. Включите **Webhook** на вкладке «Каналы» конструктора формы и укажите адрес, который начинается с `https://`. Секрет для подписи создаётся автоматически. Он хранится зашифрованным отдельно от настроек формы, поэтому его не видят те, кто может только просматривать форму.

В меню рядом с кнопкой «Проверить» пункт «Копировать секрет» получает действующий секрет с сервера, а «Перевыпустить секрет» выпускает новый: прежний перестаёт подходить сразу. То же доступно через API: `GET /api/v1/interfaces/{id}/webhook/secret` возвращает секрет, `POST` по тому же адресу перевыпускает его. Нужно право редактировать форму и scope `interfaces:write`.

Ответ приходит POST-запросом с теми же заголовками и той же подписью `v1`, что и события платформы. Тип события `interface.submitted`, а `x-legalstack-event-id` совпадает с `submissionId` ответа.

Тело:

```json
{
  "event": "interface.submitted",
  "interfaceId": "6f1c2e84-3b7a-4d0e-9c51-2a7f0b9e1d43",
  "submissionId": "a93e07c4-58d2-4f1b-b6e0-7c3d9a1f2e85",
  "data": {
    "_interfaceId": "6f1c2e84-3b7a-4d0e-9c51-2a7f0b9e1d43",
    "_source": "web",
    "_submittedAt": "2026-10-06T12:00:00.000Z",
    "_ipAddress": "203.0.113.24",
    "_userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
    "licensee_name": "Иванов Иван Иванович",
    "licensee_email": "ivanov@example.ru"
  },
  "channel": "web",
  "submittedAt": "2026-10-06T12:00:00.000Z"
}
```

В `data` лежат ответы по ключам полей формы и служебные значения с префиксом `_`: канал, из которого пришёл ответ (`web`, `embed`, `telegram` или `api`), время отправки, IP-адрес и User-Agent отправителя. Для ответов из Telegram добавляется `_telegramUserId`. Поле `channel` повторяет `_source`.

Кнопка «Проверить» отправляет на адрес тестовый ответ с полем `"test": true` и показывает код и время ответа вашего сервера. Если сервер ответил не 2xx, доставка повторяется с нарастающей задержкой, каждый раз с текущими адресом и секретом формы. Если выключить вебхук, недоставленные ответы больше не отправляются.

> **Секрет, заданный вручную**
>
> Если секрет вебхука формы вводили вручную, подпись остаётся в прежнем формате: HMAC-SHA256 в hex от тела запроса, без `v1=` и без `timestamp`. Перевыпустите секрет, чтобы перейти на общий формат подписи.
