Service Accounts — подключение и работа с API
Создайте сервисный аккаунт и подключите Chatterfy API: пошаговые запросы, права доступа, методы, фильтры, выгрузка, синхронизация и WebSocket.
Service Accounts — способ подключить внешнюю систему к Chatterfy Partners API. В Company → Service Accounts вы создаёте аккаунт интеграции, назначаете ему роль и получаете API-ключ. Затем добавляете аккаунт в нужные спейсы и выполняете запросы от его имени. Сервисный аккаунт определяет, кто обращается к API и какие данные ему доступны; ключ используется для аутентификации каждого запроса.
Это руководство описывает весь путь подключения: создание аккаунта → проверка доступа → выбор компании и бота → чтение чатов и сообщений → ответ клиенту. Далее приведены методы API, фильтры, выгрузка, синхронизация, структура объектов и обработка ошибок.
1. Создайте сервисный аккаунт и выдайте доступ
Создание в Company
Откройте Company → Service Accounts и нажмите Create account.

В поле Name укажите название аккаунта интеграции, а в Company role выберите роль. Например, чтение чатов и отправка сообщений — разные действия, поэтому роль должна содержать разрешения, необходимые вашей интеграции. Затем нажмите Create.

Сохранение ключа и примера запроса
Скопируйте полный ключ сразу после создания: он показывается только один раз. Позже в интерфейсе будут видны только последние четыре символа. Здесь же можно скопировать пример запроса и использовать его как основу для подключения.

