Skip to main content

Provider Credentials

Quick example

# Vault an OpenAI key
curl "$TRINITITE_BASE/v1/provider-credentials" \
-H "Authorization: Bearer $TRINITITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "openai",
"name": "openai-prod",
"secret": "sk-prod-XXX",
"model_allowlist": ["gpt-4o", "gpt-4o-mini"]
}'

The raw secret is returned only once at creation. After that, only the redacted ID is accessible.


Overview

Provider Credentials store upstream LLM API keys (OpenAI, Anthropic, Azure OpenAI, custom) encrypted at rest. They're referenced from proxy calls via the X-Trinitite-Credential-Id header — your applications never handle raw provider keys.

Authentication: Authorization: Bearer <session_token | api_key> with the relevant provider_credentials:* permission.


Endpoints

MethodPathPermission
GET/v1/proxy/credentialsprovider_credentials:read
POST/v1/proxy/credentialsprovider_credentials:create
GET/v1/proxy/credentials/{id}provider_credentials:read
PATCH/v1/proxy/credentials/{id}provider_credentials:create
DELETE/v1/proxy/credentials/{id}provider_credentials:delete

Org-wide spend caps and rate limits per credential — see Governance Controls.


Credential object

{
"credential_id": "cred_01JF8RCRD1A2B3C4D5E6F7G8H9I",
"provider": "openai",
"label": "production-openai-primary",
"key_prefix": "sk-proj-...",
"key_suffix": "...d8f9",
"base_url": null,
"allowed_models": ["gpt-4o", "gpt-4o-mini", "o3-mini"],
"status": "active",
"created_at": "2026-04-01T00:00:00Z",
"last_used_at": "2026-05-01T15:42:00Z",
"monthly_spend_cap_usd": "5000.00",
"rpm_limit": 600,
"disabled": false
}
FieldDescription
credential_idStable ID (cred_*). Used in X-Trinitite-Credential-Id
provideropenai, anthropic, azure_openai, custom
labelHuman-readable label
key_prefix / key_suffixTruncated bookends for identification — the raw key is never returned after creation
base_urlOverride base URL (for Azure deployments or self-hosted custom endpoints)
allowed_modelsIf set, only these model names are permitted via this credential
monthly_spend_cap_usdDecimal string (USD); null means unbounded
rpm_limitPer-credential requests-per-minute cap

POST /v1/proxy/credentials

Store a new credential.

{
"provider": "openai",
"label": "production-openai-primary",
"plaintext_key": "sk-proj-4f8d9e2a1c6b7f3a9e1d2c4b5a6f7e8d",
"allowed_models": ["gpt-4o", "gpt-4o-mini", "o3-mini"]
}
FieldTypeRequiredDescription
providerenumYesopenai, anthropic, azure_openai, custom
labelstringYes1–100 chars; unique within your org
plaintext_keystringYesRaw API key. Encrypted before storage; not returned on subsequent reads
base_urlstringNoOverride base URL — required for azure_openai and custom
allowed_modelsstring[]NoIf set, only listed models are permitted

Response — 201 Created returns the credential object including plaintext_key once. Store it in your secrets manager — subsequent reads redact it.


GET /v1/proxy/credentials

Returns the redacted list (no plaintext_key).

QueryTypeDescription
providerstringFilter by provider
statusstringactive or revoked
limit / cursorCursor pagination
{
"data": [
{ "credential_id": "cred_01JF...", "provider": "openai", "label": "production-openai-primary", "status": "active", ... }
],
"page": { "next_cursor": null, "has_more": false }
}

GET /v1/proxy/credentials/{id}

Returns one credential, redacted.


PATCH /v1/proxy/credentials/{id}

Partial update. Use plaintext_key to rotate the underlying provider key — the old key is destroyed atomically and in-flight calls switch over with no application changes.

{
"label": "production-openai-primary-rotated-2026-05",
"plaintext_key": "sk-proj-NEWKEYHERE",
"allowed_models": ["gpt-4o", "gpt-4o-mini", "o3-mini", "gpt-5"]
}
FieldDescription
labelNew label
plaintext_keyNew key (rotation). Returned once in the response
base_urlNew base URL
allowed_modelsReplace allowlist

DELETE /v1/proxy/credentials/{id}

Revoke. In-flight proxy calls using the credential start failing with 404 credential_not_found. Returns 204 No Content.


Errors

HTTPerror.codeCause
400validation_errorMissing required field; invalid provider; base_url required for custom
401unauthenticatedMissing or invalid credential
403forbiddenCaller lacks the required scope
404not_found / credential_not_foundCredential does not exist or is revoked
409conflictA credential with the same label already exists

Best practices

  • Rotate quarterly. Use PATCH with plaintext_key — clients pick up the new key on the next request without redeploys.
  • Scope by environment. Issue separate credentials for dev, staging, production. Combine with API key environment scoping for blast-radius isolation.
  • Lock down allowed_models. Restricting a credential to known-good models prevents accidental usage of expensive or unapproved tiers.
  • Cap spend. monthly_spend_cap_usd is a hard limit — Trinitite trips the credential into a soft-fail state once tripped (see Governance Controls).

Next steps