> ## Documentation Index
> Fetch the complete documentation index at: https://dev-docs.altrum.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Send a virtual key with every request to the AI Gateway, and, if your organization allows it, your own provider key.

Every request to the AI Gateway carries a [virtual key](/ai-gateway/virtual-keys). A virtual key starts with `sk-alt-` and tells Altrum which organization, AI system or person the request belongs to. You create it in Altrum and see its value once.

## Send your key

Send it as a bearer token, as every OpenAI and Anthropic SDK does when you give it an API key:

```bash theme={null}
curl https://api.altrum.ai/v1/models \
  -H "Authorization: Bearer $ALTRUM_VIRTUAL_KEY"
```

Altrum also reads the key from the headers other clients use, so you do not have to change how a client sends it:

| Header | Sent by |
| - | - |
| `Authorization: Bearer sk-alt-...` | OpenAI SDKs, most HTTP clients |
| `x-api-key: sk-alt-...` | Anthropic SDKs and Claude Code |
| `x-goog-api-key: sk-alt-...` | Gemini SDKs and Gemini CLI |
| `api-key: sk-alt-...` | Azure OpenAI SDKs |
| `x-alt-vk: sk-alt-...` | Anything, and required when you send your own provider key |

Keep the key out of source code. Read it from an environment variable or your secret store.

## When the key is refused

| Status | Code | Message |
| - | - | - |
| 401 | `virtual_key_required` | `virtual key is required. Send it as Authorization: Bearer sk-alt-..., or in the x-api-key or x-alt-vk header.` |
| 401 | `access_not_found` | `access not found. The provided credential does not exist or has been revoked.` |
| 403 | `access_blocked` | `virtual key is inactive` or `virtual key has expired` |

A key is inactive when someone switched it off in Altrum, and expired when its end date passed. See [Virtual keys](/ai-gateway/virtual-keys). Every other refusal is listed in [Errors](/api-reference/errors).

## Send your own provider key

Some teams want an application to use its own model provider key rather than the one stored in Altrum. This is off unless an owner or admin switches on **Callers may send their own provider key** under **AI Gateway**, **Settings**, **Access**.

When it is on, send three things:

* `x-alt-direct-key: true`;
* your provider key in `Authorization: Bearer`, `x-api-key` or `x-goog-api-key`;
* your virtual key in `x-alt-vk`, so Altrum knows whose request it is.

```bash theme={null}
curl https://api.altrum.ai/v1/chat/completions \
  -H "x-alt-direct-key: true" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "x-alt-vk: $ALTRUM_VIRTUAL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Say hello."}]}'
```

Altrum never stores or logs a provider key sent this way. Guardrails, budgets and limits still apply.

| Status | Code | Message |
| - | - | - |
| 401 | `policy_access_denied` | `a direct provider key must be sent with a virtual key in x-alt-vk, so Altrum knows which organization it is for.` |
| 403 | `policy_access_denied` | `direct provider keys are not enabled for this organization. An owner can turn them on in the AI Gateway settings.` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.