Webhooks
Webhooks — это HTTP-уведомления, которые платформа отправляет на ваш URL, когда в системе происходит событие. Это противоположный по направлению механизм по сравнению с External API: API — pull, webhooks — push.
Сочетая два инструмента, можно построить двустороннюю интеграцию без необходимости периодически опрашивать API.
Как это работает
- В админке («Администрирование → Разработчикам → Webhooks → Создать подписку») вы указываете:
- URL получателя — HTTPS-эндпоинт на вашей стороне;
- События — например,
order.created,sale.completed; - Фильтр (опционально) — событие будет отправлено, только если payload удовлетворяет условию.
- При создании платформа генерирует HMAC-секрет. Он показывается один раз и хранится у вас; платформа подписывает каждый запрос этим секретом.
- Когда происходит событие, платформа:
- проверяет, что модуль-источник активен у компании (системный или
isEnabled = true); - находит подписки на это событие;
- применяет ваш фильтр;
- кладёт задачу в очередь доставки;
- воркер делает HTTP POST на ваш URL с JSON-payload и подписью.
- проверяет, что модуль-источник активен у компании (системный или
- Если ваш сервер вернул 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.
Каталог событий
События сгруппированы по бизнес-модулям. Доставка ведётся только для событий активных или системных модулей компании.
| Модуль | События |
|---|---|
core | user.invited, user.activated, user.blocked |
catalog | product.*, service.*, category.* (created/updated/deleted) |
clients | client.created, client.updated, client.archived, client.restored, client.interaction.created |
orders | order.created, order.updated, order.status_changed, order.cancelled |
cashier | sale.completed, sale.refunded, shift.opened, shift.closed |
warehouse | stock.movement.created, warehouse.receipt.created, warehouse.receipt.cancelled, stock.adjusted |
calendar | calendar.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.