Три роли
Роль 1
Посетитель
Клиент на сайте или в вашем продукте. Пишет через виджет Sapportly или через ваш интерфейс — тогда входящее кладёте вы.
Роль 2
Ваш сервер
API-ключ и Sapportly TypeScript SDK (@sapportly/sdk, не @supportly/sdk). Приём входящих пишет сообщение, ответ — исходящее в ленту канала. Доставка во внешний канал (почта, мессенджер, CRM) — ваша, если это не виджет.
Роль 3
Оператор
Человек из команды в app.sapportly.pro. Сессия JWT, не API-ключ. Кнопка «Ответить» во входящих.
Источник и тред
Ключ вида namespace:slug (custom:shop) — источник для реестра и аналитики. Один магазин = один источник. Диалог с человеком — custom:shop:{uuid}: его собирает приём входящих из identity.external_id (UUID v5) или из thread_id. Не создавайте custom:user-123 — аналитика по каналам тогда разъедется.
Исключение — виджет: в панели нужен чат 1:1, поэтому посетитель живёт в widget:{uuid}, а реестр виджета один — widget:web. Подробнее про каналы.
Что происходит после приёма входящих
- Ваш сервер вызывает
POST /v1/ingest/messages(виджет —POST /v1/widget/messages). - Шлюз проверяет ключ и кладёт событие в очередь. Ответ сразу
202,message_idещёnull— запись асинхронная. - Запись в хранилище пишет строку в Postgres. Живой канал пушит кадр
message.deliveredв открытые сокеты: панель и ваш SDK. - Параллельно AI может готовить черновик. Кадры
ai.draftуходят и в панель, и интегратору (частичные —partial: true). Это не строка истории. Если в панели включён автоответ, в ленту пишется обычное исходящее агента — тот же кадр, что у оператора.
Как ответить
Исходящее в ленту — не приём входящих. Тот же ключ канала. SDK ставит idempotency_key, если вы его не передали.
await client.conversations.reply(threadChannel, {
body: "Заказ отправлен, трек 123",
});Свой inbox.reply приходит по сокету с echo: true — повторно во внешний канал его слать не нужно. Ответ оператора из панели echo не несёт: если канал custom, доставку человеку делаете вы.
Три способа получать события
| Канал | Когда | Как |
|---|---|---|
| WebSocket | Нужен живой канал (бот, своя админка) | SapportlyInbox, право ws:connect. Гайд |
| Вебхуки | Можно подождать сотни мс | HMAC на ваш URL, проверка подписи |
| REST history | Реконнект, аудит | client.conversations.messages(channel) |
Один message_id может прийти и по WS, и по вебхуку. В SDK есть MessageDeduper — SapportlyInbox включает его по умолчанию.
От сообщения до результата
После сохранения обращение остаётся единым тредом, даже если его передали другому оператору, закрыли вручную или архивировали. Панель показывает историю назначений, а аналитика различает полученные, взятые, переданные и закрытые обращения. Архив — это фильтр рабочего списка, а не удаление данных.
Вложения идут тем же безопасным маршрутом: изображение отображается как изображение, а неподдерживаемый формат — как документ. Файл сначала загружается по короткоживущей ссылке, проходит проверки, и только после этого прикрепляется к сообщению. При включённом E2EE содержимое читают только участники диалога; ключи не попадают в аналитику и вебхуки.