---
title: "Event Webhooks: события tuk-tuk.online на ваш сервер"
nav_title: "Event Webhooks"
url: https://docs.tuk-tuk.online/api/webhooks
markdown: https://docs.tuk-tuk.online/api/webhooks.md
section: "API для разработчиков"
description: "Event Webhooks tuk-tuk.online: HTTP POST уведомления о 50 типах событий (сообщения, диалоги, цепочки, каналы, AI, платежи) с подписью HMAC-SHA256 в заголовке X-Webhook-Signature. Формат, проверка подписи, список событий."
keywords: ["webhooks API", "webhook события", "серверные уведомления", "HMAC подпись webhook", "HTTP callback", "webhook интеграция", "event-driven API", "webhook сообщения мессенджер"]
lang: ru
product: tuk-tuk.online
app: https://lk.tuk-tuk.online
api_base: https://back.tuk-tuk.online
---

> [!AI] Кратко
> Event Webhooks tuk-tuk.online: платформа отправляет HTTP POST с JSON на URL вашего сервера при каждом из 50 типов событий (сообщения, диалоги, цепочки, каналы, AI, платежи, календарь, интеграции, SMS, файлы). Каждый запрос подписан HMAC-SHA256 секретным ключом вебхука.
>
> - Настройка в кабинете: Настройки проекта → Вебхуки → «Создать вебхук»: URL, типы событий, секретный ключ. Там же включение/выключение, перегенерация ключа, логи доставки.
> - Заголовки запроса: `Content-Type: application/json`, `X-Webhook-Event` (тип события), `X-Webhook-Timestamp` (ISO 8601), `X-Webhook-Signature` (HMAC-SHA256 сырого тела, hex, ключ: секрет вебхука).
> - Тело: `{ "event": "<тип>", "timestamp": "<ISO 8601>", "data": { ... } }`; состав `data` зависит от типа события.
> - Группы событий (50 типов): `message.*` (3), `dialog.*` (8), `chain.*` (7), `channel.*` (5), `ai.*` (10), `payment.*`/`balance.changed`/`tier.changed` (5), `calendar.*` (3), `integration.*` (3), `sms.*` (4), `file.*` (2).
> - Доставка: таймаут 10 секунд, без повторных попыток (fire-and-forget). После 50 последовательных ошибок вебхук отключается; успешная доставка обнуляет счётчик.
> - Проверка подписи: HMAC-SHA256 от raw body с секретом, сравнение через `crypto.timingSafeEqual` (Node.js) или `hash_equals` (PHP); при несовпадении отвечать `401`.
>
> Связанные страницы: https://docs.tuk-tuk.online/api, https://docs.tuk-tuk.online/api/rest, https://docs.tuk-tuk.online/integrations/webhooks, https://docs.tuk-tuk.online/chains/blocks/webhook, https://docs.tuk-tuk.online/channels/webhook

# Event Webhooks

Event Webhooks доставляют события tuk-tuk.online на ваш сервер HTTP POST запросом в момент, когда они происходят: новое сообщение, смена статуса диалога, запуск цепочки, платёж. Соединение server-to-server, без постоянного подключения с вашей стороны.

Каждый запрос подписан HMAC-SHA256, поэтому подделать событие без вашего секретного ключа нельзя. Всего 50 типов событий в 10 группах.

## Для ИИ-агента

- Чтобы получать события, создай вебхук в кабинете: Настройки проекта → Вебхуки → «Создать вебхук», укажи URL, выбери типы событий, скопируй секретный ключ.
- Каждое событие приходит как HTTP POST с JSON `{ "event", "timestamp", "data" }` и заголовками `X-Webhook-Event`, `X-Webhook-Timestamp`, `X-Webhook-Signature`.
- Чтобы проверить подлинность, посчитай HMAC-SHA256 от сырого тела запроса с секретным ключом (hex) и сравни с `X-Webhook-Signature` через безопасное сравнение.
- Чтобы узнать о новом сообщении клиента, подпишись на `message.inbound`: в `data` придут `messageId`, `dialogId`, `body`, `channelType`, `channelId`, `mediaType`.
- Отвечай `200` в течение 10 секунд. Повторных попыток нет; после 50 ошибок подряд вебхук отключается.

## Как подключить

