API-токены

API-токен связывает внешнее приложение с одной организацией APX Chat. Стабильный applicationId определяет tenant, пространство externalId, rate limits и webhook-проекцию; сменяемый credentialId относится к конкретному секрету.

Где взять токен: Настройки → API-токены в кабинете. Нажмите «Создать токен», выберите нужные scopes, срок действия и, при необходимости, список разрешённых IP. Секрет показывается один раз при создании — сохраните его сразу, повторно посмотреть его нельзя.

Для публичных CRM API, AI Agents API и Channels API передавайте токен ровно одним способом:

PLAINTEXT
X-Api-Token: tok_xxxxxxxxxxxxx
Authorization: Bearer tok_xxxxxxxxxxxxx

X-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.

Пример создания:

JSON
{
  "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 принимает только изменяемые поля:

JSON
{
  "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.

Безопасная последовательность:

  1. Выпустите новый credential и атомарно сохраните секрет в secret manager.
  2. Обновите все экземпляры интеграции.
  3. Выполните проверочный запрос именно с новым секретом и убедитесь, что он работает.
  4. Дождитесь окончания 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_searchGET /search; типы результатов дополнительно фильтруются ресурсными read-scopes
crm_analytics_readGET /analytics/summary; суммы и операторы проецируются по дополнительным scopes
crm_changes_readGET /changes и /changes/cursor, а также подписка crm_change
crm_audit_readGET /audit с ресурсной и чувствительной scope-проекцией
crm_settings_readGET /settings и GET /capabilities
crm_settings_managePATCH /settings

Для CRM webhook нет отдельного scope. Подписка привязывается к applicationId и требует crm_changes_read вместе с read-scope доставляемого ресурса. Сами подписки создаются в кабинете: Настройки → Webhooks — публичного маршрута для управления ими нет.

Зарезервированные CRM scopes

Следующие scopes существуют в модели прав, но соответствующие публичные маршруты не опубликованы в CRM API v1:

ScopeСтатус
crm_files_readReserved; endpoints файлов в v1 отсутствуют
crm_files_writeReserved; endpoints загрузки/привязки файлов в v1 отсутствуют
crm_import_manageReserved; endpoints import jobs в v1 отсутствуют
crm_export_manageReserved; endpoints export jobs в v1 отсутствуют
crm_bulk_manageReserved; endpoints асинхронных bulk jobs в v1 отсутствуют

Activities и deal items также не имеют опубликованных v1-маршрутов или отдельных публичных scopes. crm_amounts_* не означает наличие items API. Не выдавайте reserved scopes без будущего явно описанного endpoint и не генерируйте предполагаемые URL.

Scopes AI-агентов и каналов

ScopeДоступ
ai_agentsCRUD AI-агентов
ai_agents_products_setsCRUD наборов товаров и товаров внутри них
ai_agents_knowledge_basesCRUD баз знаний и файлов
ai_agents_api_integrationsCRUD 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:1 продолжает тот же диалог: вместо create_chat с новым id приходит open_chat с прежним id, следующим episode и reason: "reopened_inbound". Почта, Avito и групповые чаты по-прежнему создают новый диалог.
  2. Отправка приложением «Рабочее место сотрудника» в закрытый диалог: в 1:1 мессенджера — без переоткрытия и без open_chat, ответ клиента вернёт диалог к ответственному; в почту, Avito и сайт — сначала «Возобновить» от имени автора (приходит open_chat с reason: "reopened_manual"), затем отправка; в закрытую группу — 409 dialog_closed. «Автоматизация / бот» — как раньше: переоткрытие по правилам канала и open_chat с reason: "reopened_api", в ручной очереди — 409.
  3. Первый контакт «Рабочего места сотрудника» в канале с режимом «Очередь» вместо 403 создаёт диалог на автора. Если диалог получателя ждёт в очереди — 409 dialog_in_queue; если у получателя есть закрытый диалог — отправка в него по правилам пункта 2. В режиме «Авто» — как раньше.
  4. Повторные open_chat и close_chat одного диалога доставляются при каждом открытии и закрытии; раньше повтор мог теряться. Подробнее — в документации webhook.
  5. В ручной очереди отвечают только после «Взять в работу»: отправка приложением «Рабочее место сотрудника» в диалог очереди получает 403 dialog_in_queue.

Если интеграция создавала запись на каждый create_chat, обрабатывайте и open_chat вернувшегося клиента: это новое обращение в том же диалоге с прежним id. Не отбрасывайте повторные open_chat и close_chat как дубли по data.id.