External API v1: WR/ГЦК кабинеты
External API v1 для WR/ГЦК-кабинетов
External API даёт backend-сервисам доступ к WR/ГЦК-кабинетам пользователя Alitos. Через API можно читать данные, менять настройки и запускать долгие операции.
API предназначен только для server-to-server интеграций. Он не предоставляет raw WR proxy, cookies, пароли или upstream-токены.
API включается поэтапно. Если credential endpoints,
/capabilitiesили OpenAPI возвращают404, соответствующий маршрут ещё не опубликован для этой rollout-волны.
Адреса
Назначение
URL
Credentials и token exchange
https://api.alitos.io
Cabinet API
https://api.alitos.io/v1/external
OpenAPI
https://api.alitos.io/v1/external/openapi.yaml
OpenAPI определяет схемы запросов и ответов, обязательные поля, ограничения и коды ответа.
Быстрый старт
1. Получите credential
Пользователю нужно право external_api_access. Credential создаётся в настройках External API в Alitos и показывается только один раз.
Сохраните значение alitos_cred_... в secret manager. Resource endpoints не принимают credential напрямую.
Credential по умолчанию не истекает. Если политика компании требует ограниченный срок, при создании укажите expiresInDays от 1 до 365.
Управление credentials также доступно авторизованному Alitos-клиенту:
GET /external-access/credentials;POST /external-access/credentials;POST /external-access/credentials/{credentialId}/revoke.
2. Обменяйте credential на JWT
curl --request POST 'https://api.alitos.io/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode \
'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode \
'subject_token_type=urn:alitos:params:oauth:token-type:external-access-credential' \
--data-urlencode \
"subject_token=${ALITOS_EXTERNAL_CREDENTIAL}" \
--data-urlencode \
'requested_token_type=urn:ietf:params:oauth:token-type:access_token' \
--data-urlencode \
'resource=https://api.alitos.io/v1/external' \
--data-urlencode \
'scope=cabinet:read cabinet:settings:write'JWT действует 60 минут. Refresh token не выдаётся. Если scope не передан, JWT получает все scopes credential.
Возьмите access_token из ответа и передавайте его как Bearer JWT.
Запрошенные scopes должны быть подмножеством scopes credential. Значение resource должно в точности совпадать с REST resource.
Кэшируйте JWT в памяти процесса. Интеграция должна автоматически выполнять новый exchange перед истечением срока.
3. Проверьте доступ и найдите кабинет
curl 'https://api.alitos.io/v1/external/capabilities' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}"
curl 'https://api.alitos.io/v1/external/cabinets?q=example&limit=20' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}"
curl 'https://api.alitos.io/v1/external/cabinets/123456789/context' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}"/capabilities возвращает effective scopes, allowlist, доступные группы операций и текущие лимиты.
Scopes и доступ
Scope
Операции
cabinet:read
Кабинеты, пространства, контекст, настройки, проекты и аналитика
cabinet:projects:write
Создание проектов, источники и состояния
cabinet:settings:write
Безопасные settings и analytics-config updates
cabinet:deals:read
Сделки и чувствительные контактные данные
cabinet:exports:run
Экспорт сделок и source analytics jobs
Scope ограничивает credential, но не выдаёт доступ сам по себе. Alitos при каждом запросе проверяет состояние пользователя и его текущий доступ к кабинету.
Если allowlist не задан, credential действует на все кабинеты, доступные пользователю в момент запроса. Если задан — только на перечисленные wrUserId.
REST JWT нельзя использовать для MCP. Пользователь, от имени которого выпущен credential, определяется токеном и не передаётся в request.
Карта API
Все пути в таблице относятся к https://api.alitos.io/v1/external.
Сценарий
Основные endpoints
Scope
Возможности
GET /capabilities
Любой выданный scope
Discovery
GET /namespaces, /cabinets, /cabinet-filters, /cabinet-filter-values, /partners/resolve; POST /cabinets/search
cabinet:read
Cabinet reads
GET /cabinets/{wrUserId}/context, /cabinets/{wrUserId}/settings, /cabinets/{wrUserId}/analytics-config, /cabinets/{wrUserId}/projects/catalog, /cabinets/{wrUserId}/projects/sources, /cabinets/{wrUserId}/audit/changes
cabinet:read
Analytics
POST /analytics/projects/query, /analytics/sources/jobs
Соответственно cabinet:read, cabinet:exports:run
Deals
POST /cabinets/{wrUserId}/deals/search, /cabinets/{wrUserId}/deals/export-jobs
Соответственно cabinet:deals:read, cabinet:exports:run
Projects
POST /cabinets/{wrUserId}/project-creation/preparations, /project-creation/preparations/{preparationId}/commit, /cabinets/{wrUserId}/projects/source-updates, /cabinets/{wrUserId}/projects/state-updates
cabinet:projects:write
Settings
POST /cabinets/{wrUserId}/settings/updates, /cabinets/{wrUserId}/analytics-config/updates
cabinet:settings:write
Operations
GET /operations/{operationId}, /operations/{operationId}/result
Scope исходной операции
Точные query parameters, request bodies и response schemas смотрите в OpenAPI.
Общие правила
Все 64-битные WR identifiers передаются JSON-строками.
Pagination использует opaque cursor. Не разбирайте и не изменяйте его.
Продолжайте pagination с теми же фильтрами и контекстом.
Для endpoints, создающих durable operation, передавайте
reasonиIdempotency-Key.Требования к заголовкам и размерам payload задаёт OpenAPI.
Если кабинет доступен в нескольких контекстах, используйте namespaceId и placementId из discovery. Не меняйте их внутри pagination или idempotent flow.
Безопасные изменения
Idempotency
Для всех endpoints, создающих durable operation, обязателен Idempotency-Key. Используйте UUID или устойчивый идентификатор операции вашей системы.
Тот же key с тем же payload возвращает существующую операцию. Тот же key с другим payload или reason возвращает 409 idempotency_key_conflict.
Если HTTP-ответ потерян, повторите исходный запрос с тем же key. Не создавайте новый key только из-за timeout.
ETag
Settings, analytics config и источники проекта защищены ETag. Сначала выполните GET, затем передайте полученное значение в If-Match.
Strong ETag для источников возвращается при чтении одного существующего проекта. Aggregate response содержит только weak validator.
Отсутствующий If-Match возвращает 428, устаревший — 412. После 412 перечитайте ресурс и сформируйте изменение заново.
Weak ETag с префиксом W/ нельзя использовать для update.
Пример изменения settings
ETAG=$(
curl --silent --include \
'https://api.alitos.io/v1/external/cabinets/123456789/settings' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}" \
| awk -F': ' 'tolower($1)=="etag" {gsub("\r","",$2); print $2}'
)
curl --request POST \
'https://api.alitos.io/v1/external/cabinets/123456789/settings/updates' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}" \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: ${CLIENT_OPERATION_ID}" \
--header "If-Match: ${ETAG}" \
--data '{
"reason": "Включение B8 для nightly pipeline",
"patch": {
"connectProjectsB8": true
}
}'Создание проектов
Создание проекта состоит из двух шагов:
Создайте preparation для проверки данных.
Выполните commit с
reasonиIdempotency-Key.
Preparation не меняет WR и действует 30 минут.
Если job завершился с ambiguous_upstream_outcome, сначала проверьте состояние проектов. Не запускайте автоматический повтор с новым key.
Асинхронные операции
Endpoints, создающие operation, возвращают 202 Accepted, operationId, Location и Retry-After. Project preparation возвращает 201 и job не создаёт.
Статусы операции: queued, running, succeeded, failed.
curl --request POST \
'https://api.alitos.io/v1/external/analytics/sources/jobs' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}" \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: ${CLIENT_OPERATION_ID}" \
--data '{
"reason": "Еженедельный контроль источников",
"wrUserIds": ["123456789"],
"dateFrom": "2026-07-01",
"dateTo": "2026-07-27",
"metricPreset": "b4_source_quality",
"includeSourceRows": true
}'
curl \
'https://api.alitos.io/v1/external/operations/${OPERATION_ID}' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}"
curl \
'https://api.alitos.io/v1/external/operations/${OPERATION_ID}/result?pageSize=250' \
--header "Authorization: Bearer ${ALITOS_EXTERNAL_JWT}"При polling соблюдайте Retry-After и добавляйте jitter. Не опрашивайте операцию чаще одного раза в две секунды.
Операция и результат доступны тому же пользователю и credential, которые создали job. Новый JWT из того же credential подходит для polling при наличии исходного scope.
Для завершившейся с ошибкой операции status endpoint возвращает HTTP 200 и status: "failed". Запрос /result до succeeded возвращает 409 operation_not_completed.
Большие результаты читаются страницами через opaque nextCursor.
Ошибки и повторные запросы
Cabinet API возвращает RFC 7807 Problem Details. Для логики используйте code и retryable, а traceId сохраняйте для диагностики.
Ответ
Действие клиента
401
Получить новый JWT; проверить resource
403
Проверить scope, allowlist и доступ пользователя
409
Проверить code; не менять idempotency key автоматически
412
Перечитать ресурс и повторно сформировать update
428
Добавить обязательный If-Match
429
Соблюдать Retry-After
503
Повторять только при retryable: true
Повторяйте запрос без изменения только при retryable: true. Для ambiguous_upstream_outcome автоматический retry запрещён.
OAuth endpoint возвращает стандартные error и error_description, а не Problem Details.
Лимиты
Актуальные лимиты возвращает /capabilities. Начальные значения: 300 GET/min, 60 POST/min и до 5 одновременных jobs на credential.
Idempotency keys, jobs и результаты хранятся 7 дней.
Срок, замена и отзыв
Плановая ротация credential не требуется. Заменяйте его при компрометации, смене владельца или по политике компании.
Ранее выпущенные credentials сохраняют заданный expiresAt.
Revocation сразу запрещает новый token exchange. Уже выпущенный JWT может действовать до 60 минут.
При компрометации отзовите credential и обратитесь к администратору для снятия external_api_access.
Не записывайте credential, JWT или чувствительные payload в логи и поле reason.
Совместимость
Внутри /v1 изменения совместимы и additive. Игнорируйте незнакомые response fields и не полагайтесь на порядок JSON properties.
Полный контракт: OpenAPI v1.