1. Откройте [Настройки проекта](https://lk.tuk-tuk.online/settings), раздел **Вебхуки**.
2. Нажмите **Создать вебхук**.
3. Укажите URL вашего сервера (рекомендуется HTTPS).
4. Выберите типы событий, которые хотите получать.
5. Скопируйте секретный ключ: он нужен для проверки подписи.

В настройках вебхука доступны: включение и выключение, смена URL и списка событий, перегенерация секретного ключа, логи доставки.

## Формат доставки

При наступлении события tuk-tuk.online отправляет HTTP POST на ваш URL.

### Заголовки

| Заголовок | Описание |
|---|---|
| `Content-Type` | `application/json` |
| `X-Webhook-Signature` | HMAC-SHA256 подпись тела запроса (hex) |
| `X-Webhook-Event` | Тип события (например, `message.inbound`) |
| `X-Webhook-Timestamp` | Время события в формате ISO 8601 |

### Тело запроса

Все события приходят в единой структуре:

```json
{
  "event": "message.inbound",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "messageId": "msg-uuid",
    "dialogId": "dialog-uuid",
    "body": "Привет!",
    "channelType": "TELEGRAM_BOT",
    "channelId": "ch-uuid",
    "mediaType": null
  }
}
```

| Поле | Тип | Описание |
|---|---|---|
| `event` | string | Тип события |
| `timestamp` | string | Время события, ISO 8601 |
| `data` | object | Данные события, состав зависит от типа |

## Проверить подпись

Заголовок `X-Webhook-Signature` содержит HMAC-SHA256 подпись сырого тела запроса в hex-формате, ключ подписи: секретный ключ вебхука. Проверяйте подпись до разбора JSON, иначе любой сможет прислать вам поддельное событие.

> [!WARNING]
> Считайте HMAC от сырого тела запроса (raw body), а не от повторно сериализованного JSON. Сравнивайте строки безопасной функцией: `crypto.timingSafeEqual` в Node.js, `hash_equals` в PHP.

**JS / TS**

```javascript
const crypto = require('crypto');
const express = require('express');
const app = express();

const WEBHOOK_SECRET = 'your_secret_key';

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  const { event, data } = JSON.parse(req.body);
  console.log('Event:', event, data);

  res.status(200).send('OK');
});
```

**PHP**

```php
<?php
$secret = 'your_secret_key';
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $body, $secret);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

$payload = json_decode($body, true);
$event = $payload['event'];
$data = $payload['data'];

// Обработка события
http_response_code(200);
echo 'OK';
```

## Пример обработчика

Тестовый запрос для проверки вашего обработчика и полный пример с разбором нескольких типов событий.

**cURL**

```bash
# Тестовый запрос для проверки вашего обработчика
curl -X POST https://your-server.com/webhook \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Signature: your_hmac_hex" \
  -H "X-Webhook-Event: message.inbound" \
  -H "X-Webhook-Timestamp: 2025-06-15T10:30:00.000Z" \
  -d '{
    "event": "message.inbound",
    "timestamp": "2025-06-15T10:30:00.000Z",
    "data": {
      "messageId": "msg-uuid",
      "dialogId": "dialog-uuid",
      "body": "Привет!",
      "channelType": "TELEGRAM_BOT",
      "channelId": "ch-uuid",
      "mediaType": null
    }
  }'
```

**JS / TS**

```javascript
const crypto = require('crypto');
const express = require('express');
const app = express();

const SECRET = 'your_secret_key';

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-webhook-signature'];
  const expected = crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  const { event, timestamp, data } = JSON.parse(req.body);

  switch (event) {
    case 'message.inbound':
      console.log('Новое сообщение:', data.body, 'от', data.dialogId);
      break;
    case 'dialog.created':
      console.log('Новый диалог:', data.dialogId, data.clientName);
      break;
    case 'payment.succeeded':
      console.log('Оплата:', data.paymentId, data.amount);
      break;
    default:
      console.log('Событие:', event, data);
  }

  res.status(200).send('OK');
});

app.listen(3000);
```

**PHP**

```php
<?php
$secret = 'your_secret_key';
$body = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $body, $secret);

if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit('Invalid signature');
}

$payload = json_decode($body, true);
$event = $payload['event'];
$data = $payload['data'];

switch ($event) {
    case 'message.inbound':
        error_log("Новое сообщение: {$data['body']}");
        break;
    case 'dialog.created':
        error_log("Новый диалог: {$data['dialogId']}");
        break;
    case 'payment.succeeded':
        error_log("Оплата: {$data['paymentId']} на {$data['amount']}");
        break;
    default:
        error_log("Событие: $event");
}

http_response_code(200);
echo 'OK';
```

## Типы событий

50 типов в 10 группах. У каждой группы таблица событий и примеры тела `data` в раскрывающихся блоках.

### Сообщения

| Событие | Описание |
|---|---|
| `message.inbound` | Входящее сообщение от клиента |
| `message.outbound` | Исходящее сообщение оператора или бота |
| `message.status_changed` | Изменение статуса отправленного сообщения |

#### <code>message.inbound</code>: входящее сообщение от клиента

```json
{
  "event": "message.inbound",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "messageId": "msg-550e8400-e29b-41d4-a716-446655440000",
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "body": "Привет! Хочу узнать о статусе заказа",
    "channelType": "TELEGRAM_BOT",
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "mediaType": null
  }
}
```

#### <code>message.outbound</code>: исходящее сообщение

```json
{
  "event": "message.outbound",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "messageId": "msg-660e8400-e29b-41d4-a716-446655440001",
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "body": "Здравствуйте! Ваш заказ в пути.",
    "channelType": "TELEGRAM_BOT",
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "mediaType": null
  }
}
```

#### <code>message.status_changed</code>: статус сообщения изменён

```json
{
  "event": "message.status_changed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "messageId": "msg-660e8400-e29b-41d4-a716-446655440001",
    "status": "COMPLETE",
    "previousStatus": "PENDING",
    "channelType": "TELEGRAM_BOT"
  }
}
```

### Диалоги

| Событие | Описание |
|---|---|
| `dialog.created` | Создан новый диалог |
| `dialog.updated` | Диалог обновлён |
| `dialog.tag_added` | К диалогу добавлен тег |
| `dialog.tag_removed` | Из диалога удалён тег |
| `dialog.note_added` | Добавлена заметка |
| `dialog.read` | Диалог прочитан оператором |
| `dialog.merged` | Диалоги объединены |
| `dialog.custom_fields_updated` | Обновлены кастомные поля |

#### <code>dialog.created</code>: создан новый диалог

```json
{
  "event": "dialog.created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "clientName": "Иван Петров",
    "channelType": "TELEGRAM_BOT",
    "clientIdentifier": "123456789"
  }
}
```

#### <code>dialog.updated</code>: диалог обновлён

```json
{
  "event": "dialog.updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "action": "status_changed",
    "status": "CLOSED"
  }
}
```

Возможные значения `action`: `closed`, `reopened`, `status_changed`, `phone_updated`, `email_updated`, `telegram_chat_id_updated`.

#### <code>dialog.tag_added</code>: добавлен тег

```json
{
  "event": "dialog.tag_added",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "tag": "VIP"
  }
}
```

#### <code>dialog.tag_removed</code>: удалён тег

```json
{
  "event": "dialog.tag_removed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "tag": "VIP"
  }
}
```

#### <code>dialog.note_added</code>: добавлена заметка

```json
{
  "event": "dialog.note_added",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "text": "Клиент просил перезвонить завтра в 10:00"
  }
}
```

#### <code>dialog.read</code>: диалог прочитан

```json
{
  "event": "dialog.read",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000"
  }
}
```

#### <code>dialog.merged</code>: диалоги объединены

```json
{
  "event": "dialog.merged",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "targetDialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "sourceDialogId": "dlg-789abcde-f012-3456-7890-abcdef012345"
  }
}
```

#### <code>dialog.custom_fields_updated</code>: обновлены кастомные поля

```json
{
  "event": "dialog.custom_fields_updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "fields": {
      "order_id": "ORD-12345",
      "priority": "high"
    }
  }
}
```

### Цепочки автоматизации

| Событие | Описание |
|---|---|
| `chain.created` | Создана цепочка |
| `chain.updated` | Цепочка обновлена |
| `chain.removed` | Цепочка удалена |
| `chain.toggled` | Цепочка включена или выключена |
| `chain.execution_started` | Запуск цепочки для клиента |
| `chain.execution_completed` | Цепочка завершена |
| `chain.execution_failed` | Ошибка выполнения цепочки |

#### <code>chain.created</code>: создана цепочка

```json
{
  "event": "chain.created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "chainId": "chain-550e8400-e29b-41d4-a716-446655440000",
    "name": "Приветственная цепочка",
    "template": null
  }
}
```

Поле `template` содержит ключ шаблона (`"welcome"`, `"support"` и т.д.), если цепочка создана из шаблона, или `null`, если создана с нуля.

#### <code>chain.updated</code>: цепочка обновлена

```json
{
  "event": "chain.updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "chainId": "chain-550e8400-e29b-41d4-a716-446655440000",
    "name": "Приветственная цепочка v2"
  }
}
```

#### <code>chain.removed</code>: цепочка удалена

```json
{
  "event": "chain.removed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "chainId": "chain-550e8400-e29b-41d4-a716-446655440000"
  }
}
```

#### <code>chain.toggled</code>: цепочка включена или выключена

```json
{
  "event": "chain.toggled",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "chainId": "chain-550e8400-e29b-41d4-a716-446655440000",
    "isActive": true
  }
}
```

#### <code>chain.execution_started</code>: запуск цепочки

```json
{
  "event": "chain.execution_started",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "chainId": "chain-550e8400-e29b-41d4-a716-446655440000",
    "chainName": "Приветственная цепочка",
    "runId": "run-660e8400-e29b-41d4-a716-446655440001",
    "clientIdentifier": "123456789"
  }
}
```

#### <code>chain.execution_completed</code>: цепочка завершена

```json
{
  "event": "chain.execution_completed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "chainId": "chain-550e8400-e29b-41d4-a716-446655440000",
    "runId": "run-660e8400-e29b-41d4-a716-446655440001",
    "clientIdentifier": "123456789"
  }
}
```

#### <code>chain.execution_failed</code>: ошибка выполнения

```json
{
  "event": "chain.execution_failed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "chainId": "chain-550e8400-e29b-41d4-a716-446655440000",
    "runId": "run-660e8400-e29b-41d4-a716-446655440001",
    "reason": "block_error",
    "blockId": "block-abc123",
    "blockType": "SEND_MESSAGE",
    "error": "Bot not running or send error"
  }
}
```

Возможные значения `reason`: `block_error` (ошибка в блоке), `max_steps_exceeded` (превышен лимит шагов, защита от бесконечных циклов).

### Каналы

| Событие | Описание |
|---|---|
| `channel.created` | Создан канал |
| `channel.removed` | Канал удалён |
| `channel.toggled` | Канал включён или выключен |
| `channel.config_updated` | Обновлена конфигурация канала |
| `channel.error` | Ошибка в работе канала |

#### <code>channel.created</code>: создан канал

```json
{
  "event": "channel.created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "type": "TELEGRAM_BOT",
    "name": "Мой Telegram бот"
  }
}
```

#### <code>channel.removed</code>: канал удалён

```json
{
  "event": "channel.removed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "type": "TELEGRAM_BOT",
    "name": "Мой Telegram бот"
  }
}
```

#### <code>channel.toggled</code>: канал включён или выключен

```json
{
  "event": "channel.toggled",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "type": "TELEGRAM_BOT",
    "isActive": false
  }
}
```

#### <code>channel.config_updated</code>: конфигурация обновлена

```json
{
  "event": "channel.config_updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "type": "TELEGRAM_BOT"
  }
}
```

#### <code>channel.error</code>: ошибка канала

```json
{
  "event": "channel.error",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "channelType": "TELEGRAM_BOT",
    "error": "Токен бота недействителен. Проверьте токен или создайте нового бота в @BotFather."
  }
}
```

### AI

| Событие | Описание |
|---|---|
| `ai.provider_created` | Добавлен AI-провайдер |
| `ai.provider_removed` | Удалён AI-провайдер |
| `ai.instruction_created` | Создана инструкция |
| `ai.instruction_updated` | Инструкция обновлена |
| `ai.instruction_removed` | Инструкция удалена |
| `ai.assistant_created` | Создан AI-ассистент |
| `ai.assistant_updated` | Ассистент обновлён |
| `ai.assistant_removed` | Ассистент удалён |
| `ai.assistant_toggled` | Ассистент включён или выключен |
| `ai.assistant_replied` | Ассистент ответил клиенту |

#### <code>ai.provider_created</code>: добавлен AI-провайдер

```json
{
  "event": "ai.provider_created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "providerId": "prov-550e8400-e29b-41d4-a716-446655440000",
    "type": "OPENAI",
    "name": "OpenAI Production"
  }
}
```

#### <code>ai.provider_removed</code>: удалён AI-провайдер

```json
{
  "event": "ai.provider_removed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "providerId": "prov-550e8400-e29b-41d4-a716-446655440000",
    "type": "OPENAI"
  }
}
```

#### <code>ai.instruction_created</code>: создана инструкция

```json
{
  "event": "ai.instruction_created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "instructionId": "instr-550e8400-e29b-41d4-a716-446655440000",
    "name": "Инструкция для поддержки",
    "mode": "SYSTEM"
  }
}
```

#### <code>ai.instruction_updated</code>: инструкция обновлена

```json
{
  "event": "ai.instruction_updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "instructionId": "instr-550e8400-e29b-41d4-a716-446655440000",
    "name": "Инструкция для поддержки v2"
  }
}
```

#### <code>ai.instruction_removed</code>: инструкция удалена

```json
{
  "event": "ai.instruction_removed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "instructionId": "instr-550e8400-e29b-41d4-a716-446655440000"
  }
}
```

#### <code>ai.assistant_created</code>: создан AI-ассистент

```json
{
  "event": "ai.assistant_created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "assistantId": "asst-550e8400-e29b-41d4-a716-446655440000",
    "name": "Бот поддержки",
    "type": "SUPPORT"
  }
}
```

#### <code>ai.assistant_updated</code>: ассистент обновлён

```json
{
  "event": "ai.assistant_updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "assistantId": "asst-550e8400-e29b-41d4-a716-446655440000",
    "name": "Бот поддержки v2"
  }
}
```

#### <code>ai.assistant_removed</code>: ассистент удалён

```json
{
  "event": "ai.assistant_removed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "assistantId": "asst-550e8400-e29b-41d4-a716-446655440000"
  }
}
```

#### <code>ai.assistant_toggled</code>: ассистент включён или выключен

```json
{
  "event": "ai.assistant_toggled",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "assistantId": "asst-550e8400-e29b-41d4-a716-446655440000",
    "isActive": true
  }
}
```

#### <code>ai.assistant_replied</code>: ассистент ответил

```json
{
  "event": "ai.assistant_replied",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "assistantId": "asst-550e8400-e29b-41d4-a716-446655440000",
    "dialogId": "dlg-123e4567-e89b-12d3-a456-426614174000",
    "assistantName": "Бот поддержки"
  }
}
```

### Платежи и баланс

| Событие | Описание |
|---|---|
| `payment.created` | Создан новый платёж |
| `payment.succeeded` | Платёж успешно проведён |
| `payment.refunded` | Платёж возвращён |
| `balance.changed` | Баланс изменился |
| `tier.changed` | Изменён тарифный план |

#### <code>payment.created</code>: создан платёж

```json
{
  "event": "payment.created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "paymentId": "pay-550e8400-e29b-41d4-a716-446655440000",
    "amount": 2990,
    "currency": "RUB"
  }
}
```

#### <code>payment.succeeded</code>: платёж проведён

```json
{
  "event": "payment.succeeded",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "paymentId": "pay-550e8400-e29b-41d4-a716-446655440000",
    "amount": 2990
  }
}
```

#### <code>payment.refunded</code>: платёж возвращён

```json
{
  "event": "payment.refunded",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "paymentId": "pay-550e8400-e29b-41d4-a716-446655440000",
    "amount": 2990
  }
}
```

#### <code>balance.changed</code>: баланс изменился

```json
{
  "event": "balance.changed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "amount": 15000,
    "change": -500,
    "source": "ai_tokens",
    "tier": "BUSINESS"
  }
}
```

#### <code>tier.changed</code>: тарифный план изменён

```json
{
  "event": "tier.changed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "oldTier": "FREE",
    "newTier": "BUSINESS",
    "billingPeriod": "monthly",
    "reason": "user_upgrade"
  }
}
```

Возможные значения `reason`: `user_upgrade`, `trial_expired`.

### Календарь

| Событие | Описание |
|---|---|
| `calendar.event_created` | Создано событие календаря |
| `calendar.event_updated` | Событие обновлено |
| `calendar.event_removed` | Событие удалено |

#### <code>calendar.event_created</code>: создано событие

```json
{
  "event": "calendar.event_created",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "eventId": "evt-550e8400-e29b-41d4-a716-446655440000",
    "title": "Созвон с клиентом",
    "provider": "GOOGLE_CALENDAR"
  }
}
```

#### <code>calendar.event_updated</code>: событие обновлено

```json
{
  "event": "calendar.event_updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "eventId": "evt-550e8400-e29b-41d4-a716-446655440000",
    "title": "Созвон с клиентом (перенесён)"
  }
}
```

#### <code>calendar.event_removed</code>: событие удалено

```json
{
  "event": "calendar.event_removed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "eventId": "evt-550e8400-e29b-41d4-a716-446655440000",
    "title": "Созвон с клиентом"
  }
}
```

### Интеграции

| Событие | Описание |
|---|---|
| `integration.connected` | Подключена интеграция |
| `integration.disconnected` | Интеграция отключена |
| `integration.updated` | Интеграция обновлена |

#### <code>integration.connected</code>: интеграция подключена

```json
{
  "event": "integration.connected",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "integrationId": "int-550e8400-e29b-41d4-a716-446655440000",
    "type": "AMOCRM",
    "name": "AmoCRM Production"
  }
}
```

#### <code>integration.disconnected</code>: интеграция отключена

```json
{
  "event": "integration.disconnected",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "integrationId": "int-550e8400-e29b-41d4-a716-446655440000",
    "type": "AMOCRM"
  }
}
```

#### <code>integration.updated</code>: интеграция обновлена

```json
{
  "event": "integration.updated",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "integrationId": "int-550e8400-e29b-41d4-a716-446655440000",
    "type": "AMOCRM",
    "name": "AmoCRM Production v2"
  }
}
```

### SMS

| Событие | Описание |
|---|---|
| `sms.sent` | SMS отправлено |
| `sms.status_changed` | Изменился статус SMS |
| `sms.device_connected` | Подключено SMS-устройство |
| `sms.device_disconnected` | SMS-устройство отключено |

#### <code>sms.sent</code>: SMS отправлено

```json
{
  "event": "sms.sent",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "requestId": "req-550e8400-e29b-41d4-a716-446655440000",
    "to": "+79991234567",
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678"
  }
}
```

#### <code>sms.status_changed</code>: статус SMS изменён

```json
{
  "event": "sms.status_changed",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "requestId": "req-550e8400-e29b-41d4-a716-446655440000",
    "status": "delivered",
    "to": "+79991234567",
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678"
  }
}
```

#### <code>sms.device_connected</code>: SMS-устройство подключено

```json
{
  "event": "sms.device_connected",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "deviceId": "dev-550e8400-e29b-41d4-a716-446655440000",
    "channelId": "ch-987fcdeb-51a2-3c4d-b5e6-789012345678",
    "name": "Samsung Galaxy A54"
  }
}
```

#### <code>sms.device_disconnected</code>: SMS-устройство отключено

```json
{
  "event": "sms.device_disconnected",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "deviceId": "dev-550e8400-e29b-41d4-a716-446655440000",
    "reason": "manual_removal"
  }
}
```

Возможные значения `reason`: `manual_removal` (удалено пользователем), `invalid_fcm_token` (невалидный FCM-токен, устройство отключено автоматически).

### Файлы

| Событие | Описание |
|---|---|
| `file.uploaded` | Загружен файл |
| `file.deleted` | Файл удалён |

#### <code>file.uploaded</code>: файл загружен

```json
{
  "event": "file.uploaded",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "fileId": "file-550e8400-e29b-41d4-a716-446655440000",
    "originalName": "document.pdf",
    "mimeType": "application/pdf",
    "size": 245760
  }
}
```

#### <code>file.deleted</code>: файл удалён

```json
{
  "event": "file.deleted",
  "timestamp": "2025-06-15T10:30:00.000Z",
  "data": {
    "fileId": "file-550e8400-e29b-41d4-a716-446655440000",
    "filename": "document.pdf"
  }
}
```

## Ограничения и доставка

| Параметр | Значение |
|---|---|
| Таймаут доставки | 10 секунд |
| Повторные попытки | Нет: fire-and-forget, одна попытка на событие |
| Автоотключение | После 50 последовательных ошибок вебхук деактивируется |
| Сброс счётчика ошибок | При успешной доставке счётчик обнуляется |
| Подпись | HMAC-SHA256 тела запроса, заголовок `X-Webhook-Signature`, hex |

> [!TIP]
> Отвечайте `200` сразу, а тяжёлую обработку выносите в очередь. Ответ дольше 10 секунд засчитывается как ошибка доставки, а 50 таких ошибок подряд выключат вебхук.

Чтобы вызывать платформу в обратную сторону (отправить сообщение, прочитать историю), используйте [REST API v1](https://docs.tuk-tuk.online/api/rest). Вебхуки внутри цепочек автоматизации описаны на странице [блока Webhook](https://docs.tuk-tuk.online/chains/blocks/webhook).