Доступ к спейсам
Добавьте сервисный аккаунт в спейсы, к которым интеграция должна обращаться через API. Для работы с ботом и его чатами проверьте доступ к спейсу этого бота и разрешения назначенной роли. Сам по себе скопированный ключ не открывает все данные компании.
Права выдаются аккаунту в интерфейсе Chatterfy. API не создаёт пользователей, не меняет роли, не отправляет приглашения и не управляет биллингом. Он позволяет читать состав участников и их роли, например чтобы сопоставить идентификатор оператора с именем.
2. Передавайте ключ и проверяйте права
Адрес API и версии ресурсов
Базовый адрес:
api-url.txtplaintext
https://api.chatterfy.ai/public-apiВерсия указывается после имени ресурса: /chats/v1/search, /messages/v1/search, /bots/v1/{bot_id}. Ресурсы версионируются независимо: новая версия чатов не означает обязательного перехода на новую версию сообщений.
Заголовок ApiKey
Во всех запросах передавайте ключ в заголовке ApiKey. Authorization: Bearer для этого API не используется. Не передавайте ключ в query-строке: URL может сохраниться в журналах и истории браузера.
headers.httphttp
ApiKey: YOUR_API_KEYYOUR_API_KEY в примерах — обозначение вашего сохранённого ключа, а не рабочий ключ. Ключ имеет формат cfy_<окружение>_<uuid>_<секрет>. Регистр имени заголовка не важен.
У аккаунта один активный ключ. Выпуск нового мгновенно отзывает предыдущий, поэтому после замены обновите ключ во всех клиентах и процессах, которые его используют. Права привязаны к аккаунту, а не к строке ключа.
Разрешения и область доступа
У разрешений есть имена: chat.read, message.send, campaign.manage и так далее. Для каждого действия требуется соответствующее разрешение.
Право может быть выдано с более узкой областью действия:
Область действия | Что охватывает |
|---|---|
all | все записи в своей зоне |
own | только те, на которые аккаунт назначен: чаты, где он ответственный, кампании, где он байер |
own_unassigned | то же плюс записи, на которые не назначен никто |
assigned | решает бот: превращается в одно из трёх выше по его assign_type |
Если область действия разрешения ограничена, список содержит только доступные аккаунту записи. Запрос отдельной записи вне этой области возвращает 404.
Выяснять свои права перебором не нужно. Компании, спейсы и боты отдают карту access:
example.jsonjson
{
"id": "019bff49-0a87-7fd5-8cf4-64f4e3a5b328",
"name": "Acme",
"status": "active",
"access": { "chat.read": "own", "message.send": "all", "bot.read": "all" }
}Права, которого в карте нет, у вас нет. У компании карта — это объединение всего, что аккаунт может делать где-то внутри неё; у спейса и бота — точный ответ.
Поле assign_type бота уточняет режим assigned: all — назначенный ответственный не ограничивает видимость; assign_empty — доступны свои и неназначенные чаты; другие значения — только свои.
Для Tracker граница доступа — компания, а не спейс. Аккаунт-байер видит свои записи, руководитель команды баинга — также записи своих людей. Поэтому список доступных кампаний может отличаться от списка доступных ботов.
3. Выполните первые запросы по порядку
Все примеры ниже используют один и тот же ключ. YOUR_COMPANY_ID, YOUR_BOT_ID и YOUR_CHAT_ID заменяйте идентификаторами из предыдущих ответов. Не подставляйте названия объектов вместо их id.
Шаг 1. Получите доступные компании
Запросите компании, в которых состоит сервисный аккаунт:
request.shbash
curl -X POST 'https://api.chatterfy.ai/public-api/companies/v1/search' \
-H 'ApiKey: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{}'В массиве data выберите нужную компанию. Сохраните её id как YOUR_COMPANY_ID. В access можно посмотреть объединение прав аккаунта внутри компании; конкретные разрешения для спейса или бота уточняйте в access самого объекта.
Шаг 2. Получите ботов компании
request.shbash
curl -X POST 'https://api.chatterfy.ai/public-api/bots/v1/search' \
-H 'ApiKey: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"company_id":"YOUR_COMPANY_ID","limit":50}'Выберите бота из data и сохраните его id как YOUR_BOT_ID. Бот также возвращает space_id и access. Если нужного бота нет, проверьте спейс, роль и область доступа аккаунта. При необходимости получите спейсы через POST /spaces/v1/search с company_id.
Шаг 3. Получите чаты бота
request.shbash
curl -X POST 'https://api.chatterfy.ai/public-api/chats/v1/search' \
-H 'ApiKey: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"bot_id":"YOUR_BOT_ID","limit":50}'Выберите нужный чат из data и сохраните его id как YOUR_CHAT_ID. Поиск возвращает свежие диалоги сверху; для порядка используйте sort со значением asc или desc. Чат содержит данные контакта, статус, ответственного, теги и связанные данные лида, если они доступны аккаунту.
Шаг 4. Прочитайте сообщения чата
request.shbash
curl -X POST 'https://api.chatterfy.ai/public-api/messages/v1/search' \
-H 'ApiKey: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"chat_id":"YOUR_CHAT_ID","limit":50}'В data находятся сообщения и события диалога. Поле type различает message и event. Для сообщений направление задаётся sender_type: incoming — от клиента, outcoming — от вашей стороны. У поиска сообщений порядок задаётся полем order, а не sort.
Шаг 5. Ответьте клиенту
Следующий запрос отправляет реальное сообщение в выбранный чат. Выполняйте его, когда проверили chat_id и текст ответа. Для отправки требуется message.send.
request.shbash
curl -X POST 'https://api.chatterfy.ai/public-api/messages/v1/send' \
-H 'ApiKey: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"chat_id":"YOUR_CHAT_ID","content":"Hello! How can we help?"}'Передайте content, files или оба поля: пустое сообщение не принимается. Ответ отправки — объект сообщения; его status может быть waiting, ready, failed или schedule. Проверяйте статус и error, если отправка завершилась ошибкой. Идентификатор созданного сообщения сохраните для последующего чтения или редактирования.
Ключей идемпотентности пока нет. Если запрос завершился таймаутом, повтор может отправить сообщение дважды — сначала проверьте диалог.
4. Формат запросов и ответов
Коллекции читаются через POST /{ресурс}/v1/search с JSON-телом, а не через GET с query-строкой. В теле лежат сразу фильтр, страница и сортировка, и там же — родитель, которым ограничен поиск: bot_id для чатов, chat_id для сообщений, company_id для ботов, спейсов, кампаний, доменов и лендингов. Поиск умеет только сузить то, что вам и так доступно по правам, расширить он не может.
Под-ресурсы доступны по пути родительского объекта: например, теги бота, источники и параметры кампании. Списки таких под-ресурсов обычно читаются через GET с limit и cursor в query-строке. Состав участников компании и спейса запрашивается через POST /companies/v1/{company_id}/members/search и POST /spaces/v1/{space_id}/members/search. Метод для каждого вызова указан в таблицах ниже.
Тела PATCH частичные: присылайте только те поля, которые меняете. Пустой объект отклоняется с 400 no_fields, чтобы бессмысленное обновление не двигало таймстемпы и не порождало события. Неизвестные поля в теле игнорируются, поэтому новые необязательные поля вас не сломают.
Идентификаторы — UUID, кроме идентификаторов лидов и источников кампании: это короткие непрозрачные строки, и проверять их на формат UUID не надо. Время — RFC 3339 в UTC. Успешный ответ всегда 200, включая создание объектов. Коллекции возвращаются в конверте { "data": [...], "pagination": {...} }, одиночные объекты — как есть, без обёртки. Удаление отвечает {"id": "...", "deleted": true}.
Тело запроса ограничено 64 КБ, заголовки — 16 КБ.
5. Пагинация, выгрузка и синхронизация
Следующая страница
Любой поиск возвращает страницу и курсор:
example.jsonjson
{ "data": [], "pagination": { "has_more": true, "next_cursor": "b2Zmc2V0OjI1" } }Поле | Тип | Значение |
|---|---|---|
has_more | boolean | есть ли ещё страницы |
next_cursor | string | курсор следующей страницы; отсутствует на последней |
Скопируйте next_cursor в поле cursor следующего запроса, всё остальное оставьте без изменений, и остановитесь, когда has_more станет false. limit по умолчанию 25, максимум 100. Курсор непрозрачный: не разбирайте его и не собирайте сами.
Глубина поиска и меняющаяся выдача
Глубина обычного поиска ограничена 10 000 записями. При превышении границы API возвращает 400 cursor_out_of_range; учитывается сумма позиции курсора и limit, поэтому ошибка возможна уже на странице, которая пересекает границу. Это ограничение поискового индекса. Чтобы обработать больший объём, разбейте выборку по created_at и обходите интервалы отдельно либо используйте экспорт чатов.
Выдача меняется, пока вы её читаете. Чаты отсортированы по времени последнего сообщения, поэтому активный чат прыгает наверх и сдвигает остальные: один и тот же чат может прийти на двух страницах подряд, а перепрыгнувший через прочитанную границу — не прийти вовсе. Для большинства интеграций достаточно складывать результаты по id и пропускать повторы.
Полная выгрузка чатов
Для полной выгрузки используйте POST /chats/v1/export. Он читает замороженный снимок выборки, у него нет потолка в десять тысяч, и один чат в нём не встретится дважды и не потеряется, сколько бы сообщений ни приходило во время обхода. Тело то же, что у поиска: bot_id, filter, sort, limit, cursor. Сколько всего чатов попадёт в выгрузку, заранее скажет POST /chats/v1/stats с тем же фильтром.
Разница с поиском одна, но важная: курсор экспорта живёт пять минут. Снимок держится пять минут после каждой страницы; если сделать паузу дольше, следующий запрос ответит 400 cursor_expired, и выгрузку придётся начать сначала, без курсора. Поэтому листайте подряд, не откладывая страницы на потом. Экспорт требует отдельного права chat.export, и каждый его запуск записывается в журнал.
Регулярная синхронизация
Если вам нужна не разовая выгрузка, а регулярная синхронизация «что изменилось с прошлого раза», удобнее окно по updated_at в обычном поиске — сортируйте по возрастанию и двигайте нижнюю границу:
example.jsonjson
{
"bot_id": "019c61be-5ff7-70ca-8576-43950d877423",
"limit": 100,
"sort": "asc",
"filter": {
"field": "updated_at",
"operator": "between",
"value": ["2026-09-17T11:20:31Z", "2026-09-18T00:00:00Z"]
}
}Берите updated_at последнего полученного чата как нижнюю границу следующего запроса и храните последнюю метку между запусками. Изменившийся чат уезжает вперёд, в непрочитанную часть, поэтому вы можете встретить его дважды, но не перескочите через него. Именно between, а не after: after строгий, и два чата с одинаковой меткой на стыке страниц потеряли бы второго.
6. Фильтрация чатов
Один и тот же формат фильтра принимают POST /chats/v1/search, POST /chats/v1/stats и POST /chats/v1/realtime. Фильтр передаётся в поле filter тела запроса вместе с bot_id.
В примере YOUR_TAG_ID_1, YOUR_TAG_ID_2 и YOUR_OPERATOR_ID нужно заменить реальными UUID тегов и оператора.
Фильтр — это дерево. Узел бывает двух видов, смешивать их в одном объекте нельзя:
• условие — field и, для всех полей кроме query, operator и value;
• группа — filters со списком дочерних узлов и необязательный logic (and по умолчанию, либо or).
example.jsonjson
{
"logic": "and",
"filters": [
{ "field": "status", "operator": "is", "value": "manual" },
{ "field": "tags", "operator": "has_any", "value": ["YOUR_TAG_ID_1", "YOUR_TAG_ID_2"] },
{
"logic": "or",
"filters": [
{ "field": "assign_id", "operator": "is_empty" },
{ "field": "assign_id", "operator": "is", "value": "YOUR_OPERATOR_ID" }
]
}
]
}Дерево ограничено 64 узлами, глубиной 5 и сотней значений в одном условии. Неизвестное поле, неподдерживаемый для него оператор или значение не того типа отклоняются с 400, а не выполняются как попало — так опечатка не притворится честным пустым ответом.
Поля
Поле | Тип | Операторы | Смысл |
|---|---|---|---|
query | текст | оператор не нужен | подстрока по имени, username и внешнему идентификатору контакта; каждое слово должно найтись хотя бы в одном из трёх |
id | ключ | is, is_any, is_not | идентификатор чата |
external_id | ключ | is, is_any, is_not, is_not_empty | идентификатор контакта в мессенджере-источнике |
status | ключ | is, is_any, is_not | статус чата; кроме реальных значений принимает виртуальный ignore |
last_message_sender_type | ключ | is, is_any, is_not | кто написал последним: incoming или outcoming |
assign_id | ключ | is, is_any, is_not, is_empty, is_not_empty | ответственный оператор |
current_step_id | ключ | is, is_any, is_not, is_empty, is_not_empty | шаг сценария |
current_flow_id | ключ | is, is_any, is_not, is_empty, is_not_empty | сценарий, которому принадлежит шаг |
current_sale_stage_id | ключ | is, is_any, is_not, is_not_empty | стадия продажи |
tags | список ключей | has_any, has_all, has_only, has_none, is_not_empty | теги на чате |
tracker_events | список ключей | has_any, has_all, has_none, is_not_empty | типы событий лида, например purchase |
created_at | дата | before, after, between | когда чат создан |
updated_at | дата | before, after, between | время последнего сообщения; оно же ключ сортировки |
cost | число | more_number, less_number, more_equal_number, less_equal_number, equal_number, not_equal_number | деньги, приписанные лиду за этим чатом |
incoming_unread_count | число | те же, что у cost | непрочитанные сообщения от клиента; more_number со значением 0 оставит только непрочитанные |
custom_fields | вложенное | is, is_not, is_any | значение кастомного поля; требует key с UUID определения поля, а не его названия |
tracker_fields | вложенное | is, is_not, is_any | параметр трекера у лида, например utm_source; требует key с именем параметра |
Операторы
Оператор | Значение | Смысл |
|---|---|---|
is / is_not | скаляр | равно / не равно |
is_any | массив | равно любому из перечисленных |
is_empty / is_not_empty | не нужно | поле пустое / заполнено |
has_any | массив | список содержит хотя бы одно из значений |
has_all | массив | содержит все перечисленные, возможно и другие |
has_only | массив | содержит ровно эти и ничего сверх |
has_none | массив | не содержит ни одного из них |
before / after | дата | строго раньше / строго позже |
between | две даты | попадает в закрытый интервал |
more_number, less_number, more_equal_number, less_equal_number, equal_number, not_equal_number | число | обычные числовые сравнения |
Даты пишутся как RFC 3339 (2026-08-01T00:00:00Z) или как календарный день (2026-08-01). У условия с датой можно указать timezone с именем IANA — он влияет на то, как разворачивается календарный день:
example.jsonjson
{ "field": "created_at", "operator": "between", "value": ["2026-08-01", "2026-08-31"], "timezone": "Europe/Warsaw" }Примеры вложенных полей:
example.jsonjson
{ "field": "custom_fields", "key": "019c61be-5ff7-70ca-8576-43950d877423", "operator": "is", "value": "VIP" }
{ "field": "tracker_fields", "key": "utm_source", "operator": "is_any", "value": ["google", "bing"] }Важные оговорки
Поиск читает индекс, который наполняется событиями чатов. Чат, в котором ничего не происходило, может в нём отсутствовать, а изменение, сделанное секунду назад, — появиться с небольшой задержкой. Когда нужно достоверное состояние одного чата, читайте GET /chats/v1/{chat_id} — он идёт прямо в мессенджер.
Статус ignore виртуальный: он разворачивается в «статус manual и последнее сообщение входящее». Поэтому он пересекается со manual, и в total ответа статистики не входит.
Фильтр всегда применяется поверх ваших прав, а не вместо них. Если право chat.read выдано с ограниченной областью действия, к вашему условию молча добавляется ограничение по ответственному: фильтром можно сузить выборку, но не расширить.
7. Методы API по ресурсам
Префикс https://api.chatterfy.ai/public-api в таблицах опущен.
Боты
Метод и путь | Действие и параметры |
|---|---|
POST /bots/v1/search | боты одной компании; тело: company_id, status, type, query, limit, cursor |
GET /bots/v1/{bot_id} | один бот вместе с картой access |
PATCH /bots/v1/{bot_id} | name, description, status, timezone, image_url |
GET /bots/v1/{bot_id}/tags | справочник тегов бота |
POST /bots/v1/{bot_id}/tags | создать тег: name, color в формате #1A2B3C |
DELETE /bots/v1/{bot_id}/tags/{tag_id} | удалить тег и снять его со всех чатов |
GET /bots/v1/{bot_id}/custom-fields | определения кастомных полей |
query в поиске ботов — подстрока имени без учёта регистра.
Чаты
Метод и путь | Действие и параметры |
|---|---|
POST /chats/v1/search | тело: bot_id, filter, sort (asc/desc), limit, cursor |
POST /chats/v1/stats | тот же bot_id и filter, ответ — количества по статусам |
POST /chats/v1/export | полная выгрузка по фильтру: без потолка глубины, без дублей и пропусков; курсор живёт пять минут; право chat.export |
GET /chats/v1/{chat_id} | чат целиком |
PATCH /chats/v1/{chat_id} | status, assign_id, tags ({"assign": [...], "remove": [...]}), custom_fields |
DELETE /chats/v1/{chat_id} | удаляет чат вместе с сообщениями |
При изменении чата все идентификаторы проверяются на принадлежность его же боту: чужой тег, чужое кастомное поле или пользователь не из спейса бота отклоняются с 400.
Сообщения
Метод и путь | Действие и параметры |
|---|---|
POST /messages/v1/search | тело: chat_id, type (message/event), order (asc/desc), limit, cursor |
POST /messages/v1/send | тело: chat_id, content, files, reply_message_id, meta, schedule |
GET /messages/v1/{message_id} | одно сообщение |
PATCH /messages/v1/{message_id} | content |
DELETE /messages/v1/{message_id} | удалить |
В send нужно передать content, files или и то и другое — пустое сообщение отклоняется. Файлы передаются ссылкой ({"type": "photo", "url": "https://...", "meta": {}}), загрузку эндпоинт не принимает. schedule — время в будущем, когда сообщение должно уйти. reply_message_id должен указывать на сообщение из того же чата.
Постраничность сообщений построена на курсоре мессенджера, а не на смещении: в отличие от чатов, здесь курсор приходит от хранилища сообщений, а не кодирует позицию в выдаче.
Спейсы и компании
Метод и путь | Действие и параметры |
|---|---|
POST /spaces/v1/search | тело: company_id, limit, cursor |
POST /spaces/v1 | создать спейс: company_id, name, description; сервисный аккаунт становится владельцем |
GET /spaces/v1/{space_id} | спейс |
PATCH /spaces/v1/{space_id} | name, description |
DELETE /spaces/v1/{space_id} | отвечает 409 space_not_empty, пока в спейсе есть боты |
POST /spaces/v1/{space_id}/members/search | состав спейса |
POST /companies/v1/search | в каких компаниях состоит ключ |
GET /companies/v1/{company_id} | компания |
PATCH /companies/v1/{company_id} | только name |
POST /companies/v1/{company_id}/members/search | состав компании |
Списки участников доступны только на чтение. Без права на просмотр состава вместо отказа придёт только ваша собственная запись — это удобно, чтобы узнать, кто вы.
Аналитика
GET /analytics/v1/bots/{bot_id}/chats/new, /chats/active, /chats/summary, /operators/messages, /operators/chats, /operators/events, /operators/messages-per-chat, /operators/events-per-chat, /operators/reply-time.
Параметры в query-строке: from и to в RFC 3339 (обязательные, период не длиннее 366 дней), timezone (IANA, по умолчанию UTC), для рядов по операторам ещё group_by (day или hour) и operator_id, чтобы оставить одного оператора. Границы интервалов считаются по локальной полуночи в переданной таймзоне. chats/summary определяет свои периоды сам и принимает только timezone.
reply-time — медиана в секундах между входящим сообщением и человеческим ответом на него; диалог, на который так и не ответили, в медиану не попадает.
Трекер
Метод и путь | Действие и параметры |
|---|---|
POST /campaigns/v1/search | тело: company_id, status, target_type, query, bot_id, limit, cursor |
GET /campaigns/v1/{campaign_id} | кампания с источниками, параметрами, ботами и байерами |
PATCH /campaigns/v1/{campaign_id} | name, description, status |
GET /campaigns/v1/{campaign_id}/sources | источники кампании |
GET /campaigns/v1/{campaign_id}/params | параметры кампании |
POST /campaigns/v1/{campaign_id}/params | добавить параметр: name, key, placeholder |
DELETE /campaigns/v1/{campaign_id}/params/{param_id} | удалить параметр |
POST /domains/v1/search | тело: company_id, limit, cursor |
POST /domains/v1 | добавить домен: company_id, link |
GET /domains/v1/{domain_id} · PATCH /domains/v1/{domain_id} · DELETE /domains/v1/{domain_id} | домен; смена ссылки возвращает его в неготовое состояние |
POST /landings/v1/search | тело: company_id, limit, cursor |
GET /landings/v1/{landing_id} | лендинг |
GET /leads/v1/{lead_id} | лид с параметрами и событиями |
Граница арендатора в трекере — компания, а не спейс. Внутри компании аккаунт, назначенный байером, видит только свои записи, а тимлид байеров — ещё и записи своих людей.
Наружу никогда не выходят учётные данные и код: логины и пароли прокси, конфигурации интеграций, токены пикселей и идентификаторы конверсий, CSS и JS лендингов. Соответствующих полей нет ни в одном ответе. Создание кампаний тоже не открыто: рабочая кампания требует ссылок на сущности сценариев и мессенджера, принадлежность которых этот API проверить не может.
8. Что возвращает API
Поля, помеченные как необязательные, могут отсутствовать в ответе целиком — не рассчитывайте, что придёт null.
Бот
Поле | Тип | Описание |
|---|---|---|
id | uuid | идентификатор бота |
name | string | имя |
description | string | описание |
type | string | тип бота, например telegram bot |
space_id | uuid | спейс, которому принадлежит бот |
username | string | username в мессенджере-источнике |
status | string | active или stopped |
tags | array<uuid> | идентификаторы тегов бота |
image_url | string | аватар |
timezone | string | IANA-таймзона бота |
assign_type | string | режим назначения: all — ответственный не ограничивает видимость, assign_empty — видны свои и ничьи, иное — только свои |
created_at | date-time | когда создан |
activity_at | date-time | последняя активность |
access | object | карта право → широта для этого бота |
Чат
Поле | Тип | Описание |
|---|---|---|
id | uuid | идентификатор чата |
bot_id | uuid | бот, которому принадлежит чат |
name | string | имя контакта; урезано без права chat.contacts.read |
username | string | username контакта; пустой без права chat.contacts.read |
external_id | string | идентификатор контакта в мессенджере-источнике; урезан без права chat.contacts.read |
image_url | string | аватар контакта |
status | string | manual, ai, blocked, start; на запись принимаются ещё ignore и stopped, а в фильтрах ignore виртуальный |
assign_id | uuid, опц. | ответственный оператор |
tags | array<uuid> | теги на чате |
custom_fields | array<объект> | значения кастомных полей: id, key (uuid определения), value |
current_step_id | uuid, опц. | шаг сценария, на котором стоит чат |
sale_stage_id | uuid, опц. | стадия продажи; только на чтение |
last_message_id | uuid, опц. | идентификатор последнего сообщения |
last_message | объект, опц. | само последнее сообщение целиком |
read_state | объект, опц. | состояние прочтения, см. ниже |
lead_id | string, опц. | лид, если чат пришёл с размеченного трафика |
lead | объект, опц. | сам лид целиком |
created_at | date-time | когда создан |
read_state содержит incoming_read_message_id, incoming_unread_count, outcoming_read_message_id, outcoming_unread_count. Входящие — это сообщения от клиента, которые никто с нашей стороны не прочитал.
Сообщение
Поле | Тип | Описание |
|---|---|---|
id | uuid | идентификатор |
chat_id | uuid | чат |
type | string | message — обычное сообщение, event — системное событие вроде смены тега или статуса |
status | string | waiting, ready, failed, schedule |
content | string | текст |
sender_type | string | incoming — от клиента, outcoming — от нас |
sender | uuid, опц. | оператор-отправитель; пусто, если отправил бот или это входящее |
external_id | string, опц. | идентификатор сообщения в мессенджере-источнике |
error | string, опц. | причина, если status равен failed |
pinned | boolean | закреплено ли |
read | boolean | прочитано ли |
is_mailing | boolean | отправлено ли рассылкой |
reply_message_id | uuid, опц. | сообщение, на которое отвечают |
meta | object, опц. | произвольные данные, которые вы приложили при отправке |
files | array<объект> | вложения: id, type, url, status, meta, created_at |
created_at | date-time | когда создано |
published_at | date-time | когда ушло в мессенджер |
edited_at | date-time, опц. | когда отредактировано |
Типы вложений: photo, video, audio, voice, document, sticker, animation.
Лид
Поле | Тип | Описание |
|---|---|---|
id | string | короткий непрозрачный идентификатор, не UUID |
company_id | uuid | компания |
campaign_id | string, опц. | кампания, которая привела лида |
source_id | string, опц. | источник внутри кампании |
type | string | тип лида |
provider_type | string, опц. | рекламная система |
provider_click_id | string, опц. | идентификатор клика на стороне рекламной системы |
params | object | параметры перехода: ip, useragent, UTM-метки и прочие параметры ссылки |
events | array<объект> | события лида: event (например purchase) и cost с полями cost и currency |
created_at | date-time | когда создан |
Спейс, компания, участники
Объект | Поля |
|---|---|
Спейс | id, company_id, name, description, access |
Компания | id, name, status, access |
Участник спейса | id, first_name, last_name, email, avatar_url, role_id, role_name, joined_at |
Участник компании | то же плюс account_type, где service означает сервисный аккаунт |
Теги и кастомные поля бота
Объект | Поля |
|---|---|
Тег | id, bot_id, name, color, created_at |
Кастомное поле | id, bot_id, key, name, type, options, index, visibility, created_at |
key кастомного поля — это UUID его определения; именно он используется и в значениях на чате, и в фильтре custom_fields.
Трекер
Объект | Поля |
|---|---|
Кампания | id, company_id, name, description, status, target_type, domain_id, landing_id, created_at, bots, buyers, params, sources |
Бот кампании | bot_id, type, start_step_id |
Источник | id, name, provider, invite, created_at |
Параметр | id, campaign_id, key, name, placeholder, created_at |
Байер | buyer_id, role, team_lead_id |
Домен | id, company_id, link, status, buyers, created_at |
Лендинг | id, company_id, name, title, description, type, avatar_url, download_text, open_text, members_text, members_count, buyers, created_at |
target_type кампании — telegram bot, telegram channel или organic. Домен становится пригодным к работе, когда его DNS направлен на платформу; до этого он в статусе неготовности. key параметра кампании — это имя параметра в ссылке, которое затем оседает в params лида.
Аналитика
Ряды по чатам возвращают points с полями date и value. Ряды по операторам — points с date, operator_id и value. Сводка по новым чатам возвращает шесть чисел: today, yesterday, week, last_week, month, last_month.
Статистика чатов по статусам: statuses — массив пар status/count, и total. Виртуальный статус ignore в total не входит, потому что он пересекается с manual.
Подписка на обновления
channel, ws_url, connection_token, expires_in_seconds, а для подписки на один чат ещё и subscription_token.
9. Обновления в реальном времени
Вместо опроса активного бота в цикле подпишитесь на канал.
POST /chats/v1/realtime — лента всех чатов бота, подходящих под фильтр. Тело то же, что у поиска: bot_id и filter. События содержат один чат и вид изменения: bump, upsert, remove или stale.
POST /chats/v1/{chat_id}/realtime — подробности одного диалога, включая то, что не доходит до списка: например, смену статуса доставки сообщения. События приходят с видом event и полем payload_type, которое называет изменение. Этот канал общий для всех наблюдателей, поэтому в ответе есть дополнительный subscription_token, который надо предъявить при подписке.
Оба эндпоинта возвращают координаты канала, а не сами события: подключитесь по ws_url с connection_token и подпишитесь на channel. Подписка живёт expires_in_seconds секунд и продлевается повторным вызовом того же эндпоинта с тем же телом.
Если realtime в окружении не настроен, оба эндпоинта отвечают 503 realtime_unavailable, а всё остальное продолжает работать.
Порядок подключения
1. Вызовите POST /chats/v1/realtime с bot_id и нужным filter либо POST /chats/v1/{chat_id}/realtime для одного диалога. Сам HTTP-запрос аутентифицируется заголовком ApiKey.
2. Из ответа возьмите ws_url, connection_token, channel и expires_in_seconds. Для одного диалога также сохраните subscription_token.
3. Подключитесь к ws_url с connection_token и оформите подписку на channel; для одного диалога передайте subscription_token при подписке.
4. Обрабатывайте события нужного канала. Для продления повторите исходный HTTP-запрос с тем же телом и используйте обновлённые параметры подписки.
Не подставляйте API-ключ вместо connection_token или subscription_token: это разные значения с разным назначением.
10. Ошибки и ограничения частоты
У всех ошибок одинаковая структура ответа:
example.jsonjson
{
"error": {
"type": "invalid_request_error",
"code": "invalid_limit",
"message": "limit must not exceed 100",
"request_id": "req_19b1e1109b0d37e463f571b01c2b5eba"
}
}type — одно из invalid_request_error, authentication_error, permission_error, not_found_error, conflict_error, rate_limit_error, api_error. code — код причины для программной обработки, message — текстовое пояснение, которое можно показать в логе, но не стоит парсить.
Код | Когда |
|---|---|
400 | запрос неправильной формы: битый UUID, недопустимое значение перечисления, пустой PATCH, сломанный курсор, невалидный фильтр, тело больше 64 КБ |
401 | ключ отсутствует, испорчен или отозван |
403 | у аккаунта нет права, которое требует этот вызов |
404 | объекта не существует, он принадлежит другой компании либо не попадает под широту вашего права |
409 | состояние не позволяет: например, удаление спейса, в котором остались боты |
429 | лимит запросов исчерпан, смотрите Retry-After |
503 | недоступна зависимость или не настроен realtime |
Частые значения code: missing_api_key, invalid_api_key, access_denied, invalid_limit, invalid_cursor, cursor_out_of_range, cursor_expired, no_fields, empty_body, body_too_large, validation_failed, invalid_filter, rate_limit_exceeded, realtime_unavailable, service_unavailable, а также <объект>_not_found вида chat_not_found или campaign_not_found.
Обращение к чужой компании отвечает 404, а не 403: 403 подтвердил бы, что такой идентификатор где-то существует.
В каждом ответе, успешном и нет, приходит заголовок X-Request-Id, а при ошибке он же дублируется в поле request_id конверта. Указывайте его в обращении в поддержку — так вызов находится в логах быстрее всего.
Лимит запросов
Ключу по умолчанию разрешено 120 запросов в минуту, считается скользящим окном.
Заголовок | Значение |
|---|---|
X-RateLimit-Limit | разрешено запросов в окне |
X-RateLimit-Remaining | сколько осталось |
X-RateLimit-Reset | unix-время конца текущего окна |
Retry-After | только при 429, секунды до следующей попытки |
Лимит общий для всех эндпоинтов и процессов, использующих этот ключ. При ответе 429 дождитесь указанного в Retry-After времени, прежде чем повторять запрос.
11. Ограничения текущей версии
HTTP-вебхуки пока не поддерживаются: для живых обновлений используются WebSocket-каналы. Нет ключей идемпотентности — повтор отправки после таймаута может создать дубликат сообщения. Нет тестового окружения и эндпоинта загрузки файлов: вложения при отправке передаются ссылками.
API не создаёт пользователей, не выдаёт роли, не отправляет приглашения и не управляет биллингом. При этом методы изменения названия компании, создания и изменения спейсов описаны выше: ограничения на управление людьми не означают, что все ресурсы доступны только на чтение.
Создание кампаний через API пока не открыто. Ответы Tracker не содержат логины и пароли прокси, конфигурации интеграций, токены пикселей и идентификаторы конверсий, CSS и JS лендингов.
Частые вопросы
Аккаунт создан, но нужные данные не видны
Проверьте компанию, доступ аккаунта к спейсу нужного бота и карту access этого бота. Разрешение может быть ограничено своими или неназначенными записями. Параметры поиска не расширяют права. В Tracker учитываются доступ к компании и назначения байером или руководителем команды.
В поиске нет чата, который есть в интерфейсе
Поиск работает по индексу событий: неактивный чат может отсутствовать, а свежее изменение — появиться с задержкой. Для достоверного состояния известного чата используйте GET /chats/v1/{chat_id}.
Нужна вся база чатов без пропусков
Используйте POST /chats/v1/export с разрешением chat.export. Обычная пагинация поиска работает с меняющейся выдачей и ограничена глубиной 10 000 записей. Страницы экспорта читайте подряд: пауза больше пяти минут делает курсор недействительным.
Полный ключ потерян
Повторно посмотреть его нельзя. Выпустите новый ключ в интерфейсе и замените его в интеграции. Предыдущий ключ отзывается сразу.
Эта статья была полезна?

