Skip to Content
GatewayAPI ReferenceAuthentication

Authentication

The gateway uses a two-key authentication model. Every proxy request requires both a Curate-Me API key (for gateway governance) and a provider API key (for the upstream LLM provider).

Curate-Me API key

Your Curate-Me API key identifies your organization and determines which governance policies apply. API keys start with the cm_sk_ prefix (other accepted prefixes: cm_gw_, cm-gw-, cmgw_, cm_, sk_live_).

How to include your API key

The gateway checks for your API key in the following order:

PriorityMethodExample
1X-CM-API-Key header (recommended)X-CM-API-Key: cm_sk_abc123def456
2Authorization: Bearer headerAuthorization: Bearer cm_sk_abc123def456
3api_key query parameter?api_key=cm_sk_abc123def456

The X-CM-API-Key header is the recommended method. The Authorization: Bearer method works when the token starts with a recognized gateway key prefix. The query parameter is a fallback for EventSource/SSE connections that cannot send custom headers.

# Recommended: X-CM-API-Key header curl https://api.curate-me.ai/v1/chat/completions \ -H "X-CM-API-Key: cm_sk_abc123def456" \ -H "X-Provider-Key: sk-openai-xxx" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'

Key format

cm_sk_{random_alphanumeric_string}

API keys are created in the dashboard under Settings > API Keys or via the B2B API (POST /api/v1/b2b/api-keys).

Key scopes

API keys can be scoped to limit what they can do:

ScopeAllows
gateway:writeSend proxy requests (chat completions, messages)
gateway:readList models, check health
admin:writeModify governance policies, secrets, model aliases
admin:readView governance policies, usage data, billing
billing:readView billing and cost data

Proxy requests require the gateway:write scope. The GET /v1/models endpoint requires gateway:read.

Key rotation

When an API key is rotated, the old key enters a grace period during which it continues to work but the gateway returns deprecation headers:

HeaderValueDescription
X-CM-Key-Deprecated"true"The key has been replaced
X-CM-Grace-Remaining-HoursNumberHours until the old key stops working

After the grace period expires, the old key returns a 401 error with code: "key_rotated" and the ID of the replacement key.

Provider API key

The provider API key is your API key for the upstream LLM provider (OpenAI, Anthropic, Google, Groq, Mistral, xAI, and more). The gateway needs this to forward requests to the provider.

How to include your provider key

PriorityMethodExample
1X-Provider-Key header (explicit)X-Provider-Key: sk-openai-xxx
2Authorization: Bearer headerAuthorization: Bearer sk-openai-xxx
3Stored secrets (dashboard)Configure once, no header needed
4Environment variable (self-hosted)OPENAI_API_KEY, DEEPSEEK_API_KEY, etc.

When both the gateway key and provider key are sent via Authorization: Bearer, the gateway distinguishes them by prefix — tokens starting with a gateway prefix (cm_sk_, etc.) are treated as gateway keys, and all others are treated as provider keys.

# Both keys via headers curl https://api.curate-me.ai/v1/chat/completions \ -H "X-CM-API-Key: cm_sk_abc123" \ -H "X-Provider-Key: sk-openai-xxx" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}' # Gateway key via X-CM-API-Key, provider key via Authorization curl https://api.curate-me.ai/v1/chat/completions \ -H "X-CM-API-Key: cm_sk_abc123" \ -H "Authorization: Bearer sk-openai-xxx" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'

For production deployments, store provider API keys as org-scoped secrets in the dashboard under Settings > Provider Secrets. When a request arrives without an explicit provider key, the gateway retrieves the stored key from encrypted secret custody storage.

This approach keeps provider keys out of application code and client-side configurations.

# Store a provider secret via the admin API curl -X POST https://api.curate-me.ai/gateway/admin/secrets \ -H "Authorization: Bearer $DASHBOARD_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "provider": "openai", "secret": "sk-openai-xxx" }' # Now proxy requests work without explicit provider keys curl https://api.curate-me.ai/v1/chat/completions \ -H "X-CM-API-Key: cm_sk_abc123" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'

Provider-specific auth translation

Each provider uses a different authentication mechanism. The gateway handles this translation automatically:

ProviderUpstream auth methodYou always use
OpenAIAuthorization: Bearer {key}X-Provider-Key or stored secret
Anthropicx-api-key: {key} + anthropic-version: 2023-06-01X-Provider-Key or stored secret
Google Gemini?key={key} query parameterX-Provider-Key or stored secret
DeepSeekAuthorization: Bearer {key} (OpenAI-compatible)X-Provider-Key or stored secret

Dashboard JWT fallback

The gateway also accepts dashboard B2B JWT tokens for admin endpoints. When no gateway API key is present, the gateway checks for a valid Authorization: Bearer <jwt> token. If the JWT is a valid dashboard access token, the gateway derives gateway scopes from the user’s organization role.

Org roleDerived gateway scopes
Owner / Admingateway_admin, gateway:write, gateway:read, admin:write, admin:read, billing:read
Manager / Operatorgateway_operator, gateway:write, gateway:read, admin:read, billing:read
Member (default)gateway_viewer, gateway:read, admin:read, billing:read

This fallback allows the dashboard UI to access gateway admin endpoints directly without creating a separate API key.

Organization context

Every API key is bound to an organization. The org_id is extracted from the API key record and determines:

  • Which governance policy applies (rate limits, budgets, model allowlists)
  • Which daily/monthly cost counters are incremented
  • Which usage records and audit logs are created
  • Which provider secrets are retrieved

The org context is included in the X-Gateway-Org response header on every request.

Error responses

Missing gateway key

HTTP/1.1 401 Unauthorized
{ "error": { "message": "Missing or invalid credentials. Provide a valid gateway key via X-CM-API-Key / Authorization: Bearer <gateway-key>, or a valid dashboard access token via Authorization: Bearer <jwt>.", "type": "authentication_error", "param": null, "code": "invalid_api_key" } }

Invalid or expired key

HTTP/1.1 401 Unauthorized
{ "error": { "message": "Invalid API key, expired, or daily rate limit exceeded", "type": "authentication_error", "param": null, "code": "invalid_api_key" } }

Rotated key (grace period expired)

HTTP/1.1 401 Unauthorized
{ "error": { "message": "API key has been rotated. Use the new key. Replacement key ID: key_xyz789", "type": "authentication_error", "param": null, "code": "key_rotated" } }

Missing provider key

HTTP/1.1 401 Unauthorized
{ "error": { "message": "Missing provider API key. Provide your provider's API key via X-Provider-Key header, Authorization: Bearer <provider-key>, or store it via POST /gateway/admin/secrets.", "type": "authentication_error", "param": null, "code": "missing_provider_key" } }