Skip to content

Исходящие webhook-события

Исходящие webhook-события позволяют внешней системе получать уведомления о важных изменениях в SNABZHENETS+: создании сущностей, обновлении данных, смене статусов, архивировании и тестовой проверке доставки.

Короткий ответ

Подписка API/Webhooks отправляет HTTP-запрос во внешнюю систему, когда в SNABZHENETS+ происходит выбранное событие. Имя события передаётся в поле event тела webhook и в заголовке X-Snabplus-Event.

В подписке нужно выбрать хотя бы одно событие из публичного каталога. Пустой список событий не используется как режим «все события» и не принимается при создании или обновлении подписки.

Где получить каталог

Актуальный список публичных событий доступен через API:

http
GET /api/v1/integration-api/meta

События находятся в поле events ответа.

http
GET /api/v1/integrations/api-webhooks/installations/:id/console

События находятся в поле eventCatalog ответа.

Для настройки подписки используйте поле subscriptionEventCatalog: в нём только бизнес-события, доступные для выбора. integration.test_event не входит в этот список и отправляется только ручной кнопкой проверки подписки.

Настройка через API

Исходящие подписки можно создавать и обслуживать через Integration API:

http
GET    /api/v1/integration-api/webhook
POST   /api/v1/integration-api/webhook
PUT    /api/v1/integration-api/webhook/:id
DELETE /api/v1/integration-api/webhook/:id
POST   /api/v1/integration-api/webhook/:id/test

Все bodies и responses — JSON. Для чтения нужен scope webhooks.read или integration_api.read; для создания, изменения и теста — webhooks.write или integration_api.write; для отключения — webhooks.delete или integration_api.delete.

Создание принимает массив. Каждый элемент — отдельный webhook на одно событие и один URL:

http
POST /api/v1/integration-api/webhook
Content-Type: application/json
Authorization: Bearer spk_...
json
[
  {
    "name": "ERP procurement status",
    "event": "integration_api.procurement.status_changed",
    "url": "https://erp.example.com/hooks/snabzhenets",
    "isActive": true
  }
]

Ответ:

json
{
  "items": [
    {
      "id": "hook_id",
      "name": "ERP procurement status",
      "event": "integration_api.procurement.status_changed",
      "url": "https://erp.example.com/hooks/snabzhenets",
      "isActive": true,
      "successCount": 0,
      "failureCount": 0,
      "lastDeliveredAt": null,
      "lastErrorAt": null,
      "lastErrorMessage": null,
      "createdAt": "2026-06-09T09:00:00.000Z",
      "updatedAt": "2026-06-09T09:00:00.000Z",
      "secretPlaintext": "one-time-secret"
    }
  ]
}

GET /api/v1/integration-api/webhook возвращает те же параметры без secretPlaintext. DELETE не удаляет запись физически, а переводит webhook в isActive: false.

Пример изменения подписки:

http
PUT /api/v1/integration-api/webhook/hook_id
Content-Type: application/json
Authorization: Bearer spk_...
json
{
  "name": "ERP procurement updates",
  "event": "integration_api.procurement.updated",
  "url": "https://erp.example.com/hooks/procurements",
  "isActive": true
}

Пример отключения:

http
DELETE /api/v1/integration-api/webhook/hook_id
Authorization: Bearer spk_...

Пример тестовой доставки:

http
POST /api/v1/integration-api/webhook/hook_id/test
Authorization: Bearer spk_...

Тестовая доставка отправляет событие integration.test_event на URL подписки и возвращает результат попытки в JSON. Боевой секрет подписи в list/update/delete/test ответах не возвращается.

Сущности и события

