Интеграция собственного чата через Client API
Client API позволяет подключить бота Chatterfy к собственному сайту или приложению и построить полностью свой интерфейс чата. API рассчитан на работу непосредственно из браузера: фронтенд использует п
Когда использовать Client API. Выбирайте этот вариант, если готовый виджет не подходит по интерфейсу или логике и вы хотите самостоятельно управлять чатами, сообщениями, файлами и отображением событий в реальном времени.
Создание API (Web)-бота
Откройте создание нового бота и пройдите мастер настройки. Основные этапы одинаковы для виджета и API (Web); отличается только выбранная платформа.
- На экране выбора платформы отметьте API (Web) и нажмите кнопку продолжения.

Выбор API (Web) при создании бота. - Укажите пространство, тему бота и при необходимости добавьте описание и теги.

Общие параметры создаваемого бота. - Задайте понятное имя. Его можно будет изменить позднее в настройках бота.

Имя бота в мастере создания. - Можно импортировать настройки существующего бота или настроить новый бот вручную.

Импорт настроек или ручная конфигурация. - Укажите задержку ответа и при необходимости создайте кастомные поля.

Задержка ответа и кастомные поля. - Выберите модели для анализа сообщений и формирования ответов.

Модели для маршрутизации и ответов бота. - Опишите роль бота, запрещённые действия и требования к оформлению ответов, затем завершите создание.

Global Legend, Global Restrictions и User Restrictions.
Основные адреса и формат
Базовый URL: https://messenger-api.chatterfy.ai
Префикс методов: /api/client
Формат: JSON; загрузка файлов использует multipart/form-data.
Модель доступа
Где взять API-ключ
После создания бота откройте Bot Settings → API → Settings. На странице отображается публичный ключ, который используется для подключения через Web API.

