Skip to main content

Webhooks

Webhooks — это HTTP-уведомления, которые платформа отправляет на ваш URL, когда в системе происходит событие. Это противоположный по направлению механизм по сравнению с External API: API — pull, webhooks — push.

Сочетая два инструмента, можно построить двустороннюю интеграцию без необходимости периодически опрашивать API.

Как это работает

  1. В админке («Администрирование → Разработчикам → Webhooks → Создать подписку») вы указываете:
    • URL получателя — HTTPS-эндпоинт на вашей стороне;
    • События — например, order.created, sale.completed;
    • Фильтр (опционально) — событие будет отправлено, только если payload удовлетворяет условию.
  2. При создании платформа генерирует HMAC-секрет. Он показывается один раз и хранится у вас; платформа подписывает каждый запрос этим секретом.
  3. Когда происходит событие, платформа:
    • проверяет, что модуль-источник активен у компании (системный или isEnabled = true);
    • находит подписки на это событие;
    • применяет ваш фильтр;
    • кладёт задачу в очередь доставки;
    • воркер делает HTTP POST на ваш URL с JSON-payload и подписью.
  4. Если ваш сервер вернул 2xx — доставка считается успешной. Иначе — будут повторные попытки с экспоненциальным backoff.

Формат запроса

POST /your/webhook/url HTTP/1.1
Content-Type: application/json
User-Agent: easymb-webhooks/1.0
X-MBS-Event: order.created
X-MBS-Delivery-Id: 9f2c…
X-MBS-Webhook-Id: a8f3…
X-MBS-Attempt: 1
X-MBS-Signature: sha256=ab12cd34…

{
  "event": "order.created",
  "companyId": "c-1",
  "occurredAt": "2026-05-17T10:32:00.000Z",
  "actorId": "u-42",
  "data": { /* полный объект заказа */ }
}

Каждое сообщение — это «envelope» с полями event, companyId, occurredAt, actorId и data. В data всегда лежит сериализованный объект сущности, к которой относится событие.

Верификация подписи

X-MBS-Signature: sha256=<hex> — HMAC-SHA256 от сырого тела запроса с использованием вашего секрета. Проверка обязательна — без неё нельзя доверять источнику.

Node.js

import { createHmac, timingSafeEqual } from 'node:crypto';

function isValid(secret, rawBody, signatureHeader) {
  const expected = 'sha256=' +
    createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');
  return (
    expected.length === signatureHeader.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
  );
}

Python

import hmac, hashlib

def is_valid(secret: str, raw_body: bytes, signature_header: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

PHP

function isValid(string $secret, string $rawBody, string $signatureHeader): bool {
    $expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
    return hash_equals($expected, $signatureHeader);
}

Идемпотентность

Один и тот же X-MBS-Delivery-Id гарантированно не будет повторно использован в успешной попытке. При сетевом сбое возможна доставка-дубликат: чтобы не обработать одно событие дважды, сохраняйте X-MBS-Delivery-Id и пропускайте уже виденные.

Retry-политика

При ошибке (тайм-аут, не-2xx ответ, обрыв соединения) платформа повторяет доставку:

  • максимум 7 попыток;
  • экспоненциальная задержка с базой 5 секунд (5s → 20s → 80s → 5m → 21m → 1h25m → ~5h);
  • общий «срок жизни» ≈ 22 часа;
  • после исчерпания всех попыток запись доставки переводится в статус failed и складывается в dead-letter.

В админке можно перезапустить доставку руками: «Webhooks → Доставки → Повторить». Это создаёт новую запись WebhookDelivery с тем же payload.

Каталог событий

События сгруппированы по бизнес-модулям. Доставка ведётся только для событий активных или системных модулей компании.

МодульСобытия
coreuser.invited, user.activated, user.blocked
catalogproduct.*, service.*, category.* (created/updated/deleted)
clientsclient.created, client.updated, client.archived, client.restored, client.interaction.created
ordersorder.created, order.updated, order.status_changed, order.cancelled
cashiersale.completed, sale.refunded, shift.opened, shift.closed
warehousestock.movement.created, warehouse.receipt.created, warehouse.receipt.cancelled, stock.adjusted
calendarcalendar.event.created, calendar.event.updated, calendar.event.deleted

Полный список и схемы payload-ов — в разделе External API.

Фильтрация

Поле «Фильтр» в конструкторе принимает плоский JSON { "<dot-path>": <value> }. Событие доставляется, только если все указанные поля совпадают со значениями в envelope (с поддержкой dot-notation для вложенных полей).

Пример: «уведомлять только о завершённых заказах»:

{"data.status": "completed"}

Best practices

  • Отвечайте быстро. Платформа считает доставку успешной, как только получает 2xx. Тяжёлую обработку выполняйте асинхронно (queue, background-job) у себя.
  • Используйте идемпотентность. Сохраняйте X-MBS-Delivery-Id хотя бы 30 дней и пропускайте дубли.
  • Проверяйте подпись. Если не проверить — любой узнавший URL сможет подделать события.
  • Никогда не пересылайте секрет. Если случайно опубликовали — пересоздайте подписку (старый секрет будет недействителен).
  • Включите TLS. В проде webhook URL должен начинаться с https://.

Тестовая доставка

Из админки подписки можно отправить тестовое сообщение — выберите тип события и нажмите «Отправить». В payload будет data.test = true. Это удобно при настройке нового получателя.

Где смотреть историю

«Webhooks → Доставки» — последние 100 попыток. Видны:

  • статус (pending / delivering / success / failed);
  • HTTP-код ответа;
  • тело ответа (обрезано до 2 КБ);
  • сообщение об ошибке;
  • длительность последней попытки;
  • кнопка «Повторить» для ручного re-delivery.

См. также: External API.