СущностьСистемная сущностьСобытиеЧто означает
Закупкаprocurementintegration_api.procurement.createdСоздана новая закупка.
Закупкаprocurementintegration_api.procurement.updatedИзменены данные закупки без отдельного события смены статуса.
Закупкаprocurementintegration_api.procurement.status_changedУ закупки изменился статус.
Закупкаprocurementintegration_api.procurement.deletedЗакупка удалена или архивирована.
Номенклатураcatalog_itemintegration_api.catalog_item.createdСоздана новая позиция номенклатуры.
Номенклатураcatalog_itemintegration_api.catalog_item.updatedИзменены данные позиции номенклатуры.
Номенклатураcatalog_itemintegration_api.catalog_item.deletedПозиция номенклатуры удалена или архивирована.
Контрагентcounterpartyintegration_api.counterparty.createdСоздан новый контрагент.
Контрагентcounterpartyintegration_api.counterparty.updatedИзменены данные контрагента.
Контрагентcounterpartyintegration_api.counterparty.status_changedУ контрагента изменился статус.
Контрагентcounterpartyintegration_api.counterparty.deletedКонтрагент удалён или архивирован.
Складwarehouseintegration_api.warehouse.createdСоздан новый склад.
Складwarehouseintegration_api.warehouse.updatedИзменены данные склада.
Складwarehouseintegration_api.warehouse.deletedСклад удалён или архивирован.
Прайс-листprice_listintegration_api.price_list.createdСоздан новый прайс-лист.
Прайс-листprice_listintegration_api.price_list.updatedИзменены данные прайс-листа.
Прайс-листprice_listintegration_api.price_list.status_changedУ прайс-листа изменился статус.
Прайс-листprice_listintegration_api.price_list.deletedПрайс-лист удалён или архивирован.
Счётinvoiceintegration_api.invoice.createdСоздан новый счёт.
Счётinvoiceintegration_api.invoice.updatedИзменены данные счёта.
Счётinvoiceintegration_api.invoice.status_changedУ счёта изменился статус.
Счётinvoiceintegration_api.invoice.deletedСчёт удалён или архивирован.
Тест интеграцииintegrationintegration.test_eventТехническое тестовое событие для проверки подписки, URL, заголовков и подписи.

Типы событий

ТипСуффиксКогда отправляется
СозданиеcreatedВ SNABZHENETS+ появилась новая сущность.
ОбновлениеupdatedИзменились поля существующей сущности.
Смена статусаstatus_changedИзменился статус сущности, и внешняя система должна отдельно отреагировать на переход.
Удаление или архивированиеdeletedСущность стала неактивной, удалена или архивирована. Этот тип есть только у архивируемых сущностей.
Тестовое событиеtest_eventПользователь вручную проверяет webhook-подписку без изменения бизнес-данных.

Payload

Тело исходящего webhook остаётся стабильным:

json
{
  "event": "integration_api.procurement.status_changed",
  "eventId": "audit-log-uuid",
  "schemaVersion": "1.0",
  "resourceVersion": 7,
  "correlationId": "erp-order-1001",
  "tenantId": "tenant-uuid",
  "entity": {
    "type": "procurement",
    "id": "entity-uuid"
  },
  "occurredAt": "2026-05-28T10:00:00.000Z",
  "resource": {},
  "data": {
    "changes": {},
    "snapshot": {},
    "metadata": {}
  },
  "links": [
    {
      "externalSource": "erp",
      "externalId": "PO-1001",
      "entityType": "procurement",
      "entityId": "entity-uuid"
    }
  ]
}

Для документов и логистики доступны события:

  • integration_api.document_generation.completed
  • integration_api.document_generation.failed
  • integration_api.delivery_trip.created
  • integration_api.delivery_trip.updated
  • integration_api.delivery_trip.status_changed
  • integration_api.delivery_trip.stop_status_changed

resourceVersion позволяет сохранить порядок обновлений одной сущности, а eventId — безопасно удалить дубль. Постоянные ссылки на файлы в payload не передаются.

Заголовки и подпись

Каждый исходящий webhook подписывается HMAC-SHA256:

http
X-Snabplus-Event: integration_api.procurement.status_changed
X-Snabplus-Event-Id: audit-log-uuid
X-Snabplus-Timestamp: 1760000000000
X-Snabplus-Signature: sha256=<hex>

Подпись считается по строке:

text
{timestamp}.{rawBody}

Внешняя система должна проверить подпись до обработки события.

Повторы доставки

SNABZHENETS+ повторяет доставку при сетевой ошибке, 408, 429 и ответах 5xx. Каждая попытка видна в журнале доставки API/Webhooks. Worker атомарно захватывает доставку в lease, поэтому несколько экземпляров не отправляют её одновременно. DNS и публичность адреса проверяются заново непосредственно перед каждой попыткой.

Аудит восстанавливается через durable cursor: события, накопившиеся во время остановки worker, будут поставлены в очередь после запуска, а уникальная пара subscriptionId + eventId защищает от повторного создания доставки.

Связанные страницы

Документация платформы SNABZHENETS+.