The resolve chain
USER → ORGANIZATION → PLATFORM — how a runtime gets the one key it should use, plaintext included.
POST /internal/v1/credentials/resolve is what translation runtimes call before every job: give me the credential to use for user X, provider P. It is a service-plane endpoint requiring the provider-credentials.invoke scope (Service Plane Tokens) — and, together with reveal, one of only two places plaintext ever leaves the service.
The request
{
"user_id": "b6a4f6cd-…",
"org_id": "0d1e2f3a-…",
"provider_code": "deepl_api",
"purpose": "translation",
"allow_platform_fallback": true,
"correlation_id": "req-01J8…"
}
user_id and org_id must come from a validated user token of your own plane — never from client input. Membership is deliberately not re-checked here: supplying a trustworthy pair is the caller's half of the contract. org_id is optional; without it the organization level is simply skipped. purpose names the workload the key is resolved for — translation, glossary, or tag_fix; any other value is a 400. correlation_id is yours, travels into the audit trail, and comes back in X-Correlation-ID.
The walk
Within the provider's credential_resolution policy (Vendors and Providers), the service walks a fixed chain and returns the first active default it finds:
- USER — the user's own default for the provider.
- ORGANIZATION — when
org_idis present and the provider admits the level: the organization's default. (Audited with reasonUSER_NOT_CONFIGURED_ORG_SELECTED.) - PLATFORM — when the policy and your
allow_platform_fallbackboth permit: the shared platform default.
Nothing found → 404 CREDENTIAL_NOT_CONFIGURED (the provider exists; nobody configured a key — distinct from 404 PROVIDER_NOT_FOUND). A disabled/removed provider → 409 PROVIDER_DISABLED. Every hit and miss is recorded in the audit trail.
The response
{
"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 tells you whose key you hold — surface it to the user ("running on your organization's key"). Treat credentials as radioactive: use immediately, never persist, never log; the response arrives with Cache-Control: no-store.
No fallback on runtime errors — by design
If the resolved key turns out broken at the vendor (401, quota exhausted), the service will not hand you the next level on retry: resolution depends only on stored state, so you will get the same key again. Surface the failure to whoever owns the key; do not silently burn someone else's quota. allow_platform_fallback: false exists for the reverse discipline — contracts that require customer-owned keys only.
Worked end-to-end walk (user hit → org fallback → platform fallback → miss): cookbook scenario 06. What a UI should show before submitting a job: Credential Status.