API-токены
API-токен связывает внешнее приложение с одной организацией APX Chat. Стабильный
applicationId определяет tenant, пространство externalId, rate limits и webhook-проекцию;
сменяемый credentialId относится к конкретному секрету.
Где взять токен: Настройки → API-токены в кабинете. Нажмите «Создать токен», выберите нужные scopes, срок действия и, при необходимости, список разрешённых IP. Секрет показывается один раз при создании — сохраните его сразу, повторно посмотреть его нельзя.
Для публичных CRM API, AI Agents API и Channels API передавайте токен ровно одним способом:
X-Api-Token: tok_xxxxxxxxxxxxx
Authorization: Bearer tok_xxxxxxxxxxxxxX-Api-Key поддерживается только для старых интеграций. Не помещайте секрет в query string, URL,
клиентский JavaScript или мобильное приложение.
Управление приложениями и credentials
Создавать и изменять токены могут Creator и Admin в разделе
Настройки → API-токены. То же самое доступно через
management-маршруты ниже: они требуют пользовательский JWT и не принимают управляемый API-токен
вместо него.
Пути ниже указаны относительно https://api.apx.chat/api:
| Метод и путь | Назначение |
|---|---|
GET /api-tokens | Список приложений и метаданных их текущих credentials |
POST /api-tokens | Создать приложение и выпустить первый credential |
PATCH /api-tokens/:id | Изменить приложение, CIDR allowlist или срок credentials |
POST /api-tokens/:id/rotate | Выпустить новый credential; тело не требуется |
DELETE /api-tokens/:id | Отозвать приложение и все его credentials |
:id принимает стабильный applicationId; для обратной совместимости также принимается UUID
ранее созданного credential. В ответе поле id пока остаётся UUID отображаемого credential,
поэтому новый код должен сохранять явные applicationId и credentialId.
Пример создания:
{
"name": "Синхронизация с ERP",
"description": "Контакты, компании и сделки",
"scopes": ["crm_contacts_read", "crm_companies_read", "crm_deals_read"],
"expiresInDays": 90,
"allowedCidrs": ["203.0.113.10/32", "2001:db8:1234::/48"]
}name содержит 2–150 символов, description — до 500, а scopes не может быть пустым. Полный
token возвращается только в ответе на create или rotate. После закрытия окна его нельзя получить
снова: сервер хранит SHA-256 hash, prefix и suffix, но не исходный секрет.
PATCH принимает только изменяемые поля:
{
"name": "ERP production",
"description": null,
"scopes": ["crm_contacts_read", "crm_deals_read", "crm_changes_read"],
"expiresInDays": 60,
"allowedCidrs": ["203.0.113.0/24"]
}TTL и истечение
expiresInDaysзадаётся целым числом от 1 до 365.- Если есть хотя бы один CRM scope и срок не указан, новый credential получает TTL 90 дней.
- CRM credential не может быть бессрочным.
- Для приложения без CRM scopes срок можно не задавать; при PATCH его можно очистить значением
null. - Изменение
expiresInDaysпересчитываетexpiresAtтекущего активного credential от момента обновления.
В ответах expiresAt содержит фактический ISO 8601 timestamp, а expiresInDays — политику для
активного и следующих credentials. Планируйте ротацию до expiresAt; истёкший credential даёт тот
же 401 invalid_token, что и неверный секрет.
CIDR allowlist
allowedCidrs принимает до 50 IPv4/IPv6 сетей в CIDR-формате. Для одного адреса используйте /32
для IPv4 или /128 для IPv6. Пустой список разрешает запросы с любого IP.
Запрос вне allowlist отклоняется как 401 invalid_token, чтобы не раскрывать существование секрета.
Перед включением ограничения внесите все NAT/egress IP production-инстансов и резервных workers.
Изменение списка применяется к приложению и сразу влияет на текущий и grace credentials.
Ротация без простоя
POST /api-tokens/:id/rotate возвращает новый token один раз. applicationId, scopes, CIDR
allowlist и namespace externalId не меняются. Предыдущий credential принимается до
rotationGraceUntil — не более 24 часов и никогда дольше его исходного expiresAt.
Безопасная последовательность:
- Выпустите новый credential и атомарно сохраните секрет в secret manager.
- Обновите все экземпляры интеграции.
- Выполните проверочный запрос именно с новым секретом и убедитесь, что он работает.
- Дождитесь окончания grace period или отзовите приложение при компрометации.
lastUsedAt обновляется coalesced, не чаще одного раза примерно в 5 минут, поэтому это не
поминутный журнал запросов и не мгновенный health check. Повторная ротация выпускает ещё один
секрет; она не восстанавливает потерянное значение предыдущего ответа.
DELETE отзывает приложение и все его credentials немедленно, включая ключи в grace period.
CRM scopes: основные ресурсы
| Scope | Доступ в публичном CRM API v1 |
|---|---|
crm_contacts_read | Читать контакты |
crm_contacts_write | Создавать и изменять контакты |
crm_contacts_delete | Удалять или анонимизировать контакты |
crm_companies_read | Читать компании |
crm_companies_write | Создавать и изменять компании |
crm_companies_delete | Удалять компании |
crm_deals_read | Читать сделки |
crm_deals_write | Создавать, изменять и перемещать сделки |
crm_deals_delete | Удалять сделки |
crm_tasks_read | Читать задачи |
crm_tasks_write | Создавать и изменять задачи |
crm_tasks_delete | Удалять задачи |
crm_pipelines_read | Читать воронки, этапы и причины отказа |
crm_pipelines_manage | Управлять воронками, этапами и причинами отказа |
crm_tags_read | Читать справочник тегов |
crm_tags_manage | Управлять справочником тегов |
crm_custom_fields_read | Читать definitions и разрешённые значения дополнительных полей |
crm_custom_fields_manage | Управлять definitions/options и назначать значения |
crm_operators_read | Читать операторов и раскрывать actor/leaderboard операторов |
CRM scopes: защищённые значения
| Scope | Доступ в опубликованных маршрутах v1 |
|---|---|
crm_contact_data_read | Раскрывать телефоны, email и контактные custom fields |
crm_contact_data_write | Изменять телефоны, email и контактные custom fields |
crm_amounts_read | Раскрывать суммы сделок, analytics и custom fields типа money |
crm_amounts_write | Изменять денежные поля в доступных deal/custom-field mutations |
Эти scopes применяются дополнительно к scope ресурса. Например, crm_contacts_read без
crm_contact_data_read позволяет получить контакт, но телефоны и email будут исключены или
замаскированы. crm_custom_fields_manage без crm_contact_data_write не позволяет установить
phone/email, а без crm_amounts_write — значение money.
CRM scopes: поиск, синхронизация и настройки
| Scope | Доступ в публичном CRM API v1 |
|---|---|
crm_search | GET /search; типы результатов дополнительно фильтруются ресурсными read-scopes |
crm_analytics_read | GET /analytics/summary; суммы и операторы проецируются по дополнительным scopes |
crm_changes_read | GET /changes и /changes/cursor, а также подписка crm_change |
crm_audit_read | GET /audit с ресурсной и чувствительной scope-проекцией |
crm_settings_read | GET /settings и GET /capabilities |
crm_settings_manage | PATCH /settings |
Для CRM webhook нет отдельного scope. Подписка привязывается к applicationId и требует
crm_changes_read вместе с read-scope доставляемого ресурса. Сами подписки создаются в кабинете:
Настройки → Webhooks — публичного маршрута для управления ими нет.
Зарезервированные CRM scopes
Следующие scopes существуют в модели прав, но соответствующие публичные маршруты не опубликованы в CRM API v1:
| Scope | Статус |
|---|---|
crm_files_read | Reserved; endpoints файлов в v1 отсутствуют |
crm_files_write | Reserved; endpoints загрузки/привязки файлов в v1 отсутствуют |
crm_import_manage | Reserved; endpoints import jobs в v1 отсутствуют |
crm_export_manage | Reserved; endpoints export jobs в v1 отсутствуют |
crm_bulk_manage | Reserved; endpoints асинхронных bulk jobs в v1 отсутствуют |
Activities и deal items также не имеют опубликованных v1-маршрутов или отдельных публичных scopes.
crm_amounts_* не означает наличие items API. Не выдавайте reserved scopes без будущего явно
описанного endpoint и не генерируйте предполагаемые URL.
Scopes AI-агентов и каналов
| Scope | Доступ |
|---|---|
ai_agents | CRUD AI-агентов |
ai_agents_products_sets | CRUD наборов товаров и товаров внутри них |
ai_agents_knowledge_bases | CRUD баз знаний и файлов |
ai_agents_api_integrations | CRUD API-интеграций AI-агента |
channel_ai_agent | Выбор и настройка AI-агента на канале |
Практика безопасности
- Выпускайте отдельное приложение для каждой интеграции и среды.
- Выдавайте минимальный набор scopes и регулярно пересматривайте его.
- Ограничивайте серверные интеграции через
allowedCidrs. - Храните секрет в secret manager и исключите его из логов, exception telemetry и Git.
- Ротируйте credential до
expiresAt; обрабатывайте старый и новый ключи только в grace period. - При компрометации сразу удаляйте приложение: это отзывает все связанные credentials.
- Передавайте безопасный
X-Request-Idи сохраняйте его для диагностики, но не включайте секрет.
Первый CRM-запрос и OpenAPI
Базовый URL CRM API: https://api.apx.chat/api/v1/crm
const url = "https://api.apx.chat/api/v1/crm/capabilities";
const response = await fetch(url, {
method: "GET",
headers: {
"X-Api-Token": "tok_xxxxxxxxxxxxx",
},
});
const data = await response.json();
console.log(data);OpenAPI описывает публичный CRM API. JWT management-маршруты /api-tokens в эту спецификацию не
входят.
Документация по другим API
Приложения для переписки — Messaging API
Права переписки назначает только владелец кабинета в этом же разделе. Новые scopes не добавляются старым токенам автоматически. Администратор продолжает управлять обычными CRM/AI-токенами, но не приложениями с правами Messaging API.
При создании выберите тип приложения:
- Автоматизация / бот соблюдает паузу автоматизации. Ответ от имени приложения не требует выбора сотрудника.
- Рабочее место сотрудника — явно разрешённое владельцем внешнее приложение. Оно может отвечать при паузе бота только от имени выбранного сотрудника с действующим правом ответа. В закрытом диалоге и в ручной очереди приложение действует так же, как этот сотрудник в APX, — см. ниже. Тип приложения после создания неизменяем; для другого типа создайте отдельное приложение.
Выберите доступные каналы. Пустой список полностью закрывает доступ к диалогам. Чтобы разрешить отправку от имени сотрудника, выдайте messaging_send_as_employee и выберите существующих авторов APX. Пустой список авторов отключает такую отправку, но не выдаёт и не отзывает права самого сотрудника. Эти права меняются только в едином центре «Сотрудники и доступ». Подпись «API: название приложения» сохраняется во всех случаях.
| Scope | Возможность |
|---|---|
messaging_read | Чтение доступных диалогов и публичной истории |
messaging_send | Отправка сообщения в диалог |
messaging_send_as_employee | Отправка от имени явно разрешённого сотрудника |
messaging_start | Первый контакт, если его поддерживает канал |
messaging_handoff | Передача сотруднику с остановкой автоматизации |
messaging_response_resolve | Снятие ожидания ответа с проверкой актуальной версии |
messaging_upload | Загрузка файлов для публичного ответа |
messaging_events_read | Чтение событий переписки |
Ответ API по умолчанию сохраняет ожидание ответа. Принятие запроса не означает доставку клиенту. Повторять отправку нужно с тем же идентификатором сообщения и ключом идемпотентности. При конфликте версии настроек обновите карточку, проверьте актуальные разрешения и сохраните заново.
В настройках webhook событие «Переписка Messaging API» подключает доставку событий сообщения, его доставки, ожидания ответа, передачи и управления ботом. Для подписки нужны secret и действующее приложение со scopes messaging_read и messaging_events_read. Подпиской управляет владелец. Внутренние заметки и их файлы в Messaging API не передаются.
Закрытый диалог и ручная очередь
Приложение «Рабочее место сотрудника» отправляет сообщение так же, как сотрудник authorUserId в APX:
| Диалог | Что происходит при отправке |
|---|---|
| Закрытый 1:1 в мессенджере: Telegram, Telegram Personal, WhatsApp, WhatsApp Personal, VK, MAX, MAX Personal, Instagram | Сообщение уходит клиенту без переоткрытия: диалог остаётся закрытым, open_chat не приходит. Когда клиент ответит, диалог вернётся к ответственному, а если ответственного нет или он недоступен — по правилам канала |
| Закрытые почта, Avito, сайт | Сначала диалог возобновляется от имени автора, как «Возобновить» в APX: ответственным становится authorUserId, приходит open_chat с reason: "reopened_manual". Затем сообщение уходит клиенту |
| Закрытая группа | 409 dialog_closed: сотрудник покинул группу, вернуться в неё можно только в APX |
| Ждёт в ручной очереди | 403 dialog_in_queue: ответить можно после «Взять в работу» в APX; в details.dialogId — идентификатор диалога |
Без права ответа или возобновления приходит 403, и диалог не меняется. Если автора нельзя назначить ответственным, приходит 409 с кодом назначения, например ASSIGNEE_TEMPORARILY_UNAVAILABLE или ASSIGNEE_HAS_NO_CHANNEL_ACCESS.
Приложение «Автоматизация / бот» работает как раньше: отправка в закрытый диалог возвращает его в работу по правилам канала и присылает open_chat с reason: "reopened_api". Если диалог при этом попадает в ручную очередь, отправка получает 409, а переоткрытие отменяется.
Первый контакт
Первый контакт — отправка с channelId и recipient вместо dialogId; нужны scopes messaging_send и messaging_start. Для приложения «Рабочее место сотрудника»:
- в канале с режимом «Очередь» новый диалог сразу назначается автору (
authorUserId) и в очередь не встаёт. Раньше такой запрос получал403. Автор должен состоять в канале, иметь право «Написать первым» и не быть на паузе, иначе приходит403или409с кодом назначения; - если у получателя есть активный диалог в канале, сообщение уходит в него. Если этот диалог ждёт в ручной очереди, приходит
409 dialog_in_queue«Диалог ждёт в очереди — возьмите его в работу», а вdetails.dialogId— идентификатор этого диалога; - если у получателя есть закрытый диалог, сообщение уходит в него по правилам таблицы выше. Если у автора нет прав на этот диалог, создаётся новый;
- в режиме «Авто» всё как раньше: ответственного выбирает распределение канала.
Для приложения «Автоматизация / бот» ничего не изменилось: новый диалог распределяется по правилам канала, а в канале с режимом «Очередь» отправка получает 409.
Изменения 29.09.2026 (v1)
Версия API осталась v1: схемы запросов и ответов не менялись. Добавились поля episode и reason в webhook-событии open_chat для payload v1 и v2 (среди значений reason — новое reopened_api) и код ошибки dialog_in_queue (409 при первом контакте, 403 при отправке в диалог очереди; идентификатор — в details.dialogId). Изменилось поведение:
- Вернувшийся клиент в мессенджере 1:1 продолжает тот же диалог: вместо
create_chatс новымidприходитopen_chatс прежнимid, следующимepisodeиreason: "reopened_inbound". Почта, Avito и групповые чаты по-прежнему создают новый диалог. - Отправка приложением «Рабочее место сотрудника» в закрытый диалог: в 1:1 мессенджера — без переоткрытия и без
open_chat, ответ клиента вернёт диалог к ответственному; в почту, Avito и сайт — сначала «Возобновить» от имени автора (приходитopen_chatсreason: "reopened_manual"), затем отправка; в закрытую группу —409 dialog_closed. «Автоматизация / бот» — как раньше: переоткрытие по правилам канала иopen_chatсreason: "reopened_api", в ручной очереди —409. - Первый контакт «Рабочего места сотрудника» в канале с режимом «Очередь» вместо
403создаёт диалог на автора. Если диалог получателя ждёт в очереди —409 dialog_in_queue; если у получателя есть закрытый диалог — отправка в него по правилам пункта 2. В режиме «Авто» — как раньше. - Повторные
open_chatиclose_chatодного диалога доставляются при каждом открытии и закрытии; раньше повтор мог теряться. Подробнее — в документации webhook. - В ручной очереди отвечают только после «Взять в работу»: отправка приложением «Рабочее место сотрудника» в диалог очереди получает
403 dialog_in_queue.
Если интеграция создавала запись на каждый create_chat, обрабатывайте и open_chat вернувшегося клиента: это новое обращение в том же диалоге с прежним id. Не отбрасывайте повторные open_chat и close_chat как дубли по data.id.