◆ Sapport для разработчиков Справочник API Каталог ошибок OpenAPI
Разделы

#Аутентификация и права

Статус: Доступно — ключи, области, права, список IP. Планируется — тестовые ключи и песочница.

Назначение: как устроены интеграции и ключи, что означают области, как вычисляются фактические права запроса, как ротировать и отзывать ключи и что делать при утечке.

#Модель: арендатор → интеграция → ключи

Арендатор
 └── Интеграция «Моя CRM»  (служебная учётная запись, без входа в кабинет)
      ├── роль интеграции (набор прав)
      ├── Ключ A  (области, срок, список IP, режим)
      └── Ключ B  (выпущен при ротации; Ключ A доживает до 24 ч)

#Формат ключа

sap_<ячейка>_<режим>_<идентификатор>_<секрет>
ЧастьЗначенияСмысл
sapпостоянноПрефикс. По нему сканеры секретов находят утёкшие ключи.
ячейкаru, en, idЯчейка, в которой ключ действует.
режимlive, testБоевой или тестовый (песочница — Планируется).
идентификатор12 знаков base32Публичный идентификатор ключа (key_id). Виден в кабинете и журналах.
секретоколо 43 знаков base62Секретная часть: 32 случайных байта.

Пример (заведомо ненастоящий):

sap_ru_test_EXAMPLE0KEYID_EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEX

На стороне платформы хранится только необратимый хеш секрета с серверным «перцем» ячейки. Утечка базы без перца не раскрывает ключи.

#Как передавать ключ

Только в заголовке Authorization, только по TLS:

GET /api/public/v1/me HTTP/1.1
Host: formula-cream.pro
Authorization: Bearer sap_ru_live_EXAMPLE0KEYID_…

#Жизненный цикл ключа

СобытиеКак работает
ВыпускКабинет: Настройки → API. Ключ показывается один раз; сохраните его немедленно. Событие записывается в аудит, владельцу арендатора уходит уведомление.
ХранениеОтветственность клиента: хранилище секретов или переменные окружения. Не в репозитории, не во фронтенде, не в логах.
СрокПо умолчанию 365 суток, больше 365 задать нельзя. Срок виден в GET /me (key.expires_at). За 30 и за 7 суток до истечения приходит событие вебхука api_key.expiring. Ключ с истёкшим сроком получает тот же ответ, что и несуществующий: 401 invalid_api_key.
РотацияВыпустите новый ключ интеграции, переключите свои системы, затем отзовите старый. Старый ключ при ротации продолжает работать до 24 часов, чтобы переключение не было мгновенным.
ОтзывКабинет → ключ → «Отозвать». Действует не позже чем через 60 секунд (результат проверки кэшируется до минуты, при отзыве кэш сбрасывается). Отзыв необратим.
УчётДля каждого ключа в кабинете видны время и (в хешированном виде) адрес последнего использования.

#Пошаговая ротация без простоя

  1. Выпустите новый ключ той же интеграции с теми же областями.
  2. Разверните новый ключ в хранилище секретов и перезапустите сервисы.
  3. Убедитесь по «Использованию API» в кабинете, что старый ключ больше не вызывается.
  4. Отзовите старый ключ. Если забыли — он погаснет сам по окончании окна перекрытия.

#Области (scopes)

Область — право, которое вы даёте ключу. Выдавайте минимально необходимые. Колонка «Платно» — списывается ли плата с кошелька арендатора. Колонка «ПДн» — попадают ли в ответы персональные данные клиентов арендатора.

