Цепочка resolve
USER → ORGANIZATION → PLATFORM — как рантайм получает тот единственный ключ, которым должен работать, вместе с plaintext.
POST /internal/v1/credentials/resolve — это то, что рантаймы перевода вызывают перед каждой задачей: дай мне credential для пользователя X, провайдера P. Это эндпоинт сервисной плоскости, требующий scope provider-credentials.invoke (Service Plane Tokens) — и, вместе с reveal, одно из всего двух мест, где plaintext покидает сервис.
Запрос
{
"user_id": "b6a4f6cd-…",
"org_id": "0d1e2f3a-…",
"provider_code": "deepl_api",
"purpose": "translation",
"allow_platform_fallback": true,
"correlation_id": "req-01J8…"
}
user_id и org_id должны происходить из валидированного пользовательского токена вашей собственной плоскости — никогда из клиентского ввода. Членство здесь намеренно не перепроверяется: поставить достоверную пару — половина контракта на стороне вызывающего. org_id опционален; без него уровень организации просто пропускается. purpose называет назначение, под которое резолвится ключ, — translation, glossary или tag_fix; любое другое значение — 400. correlation_id — ваш, едет в audit-ленту и возвращается в X-Correlation-ID.
Проход
В рамках политики credential_resolution провайдера (Vendors and Providers) сервис проходит фиксированную цепочку и возвращает первый активный default, который найдёт:
- USER — собственный default пользователя для провайдера.
- ORGANIZATION — когда
org_idприсутствует и провайдер допускает уровень: default организации. (В audit — с причинойUSER_NOT_CONFIGURED_ORG_SELECTED.) - PLATFORM — когда и политика, и ваш
allow_platform_fallbackэто разрешают: общий платформенный default.
Ничего не найдено → 404 CREDENTIAL_NOT_CONFIGURED (провайдер существует; ключ никто не настроил — отличается от 404 PROVIDER_NOT_FOUND). Отключённый/удалённый провайдер → 409 PROVIDER_DISABLED. Каждое попадание и каждый промах записываются в audit-ленту.
Ответ
{
"credential_id": "…",
"provider_code": "deepl_api",
"credential_source": "organization",
"owner_type": "organization",
"owner_id": "0d1e2f3a-…",
"credentials": {"api_key": "the-actual-plaintext-key"},
"configuration": {},
"catalog_version": "2026.08.24",
"resolved_at": "2026-08-12T12:30:00Z"
}
credential_source говорит, чей ключ вы держите — покажите это пользователю («работает на ключе вашей организации»). Обращайтесь с credentials как с радиоактивным материалом: используйте немедленно, никогда не сохраняйте, никогда не логируйте; ответ приходит с Cache-Control: no-store.
Никакого fallback при runtime-ошибках — так задумано
Если отрезолвленный ключ окажется сломанным у вендора (401, исчерпанная квота), сервис не выдаст вам следующий уровень при повторе: резолюция зависит только от сохранённого состояния, так что вы получите тот же ключ снова. Доносите сбой до владельца ключа; не сжигайте молча чужую квоту. allow_platform_fallback: false существует для обратной дисциплины — контрактов, требующих исключительно клиентских ключей.
Разобранный сквозной проход (попадание в user → fallback на org → fallback на платформу → промах): сценарий cookbook 06. Что UI должен показать перед отправкой задачи: Credential Status.