Аутентификация
Bearer-ключи с префиксом aac_live_, модель прав, привязка к индексу, CORS и заголовки, которые возвращает каждый ответ.
Все эндпоинты под /api/v1/indexes/*, /api/v1/analytics/*, /api/v1/billing/*, /api/v1/organization/*, /api/v1/admin/*, /api/v1/jobs/*, /api/v1/api-keys/* и /api/v1/recent требуют API-ключ. Публичны /health, /status, /capabilities, /openapi.json, /embed/* (по токену сессии) и /widget.js.
Как передать ключ
curl -sS "$AAC_API/indexes/documentation/search?q=тарифы" \
-H "Authorization: Bearer aac_live_9f3c2a1bD4eF6gH8iJ0kL2mN4oP6qR8s"Заголовок должен начинаться с Bearer , а ключ — соответствовать шаблону ^aac_(live|test)_[A-Za-z0-9_-]{20,}$. Всё остальное отклоняется до обращения к базе:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"request_id": "req_2f8a1c4e6b9d0e3f5a7c8b1d"
}
}Тот же 401 возвращается для корректного по форме, но неизвестного или отозванного ключа (у него заполнено revokedAt). Ключи хранятся как SHA-256-хеши, открытое значение существует только в ответе на создание.
Права (scopes)
Ключ несёт список прав. * даёт все. Консоль предлагает следующие:
| Право | Что открывает |
|---|---|
search:read | /search, /suggest, /stats, GET /documents/{id}, диалоги, ассистент, answer, voice, image, ограниченные ключи на чтение |
documents:read | GET /documents/export, GET /snapshots, GET /snapshots/{id} |
documents:read_private | Приватные поля в хитах, документах и экспорте (см. Приватные поля) |
documents:write | POST /documents, /documents/bulk, DELETE /documents…, /webhook, /docsearch/preview; вместе с settings:write — создание, удаление и восстановление снимков |
settings:write | POST /reindex, /scoped-key, GET /conversation-models, POST /api-keys/{keyId}/rotate, чтение и отмена задач reindex, file_import, snapshot, restore |
analytics:read | Чтение аналитики (обзор также принимает search:read) |
analytics:write | POST /events — клики и конверсии |
billing:read | /billing/usage, /billing/wallet, /billing/invoices, /billing/invoices/{id}[.pdf] |
Недостающее право — это 403, а не 404: эндпоинт существует, ключу просто нельзя им пользоваться.
curl -sS -X POST "$AAC_API/indexes/documentation/documents" \
-H "Authorization: Bearer aac_live_…read_only_key…" \
-H "Content-Type: application/json" \
-d '{"documents":[{"external_id":"x","content":"y"}]}'{
"error": {
"code": "FORBIDDEN",
"message": "Missing scope: documents:write",
"request_id": "req_7b3e9d2a5c8f1a4b6d0e3c7a"
}
}Привязка к индексу
Ключ, созданный для одного индекса (indexId заполнен), может работать только с ним. Обращение к соседнему индексу той же организации возвращает:
{
"error": {
"code": "FORBIDDEN",
"message": "API key is scoped to a different index",
"request_id": "req_4d8c2a6f0b1e3d5c7a9f2b4e"
}
}Слаг, которого нет в организации ключа, даёт 404 INDEX_NOT_FOUND — включая слаги, существующие у другого тенанта. Тенант определяется по ключу, а не по пути или телу запроса.
CORS
API аутентифицируется ключом и не использует cookie, поэтому возвращает полученный Origin и разрешает GET, POST, PUT, DELETE, OPTIONS. Preflight обрабатывается до аутентификации и возвращает 204. Открытые заголовки: x-request-id, x-aac-quota-remaining, x-aac-quota-limit.
Для браузерных ключей ограничивайте список разрешённых origin в консоли, а не полагайтесь только на CORS.
Заголовки ответа
| Заголовок | Значение |
|---|---|
x-request-id | Идентификатор запроса; дублируется в теле как request_id |
x-ratelimit-limit | Сколько запросов разрешено ключу в окне (по умолчанию 600) |
x-ratelimit-remaining | Сколько осталось в текущем окне |
x-ratelimit-reset | Момент сброса окна, Unix-время в секундах |
x-aac-quota-limit | Сколько единиц метрики включено в тариф |
x-aac-quota-remaining | Сколько единиц осталось в периоде |
x-aac-index | Индекс, к которому разрешился запрос |
x-aac-profile | Использованный профиль поиска или default |
x-aac-charged-cents | Сколько списано с кошелька за превышение |
Жизненный цикл ключа
- Создание — в консоли: имя, права, при необходимости привязка к индексу. Лимит зависит от тарифа: 3 ключа на Free, 10 на Pro, 100 на Enterprise.
- Ротация —
POST /api-keys/{keyId}/rotate(scopesettings:write, тело{ "grace_days": 7 }) или диалог в консоли. Преемник получает те же права, origins и привязку к индексу; его открытый текст возвращается один раз (201). Старый ключ работаетgrace_daysдней (по умолчанию 7, от 0 до 30), затем получает401 KEY_ROTATED. Повторная ротация —409 KEY_ALREADY_ROTATED, ротация отозванного или истёкшего ключа —409 KEY_NOT_ACTIVE. Ротация никогда не расширяет доступ: ключ не может выпустить преемника с правами или origins, которых у него нет; ключ, привязанный к индексу, ротирует только ключи своего индекса; ключ не может ротировать ключ, который переживёт его самого (403 KEY_LIFETIME_EXCEEDS_CALLER); ключ, сам находящийся в льготном периоде после ротации, не может ротировать ключи и выпускать ограниченные ключи (403 KEY_IN_GRACE). - Владельцы получают одно напоминание, когда ключ перестанет работать в течение 7 дней (истечение срока или конец льготного периода ротации).
- Отзыв — в консоли, действует немедленно: следующий запрос с этим ключом получит 401. Отзыв ключа, находящегося в льготном периоде ротации, завершает и льготный период.
lastUsedAtобновляется при каждом аутентифицированном вызове, поэтому «мёртвые» ключи легко найти в консоли.
Администрирование платформы
/api/v1/admin/* и POST /billing/invoices/generate предназначены оператору развёртывания: кроме API-ключа нужен заголовок x-platform-admin-token, равный SEARCH_PLATFORM_ADMIN_TOKEN сервера. Без него (или если переменная не задана) — 403 FORBIDDEN, попытка пишется в аудит.
Ключи для браузера
Никогда не отправляйте aac_live_… в браузер. Используйте POST /indexes/{slug}/scoped-key на сервере (право settings:write) или, лучше, POST /embed/session: он возвращает короткоживущий токен сессии только для поиска (по умолчанию 15 минут, максимум час), который виджет использует через прокси /embed/*, так что и креденшл движка на страницу не попадает. У ограниченного ключа фильтр тенанта и лимит выдачи встроены. Подробнее — в разделе Сессия встраивания.