ОбластьЧто даётПлатноПДн
conversations:readДиалоги и сообщениянетда
conversations:writeПриём сообщения клиента из вашего канала, сообщение оператора, передача, закрытие, перехват и возврат ИИнетда
ai_seller:invokeОтвет ИИ-продажника на сообщение клиента из вашего каналадада
leads:readЛиды и контактынетда
leads:writeСоздание и правка лида, принятие во внешнюю CRM, заметки, задачи, подтверждение запросов действий агента, запись внешней оценки (PUT /leads/{id}/score), карта статусов (PUT /crm/status-map)нетда
leads:scoreОценка лида нашей модельюдада
analytics:readАгрегаты трафика, источники, воронка, видимостьнетнет
analytics:writeПриём событий и офлайн-конверсий; только псевдонимные идентификаторынетнет (см. примечание 2)
dashboards:readГотовые срезы «Обзор» и «Пульс»нетнет
articles:readСтатьи, ревизии, статусы публикациинетнет
articles:writeПравка, утверждение, публикациянетнет
articles:generateЗаказ генерации статьиданет
strategist:readОтчёты, очередь тем, действия чатанетнет (примечание 1)
strategist:writeУтверждение и отклонение тем, подтверждение и отмена действий стратеганетнет
strategist:chatХод разговора со стратегомданет (примечание 1)
webhooks:manageПодписки на события, журнал доставокнетнет (примечание 6)

Примечания.

  1. Стратег работает с агрегатами и не видит персональные данные по построению; это подтверждается при выпуске API.
  2. Приём событий принимает только псевдонимные идентификаторы: anonymous_id и user_id — ваши собственные псевдонимы. Почта, телефон и имя в properties отклоняются ответом 422 validation_failed (JSON Pointer указывает поле), поэтому область не относится к ПДн.
  3. POST /leads/{id}/score платный и требует leads:score. Запись собственной оценки внешней CRM (PUT /leads/{id}/score) бесплатна и требует leads:write.
  4. Утверждение темы стратега и подтверждение его действия дополнительно требуют права на то, что предлагается выполнить, и доступного бюджета. Утверждение темы генерацию статьи не запускает.
  5. GET /me, GET /wallet и GET /operations/{id} доступны любому действующему ключу и не требуют области (GET /me и GET /wallet работают и при остановленном обмене — Кошелёк); операции видны только той интеграции, которая их создала.
  6. Подписка с include_pii: true дополнительно требует у интеграции leads:read и/или conversations:read (Вебхуки).
  7. Журнал выдач ПДн виден арендатору в кабинете и по GET /disclosures (хранится 3 года). Какое право нужно ключу для этого запроса, в v1 не зафиксировано и будет указано в OpenAPI. Подробнее — Ячейки и данные.

#Соответствие областей и конечных точек

ОбластьКонечные точки
conversations:readGET /conversations, GET /conversations/{id}, GET /conversations/{id}/messages
conversations:writePOST /conversations, POST /conversations/{id}/messages (приём), POST /conversations/{id}/operator-messages, POST /conversations/{id}/takeover, /release, /close
ai_seller:invokeответ ИИ в POST /conversations/{id}/messages (вместе с conversations:write)
leads:readGET /leads, GET /leads/{id}, GET /leads/by-external/{external_id}
leads:writePOST /leads, PATCH /leads/{id}, POST /leads/{id}/accept, POST /leads/{id}/notes, POST /leads/{id}/tasks, POST /leads/{id}/actions/{action_id}/ack, PUT /leads/{id}/score, PUT /crm/status-map
leads:scorePOST /leads/{id}/score
analytics:readGET /analytics/timeseries, /analytics/traffic-sources, /analytics/funnel, /analytics/visibility
analytics:writePOST /analytics/events, POST /analytics/conversions
dashboards:readGET /dashboards/overview, GET /dashboards/pulse
articles:readGET /articles, GET /articles/{id}, GET /articles/generation-jobs/{id}
articles:writePATCH /articles/{id}, POST /articles/{id}/approve, POST /articles/{id}/publish
articles:generatePOST /articles/generation-jobs (или generate: true при утверждении темы стратега)
strategist:readGET /strategist/reports, GET /strategist/reports/{id}, GET /strategist/topics, GET /strategist/actions, GET /strategist/actions/{id}
strategist:writePOST /strategist/topics/{id}/approve, POST /strategist/topics/{id}/reject, POST /strategist/actions/{id}/confirm, POST /strategist/actions/{id}/cancel
strategist:chatPOST /strategist/chat/sessions, POST /strategist/chat/sessions/{id}/turns
webhooks:manageGET/POST/PATCH/DELETE /webhooks, POST /webhooks/{id}/test, POST /webhooks/{id}/rotate-secret, GET /webhooks/{id}/deliveries
без областиGET /me, GET /wallet, GET /operations/{id}

