Every /v1 request must carry a credential in the Authorization: Bearer header. The gateway identifies the caller from it (team, workspace, user or service account) and applies that caller’s access rules, limits and budgets.
curl https://app.manylayers.io/v1/models \
  -H "Authorization: Bearer $ML_API_KEY"
Authorization: Bearer is the only header the gateway reads a credential from. The x-api-key header is not accepted. When you use the Anthropic SDK against /v1/messages, configure it to send a bearer token (see below).

How the credential is recognised

The gateway checks the shape of the token to decide how to verify it:
  1. No bearer token, but an ml_session cookie goes to console session authentication (only when console sign-in is enabled). With neither, the request is refused.
  2. A JWT-shaped token (three dot-separated parts, not starting with ml-) is verified against your identity provider, when OIDC is configured.
  3. Anything else is looked up as a stored credential: an API key, a personal access token or a virtual account token.
Each type is verified only by its own authenticator, so an identity provider outage cannot affect API keys and vice versa. Stored credentials are kept only as SHA-256 hashes, and the plaintext is shown once when a credential is issued.

Credential types

CredentialPrefixIssued byUse it for
API keyml-Console (Gateway → API Keys), POST /admin/keys, or gateway.yaml on self-hosted installsServices and applications owned by a team
Personal access token (PAT)ml_pat_A signed-in person, in the console (Gateway → Access → Personal Access Tokens)Your own scripts, notebooks and local tools; acts as you
Virtual account token (VAT)ml_vat_A signed-in person holding gateway.serviceaccounts.manage (Gateway → Access → Virtual Accounts)Production workloads that need their own model and provider allow-list and scheduled rotation
OIDC JWT(JWT)Your identity providerCallers that already hold an SSO token
Console sessionml_session cookieConsole sign-inThe console and its Playground
An API key belongs to a team and carries a role (member, editor or admin; default member). team and name are required. Optional fields are rpm_limit, tpm_limit, budget_usd_monthly, budget_reset_period (daily, weekly, monthly or never), expires_at (RFC 3339) and user_id (an owner in the same team). Pass workspace_id to bind the key to a workspace: it then resolves chat models from that workspace’s console providers. Without it, the key is team-scoped and resolves from gateway.yaml. The plaintext key is returned once, in the creation response.Key management is a control-plane call on the console host, made by a signed-in person or a credential allowed to manage Gateway API keys:
curl https://app.manylayers.io/admin/keys \
  -H "Authorization: Bearer $ML_PAT" -H "Content-Type: application/json" \
  -d '{"team": "platform", "name": "checkout-service", "role": "member", "expires_at": "2027-01-01T00:00:00Z"}'
Self-hosted installs can also seed keys under teams[].api_keys in gateway.yaml (name, key, role), with ${ENV_VAR} interpolation for the key value.
A PAT authenticates as the person who created it, with their roles, in the one workspace it was issued for. If their access is revoked, so is the token’s. PATs can be issued or rotated only from a signed-in console session, never with another token, and every PAT must have an expiry. The organization’s credential policy caps the lifetime and the number of PATs per person.
A virtual account is a service identity in a workspace with optional allowed_models and allowed_providers lists. A request for a model outside the list is refused with 403 model_not_allowed. Its tokens can be rotated on a schedule with a grace period. See Credentials.
The gateway verifies the token’s signature, issuer and audience, then maps a claim to a team by name. A token whose team claim is missing or maps to no team is rejected. If the role claim matches admin_role, the caller is an admin; otherwise a member.

Key expiry

API keys, PATs and VATs can carry an expires_at timestamp. From that moment the credential is refused with 401 key_expired rather than invalid_api_key, so a client can tell “rotate your credential” apart from “your credential is wrong”. PATs always expire. A VAT may be set never to expire if the organization’s credential policy allows it. An expired OIDC JWT fails signature verification and returns invalid_api_key.

Acting as a PAT from the console

X-ManyLayers-Credential
string
The id of one of your own PATs. Accepted only from a signed-in console session. The request is then authenticated and attributed as that PAT, so its limits, usage and traces match the snippet the Playground generates. The header is never forwarded upstream.
Sending it with a token instead of a session returns 400 credential_header_not_allowed. Naming a PAT that is not yours, or is no longer usable, returns 401 invalid_api_key.

Configure OIDC

Self-hosted installs enable OIDC under auth.oidc in gateway.yaml. An empty issuer disables it, and audience is required when issuer is set.
gateway.yaml
auth:
  oidc:
    issuer: https://login.example.com/     # expected iss claim (required)
    audience: manylayers-gateway           # expected aud claim (required with issuer)
    jwks_url: ""                           # static JWKS URL; empty = OIDC discovery from issuer
    team_claim: team                       # claim carrying the team (default "team")
    team_mapping:                          # optional: claim value -> gateway team name
      eng-platform: platform
    role_claim: role                       # claim carrying the role (default "role")
    admin_role: admin                      # role value that grants admin (default "admin")
    spa_client_id: ""                      # client id for console SSO; empty hides the SSO button
Set jwks_url for air-gapped installs. With a static JWKS URL, the gateway makes no discovery call at startup.

Use the Anthropic SDK

import anthropic

client = anthropic.Anthropic(
    base_url="https://app.manylayers.io",  # the SDK appends /v1/messages
    auth_token="ml-...",               # sent as Authorization: Bearer
)

Error responses

Authentication errors use the OpenAI error envelope:
{"error": {"message": "invalid or missing API key", "type": "invalid_request_error", "code": "invalid_api_key"}}
StatuscodeCause
401invalid_api_keyNo credential, an unknown or disabled credential, or an OIDC token that fails verification
401key_expiredThe credential’s expires_at has passed
400credential_header_not_allowedX-ManyLayers-Credential sent without a console session
403product_not_entitledThe caller’s organization does not hold the AI Gateway product (type permission_error)
403model_not_allowedThe credential’s team or virtual account may not use the requested model (type permission_error)
Verified identities are cached for up to 30 seconds. Revoking a key or changing a role is announced to every process immediately, so the change applies without waiting for the cache to expire.

Next steps

Access control

Roles, permissions and model allow-lists.

Credentials

Manage PATs, virtual accounts and rotation.