Webhook

Webhook - это HTTP POST, который система отправляет на ваш URL при наступлении события.

Успешной доставкой считается любой ответ со статусом 200-299.

Формат payload

Тело запроса всегда JSON:

JSON
{
  "type": "create_message",
  "data": {}
}

Мы не добавляем служебные поля в тело запроса: в payload находятся только type и data.

Подпись (secret)

Если указан secret, добавляется заголовок X-Webhook-Signature.

TEXT
X-Webhook-Signature: 2a1b... (hex hmac-sha256)

Пример проверки подписи (Node.js):

TS
import { createHmac, timingSafeEqual } from 'crypto';

function verifySignature(bodyRaw: string, signature: string, secret: string) {
  const expected = createHmac('sha256', secret).update(bodyRaw).digest('hex');
  return timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
}

Повторные попытки

  • Повторяются только неуспешные доставки.
  • 5 попыток с экспоненциальной задержкой: 1m, 2m, 4m, 8m, 16m.

Список событий

  • Messages: create_message, edit_message, delete_message
  • Deals: create_deal, move_deal, close_deal, delete_deal
  • Pipelines: create_pipeline, update_pipeline, delete_pipeline
  • CRM Tags: create_crm_tag, update_crm_tag, delete_crm_tag
  • Clients: create_client, update_client
  • Chats: create_chat, close_chat, transfer_chat, open_chat

Базовая типизация

TS
type WebhookEventType =
  | 'create_message'
  | 'edit_message'
  | 'delete_message'
  | 'create_chat'
  | 'close_chat'
  | 'open_chat'
  | 'transfer_chat'
  | 'create_deal'
  | 'move_deal'
  | 'close_deal'
  | 'delete_deal'
  | 'create_pipeline'
  | 'update_pipeline'
  | 'delete_pipeline'
  | 'create_crm_tag'
  | 'update_crm_tag'
  | 'delete_crm_tag'
  | 'create_client'
  | 'update_client';

type WebhookEnvelope<T> = { type: WebhookEventType; data: T };

Типы data для событий

create_message

TS
type CreateMessageData = {
  id: string;
  chatUuid: string;
  content: string;
  type: 'system' | 'default';
  senderType: 'client' | 'operator';
  senderUuid: string;
  isUpdated: boolean;
  isSpam: boolean;
  isEmailSent: boolean;
  answerMessageUuid: string | null;
  socialMessageUuid: string | null;
  aiOperatorMeta: any | null;
  createdAt: string;
  updatedAt: string;
};

edit_message

TS
type EditMessageData = CreateMessageData;

delete_message

TS
type DeleteMessageData = CreateMessageData & { deletedAt: string };

create_chat

TS
type CreateChatData = {
  id: string;
  clientUuid: string;
  operatorUuid: string;
  channelUuid: string;
  departmentUuid: string | null;
  transfersUuid: string[];
  status: 'active' | 'closed';
  subject?: string | null;
  createdAt: string;
  updatedAt: string;
};

close_chat

TS
type CloseChatData = CreateChatData & { closedAt: string };

open_chat

TS
type OpenChatData = CreateChatData & { openedAt: string };

transfer_chat

TS
type TransferChatData = CreateChatData & {
  previousOperatorUuid: string;
  newOperatorUuid: string;
  transferredAt: string;
};

create_deal

TS
type CreateDealData = {
  id: string;
  title: string;
  description: string | null;
  amount: number;
  currency: string;
  status: 'active' | 'won' | 'lost' | 'paused';
  priority: 'low' | 'medium' | 'high' | 'urgent';
  source: string;
  expectedCloseDate: string | null;
  actualCloseDate: string | null;
  lossReason: string | null;
  customFields: object | null;
  clientUuid: string | null;
  ownerUuid: string;
  pipelineUuid: string;
  stageUuid: string;
  lostReasonCategoryUuid: string | null;
  chatUuid: string | null;
  rootChanelUuid: string;
  createdAt: string;
  updatedAt: string;
};

delete_deal

TS
type DeleteDealData = CreateDealData & { deletedAt: string };

move_deal

TS
type MoveDealData = CreateDealData & {
  previousStageUuid: string;
  newStageUuid: string;
  movedAt: string;
};

close_deal

TS
type CloseDealData = CreateDealData & { closedAt: string };

create_pipeline

TS
type CreatePipelineData = {
  id: string;
  name: string;
  description: string | null;
  color: string;
  isActive: boolean;
  isDefault: boolean;
  sortOrder: number;
  rootChanelUuid: string;
  createdAt: string;
  updatedAt: string;
};

update_pipeline

TS
type UpdatePipelineData = CreatePipelineData;

delete_pipeline

TS
type DeletePipelineData = CreatePipelineData & { deletedAt: string };

create_crm_tag

TS
type CreateCrmTagData = {
  id: string;
  name: string;
  color: string;
  description: string | null;
  type: 'client' | 'deal' | 'universal';
  isActive: boolean;
  usageCount: number;
  rootChanelUuid: string;
  createdAt: string;
  updatedAt: string;
};

update_crm_tag

TS
type UpdateCrmTagData = CreateCrmTagData;

delete_crm_tag

TS
type DeleteCrmTagData = CreateCrmTagData & { deletedAt: string };

create_client

TS
type CreateClientData = {
  id: string;
  type: 'site' | 'telegram' | 'mail' | 'whatsapp' | 'vk' | 'max';
  rootChanelUuid: string;
  socialId: number | null;
  nameSocial: string | null;
  lastName: string | null;
  name: string | null;
  email: string[];
  avatarUrl: string | null;
  phone: string[];
  ip: string[];
  city: string[];
  isOnline: boolean;
  isContactRequestPending: boolean;
  otherFields: object | null;
  lastVisit: string;
  createdAt: string;
  updatedAt: string;
};

update_client

TS
type UpdateClientData = CreateClientData;

Поля в data могут расширяться обратно-совместимо. Не завязывайтесь на строго фиксированный набор полей без необходимости.