#Как вычисляются права запроса

Запрос разрешён, только если каждое из условий выполнено. Фактические права — пересечение:

права запроса = области ключа
              ∩ роль интеграции
              ∩ ТЕКУЩИЕ права сотрудника-владельца
              ∩ лицензия (api_access) и тариф
СлойЧто означает на практике
Области ключаЗадаются при выпуске. Расширить область нельзя — выпускают новый ключ.
Роль интеграцииМаксимум того, что интеграция вообще может делать в арендаторе.
Текущие права владельцаПроверяются в момент запроса, а не при выпуске. Если у сотрудника отобрали право, интеграция тоже его теряет. Если сотрудник деактивирован — ключи его интеграций приостанавливаются, владельцу арендатора приходит тревога.
Лицензия и тарифПризнак api_access и ограничения тарифа. Отказ — 403 feature_not_in_plan.

Помимо этого действует проверка арендатора: арендатор должен быть активен. Идентификатор чужого ресурса даёт 404 not_found.

Следствие для интегратора: ключ, который работал вчера, может сегодня получить scope_missing, если права владельца изменились. Это нормальное поведение; обрабатывайте 403.

#Список разрешённых IP

#Тестовые ключи

Статус: Планируется.

Ключи с режимом test (sap_<ячейка>_test_…) работают в песочнице ячейки — отдельном арендаторе с вымышленными данными.

СвойствоТестовый ключ
Данныевымышленные, ваши боевые данные не затрагиваются
Платные операциине списывают деньги
ИИ-продажникотвечает заготовками
Вебхукисобытия приходят
Тот же кодразличие только в ключе и в данных

Боевой ключ не работает в песочнице, тестовый — в боевом арендаторе.

#Что делать при утечке ключа

  1. Отзовите ключ в кабинете (Настройки → API). Отзыв действует не позже чем через 60 секунд.
  2. Выпустите новый ключ и обновите секреты в своих системах.
  3. Проверьте журнал «Использование API» (хранится 90 суток; в нём метод, маршрут, статус, время, стоимость, request_id — без тел запросов и ПДн) и аудит: что делал ключ после предполагаемой даты утечки.
  4. Если у ключа были области с ПДн, проверьте журнал выдач ПДн (кабинет или GET /disclosures, хранится 3 года) и оцените, какие данные могли быть выданы. Обязанности оператора ПДн по уведомлению регуляторов и субъектов определяются законодательством вашей юрисдикции.
  5. Проверьте, не создавались ли новые вебхуки, ключи и интеграции, и отзовите лишнее; при подозрении ротируйте секреты вебхуков.
  6. Удалите ключ из истории репозитория и из логов; одного коммита-удаления мало.

Профилактика: минимальные области, список IP, срок жизни меньше максимального, отдельные ключи на отдельные системы, хранение в менеджере секретов.

#Ошибки аутентификации

КодHTTPКогда
invalid_api_key401Ключа нет, он неверен, отозван или истёк. Ответ намеренно одинаков во всех случаях
wrong_cell401Ключ другой ячейки; в ответе поле correct_base_url с правильным адресом
scope_missing403У ключа нет нужной области или она «срезана» ролью, правами владельца или тарифом
ip_not_allowed403Адрес вне списка разрешённых
feature_not_in_plan403Тариф или лицензия не включают API/функцию
pii_transfer_not_allowed403Ячейка RU: область с ПДн или include_pii запрошены для интеграции со страной размещения вне РФ

Тело ответа — application/problem+json с type вида https://<домен ячейки>/developers/errors#<code> (Соглашения). Полный каталог — Соглашения.

#Заметки