client_key — публичный ключ бота вида cli_..., доступный в настройках бота.session_token — JWT конкретного пользователя. По умолчанию действует 24 часа и используется во всех запросах после создания сессии.external_id — ваш идентификатор пользователя. Если его не передать, сервер создаст visitor_<uuid>.Если в настройках бота заполнен allowed_origins, запрос создания сессии будет разрешён только с указанных доменов. Для остальных Origin сервер вернёт 403.
Общий поток работы
- POST /api/client/session возвращает session_token, пользователя и параметры realtime.
- Используйте данные Centrifugo из ответа сессии, если блок realtime присутствует.
- Запросите GET /chats. Если список пуст, создайте чат через POST /chats.
- Получите сообщения через GET /chats/{chat_id}/messages и отсортируйте их по created_at.
- POST /chats/{chat_id}/messages создаёт сообщение пользователя. Ответ бота или оператора приходит через realtime.
Все методы, кроме создания сессии, требуют заголовок:
http
Authorization: Bearer <session_token>1. Создание сессии
POST /api/client/session — находит или создаёт пользователя по external_id и возвращает токен сессии.json
{
"client_key": "cli_019f8433...",
"external_id": "user-42",
"first_name": "Alex",
"last_name": "Taylor"
}Обязателен только
client_key. Имя, фамилия и external_id можно не передавать.json
{
"session_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": "2026-07-25T12:00:00Z",
"user": {
"id": "019be668-9658-7a8d-b0d5-c73aa98e73d2",
"external_id": "user-42",
"bot_id": "019be6...",
"first_name": "Alex",
"last_name": "Taylor",
"image_url": null,
"status": "active"
},
"realtime": {
"provider": "centrifugo",
"url": "wss://realtime.chatterfy.ai/connection/websocket",
"token": "eyJhbGciOiJIUzI1NiIs...",
"channel": "nexus:user#019be668-9658-7a8d-b0d5-c73aa98e73d2"
},
"widget": {
"primary_color": "#4f46e5",
"multiple_chats": false,
"attachments": true
}
}Сохраните user.external_id в localStorage и передавайте его при следующих посещениях.
Блок realtime отсутствует, если realtime отключён для окружения. В этом случае можно работать только через HTTP.
Блок widget содержит настройки оформления и поведения бота. Собственный интерфейс может их использовать или игнорировать.
2. Работа с чатами
Получить список
GET /api/client/chats возвращает чаты текущего пользователя. Поле unread_msg_by_user удобно использовать для бейджа непрочитанных сообщений.Создать чат
POST /api/client/chats создаёт новый чат. Поле name обязательно.json
{
"name": "support-chat",
"additional_info": {
"page": "/pricing"
},
"image_url": null
}Для режима одного чата при старте вызовите GET /chats. Если массив пуст, создайте чат и дальше работайте только с ним.
3. Сообщения
Получить историю
GET /api/client/chats/{chat_id}/messages возвращает массив сообщений. Перед отображением отсортируйте его по created_at.json
{
"id": "3946090380478382140",
"content": "example-message",
"content_type": "text",
"sender_type": "outcoming",
"chat_id": "019bef...",
"sender": "operator",
"files": null,
"is_edited": false,
"created_at": "2026-07-24T10:05:00Z"
}incoming — сообщение от пользователя, отправленное через Client API.outcoming — сообщение от бота или оператора.Отправить сообщение
POST /api/client/chats/{chat_id}/messages создаёт сообщение пользователя.
json
{
"content": "example-message",
"content_type": "text",
"files": [],
"complete_files": []
}Для complete_files можно передать файл по прямой ссылке:
[{ "url": "...", "type": "photo" }]. Ответ — 201 Created.Отметить чат прочитанным
POST /api/client/chats/{chat_id}/read обнуляет счётчик непрочитанных и возвращает { "status": "ok" }.
4. Загрузка файлов
Файл отправляется в два этапа: сначала загрузите его, затем добавьте полученный ID в сообщение.
- Отправьте POST /api/client/files в формате multipart/form-data. Поле file обязательно, type — photo, video, audio, voice или document.
- Ответ 201 Created содержит id, type, url, status и метаданные файла.
- json
{ "content": "", "content_type": "photo", "files": ["019bef-file-id"] }
5. События в реальном времени через Centrifugo
Ответы бота, сообщения оператора и события прочтения приходят по WebSocket. Используйте url, token и channel из блока realtime ответа /session.
javascript
import { Centrifuge } from 'centrifuge';
const { url, token, channel } = session.realtime;
const centrifuge = new Centrifuge(url, { token });
const sub = centrifuge.newSubscription(channel);
sub.on('publication', (ctx) => {
const { type, data } = ctx.data;
if (type === 'message') {
renderMessage(data);
} else if (type === 'action') {
handleAction(data);
}
});
sub.subscribe();
centrifuge.connect();message — новое сообщение: { type: "message", data: <message> }.action — действие, например read: { type: "action", data: { action: "read", chat_id: "..." } }.В канал приходят и сообщения, отправленные текущим пользователем. Делайте дедупликацию по message.id, чтобы не показать сообщение дважды — из HTTP-ответа и realtime.
6. Сохранение пользователя между посещениями
- При старте возьмите сохранённый ID из localStorage.
- Передайте external_id в POST /session. Если ID ещё нет, не добавляйте поле.
- Запишите user.external_id из ответа обратно в localStorage.
Так посетитель будет возвращаться в свою историю чатов. Уникальность external_id обеспечивает ваша система.
Коды ошибок
400 — некорректное тело запроса, chat_id или загрузка без файла.
401 — отсутствует или недействителен session_token.
403 — бот выключен, Client API отключён, Origin не разрешён или чат не принадлежит сессии.
404 — бот или чат не найден.
500 — внутренняя ошибка сервера.
Тело ошибки имеет формат
{ "error": "error-description" }.Полный минимальный пример
javascript
const BASE = 'https://messenger-api.chatterfy.ai/api/client';
const CLIENT_KEY = 'cli_019f8433...';
const LS_KEY = `chatterfy:${CLIENT_KEY}:external_id`;
async function startSession() {
const externalId = localStorage.getItem(LS_KEY) || undefined;
const res = await fetch(`${BASE}/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_key: CLIENT_KEY,
external_id: externalId,
}),
});
if (!res.ok) throw new Error(`session failed: ${res.status}`);
const session = await res.json();
localStorage.setItem(LS_KEY, session.user.external_id);
return session;
}
function authHeaders(token) {
return {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
};
}
async function ensureChat(token) {
const chats = await fetch(`${BASE}/chats`, {
headers: authHeaders(token),
}).then((response) => response.json());
if (chats.length) return chats[0];
return fetch(`${BASE}/chats`, {
method: 'POST',
headers: authHeaders(token),
body: JSON.stringify({ name: 'support-chat' }),
}).then((response) => response.json());
}
async function sendMessage(token, chatId, text) {
return fetch(`${BASE}/chats/${chatId}/messages`, {
method: 'POST',
headers: authHeaders(token),
body: JSON.stringify({ content: text, content_type: 'text' }),
}).then((response) => response.json());
}
const session = await startSession();
const chat = await ensureChat(session.session_token);
const history = await fetch(`${BASE}/chats/${chat.id}/messages`, {
headers: authHeaders(session.session_token),
}).then((response) => response.json());
await sendMessage(session.session_token, chat.id, 'example-message');После запуска HTTP-части добавьте подписку на session.realtime, чтобы ответы бота и оператора появлялись в интерфейсе без опроса истории.
Эта статья была полезна?

