Перейти к содержимому

Webhooks IZI: подписка на события

Опубликовано: · IZI Team

Webhooks — способ получать события из IZI в реальном времени без постоянного опроса API. Сессия началась, баланс пополнен, клиент зарегистрирован — IZI сам отправит POST на ваш endpoint.

Это удобнее polling’а для любой интеграции, которой нужна реакция на события: синхронизация с кассой, уведомления в мессенджер, начисление бонусов во внешней системе.

mutation CreateWebhook($input: CreateWebhookInput!) {
createWebhook(input: $input) {
id
url
events
secret
isActive
}
}
{
"input": {
"url": "https://your-service.example.com/izi/events",
"events": [
"SESSION_STARTED",
"SESSION_ENDED",
"BALANCE_TOPPED_UP"
],
"clubId": "club_abc123"
}
}

В ответе получите secret — сохраните его. Он нужен для верификации подписи. Повторно получить нельзя.

СобытиеКогда срабатывает
SESSION_STARTEDНачало игровой сессии на устройстве
SESSION_ENDEDЗавершение сессии
SESSION_PAUSEDПауза сессии
BALANCE_TOPPED_UPПополнение денежного баланса клиента
BONUS_CREDITEDНачисление бонусов
TARIFF_PURCHASEDПокупка тарифа клиентом
CLIENT_REGISTEREDРегистрация нового клиента
DEVICE_STATUS_CHANGEDИзменение статуса устройства (online/offline)
SHIFT_OPENEDОткрытие смены администратором
SHIFT_CLOSEDЗакрытие смены
ORDER_CREATEDНовый заказ в баре
PAYMENT_RECEIVEDВходящий платёж

Актуальный полный список через introspection:

{
__type(name: "WebhookEventType") {
enumValues { name description }
}
}

Каждое событие приходит как POST с JSON-телом:

{
"id": "evt_01HZ3K...",
"type": "SESSION_ENDED",
"timestamp": "2026-05-30T14:32:00Z",
"clubId": "club_abc123",
"data": {
"sessionId": "sess_xyz789",
"deviceId": "device_001",
"clientId": "client_456",
"duration": 3600,
"cost": 150,
"endReason": "MANUAL"
}
}

Поля id, type, timestamp, clubId — всегда присутствуют. Содержимое data зависит от типа события.

Каждый запрос от IZI содержит заголовок X-IZI-Signature — HMAC-SHA256 от тела запроса с вашим секретом.

const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
// Сравнение безопасное к timing-атакам
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// В вашем обработчике
app.post('/izi/events', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-izi-signature'];
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
processEvent(event);
res.status(200).send('OK');
});

Важно: используйте express.raw() или аналог — нужно именно сырое тело для корректного подсчёта подписи. Если парсите JSON до проверки — подпись не сойдётся.

IZI доставляет события с гарантией “хотя бы один раз”. Одно событие может прийти дважды при сетевых сбоях. Защититесь от дублей:

const processedEvents = new Set(); // в проде — Redis или БД
async function processEvent(event) {
if (processedEvents.has(event.id)) {
console.log(`Skipping duplicate event: ${event.id}`);
return;
}
await handleEvent(event); // ваша бизнес-логика
processedEvents.add(event.id);
}

Используйте поле event.id как ключ идемпотентности.

Если ваш endpoint не ответил 200 OK в течение 5 секунд, IZI повторит доставку:

ПопыткаЗадержка
230 секунд
35 минут
430 минут
52 часа
624 часа

После 6 неудачных попыток webhook переходит в статус FAILED. Для реактивации:

mutation ReactivateWebhook($id: ID!) {
updateWebhook(id: $id, input: { isActive: true }) {
id
isActive
}
}

Список всех webhook’ов организации:

query ListWebhooks {
webhooks {
id
url
events
isActive
lastDeliveredAt
failureCount
}
}

Удаление:

mutation DeleteWebhook($id: ID!) {
deleteWebhook(id: $id) {
success
}
}
Окно терминала
# Установите ngrok
brew install ngrok
# Откройте туннель
ngrok http 3000
# Используйте полученный URL при создании webhook
# https://abc123.ngrok.io/izi/events

Частые вопросы

Какие события поддерживают webhooks?

SESSION_STARTED, SESSION_ENDED, BALANCE_TOPPED_UP, TARIFF_PURCHASED, DEVICE_STATUS_CHANGED, CLIENT_REGISTERED — и ещё около 15 типов событий. Полный список через introspection: __type(name: WebhookEventType).

Что делает IZI если endpoint не отвечает?

IZI повторяет доставку с экспоненциальной задержкой: 30 сек, 5 мин, 30 мин, 2 часа, 24 часа. После 5 неудачных попыток webhook помечается как failed и требует ручной активации.

Можно ли подписаться на события конкретного клуба?

Да. При создании webhook укажите clubId — тогда события будут только по этому клубу. Без clubId приходят события по всем клубам организации.

Как протестировать webhook локально?

Используйте ngrok или cloudflare tunnel для временного публичного URL. Потом замените на production endpoint.