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

# Authentication

> Bearer keys, the x-api-key alias, and what happens when a key is revoked.

Every request to the inference API carries an API key. There is no unauthenticated
endpoint, not even `/v1/models`.

## The header

```bash theme={null}
Authorization: Bearer $PRENTIS_API_KEY
```

On `/v1/messages` the Anthropic-style header is accepted instead, so an Anthropic SDK
works without touching its auth code:

```bash theme={null}
x-api-key: $PRENTIS_API_KEY
```

Both name the same key. Sending both is fine as long as they match.

## Where keys come from

Keys are created in the console under **API Keys**, and belong to an account. A key looks
like `mk-prod-` followed by 32 random characters.

<Warning>
  The plaintext is shown **once, at creation**. We store a hash of it and the first eight
  characters for display — there is no screen, and no support process, that can recover a
  key after that. If you lose one, revoke it and create another.
</Warning>

That is the whole reason the snippets on this site read `PRENTIS_API_KEY` from the
environment instead of carrying a key inline: a key that ends up in a repository has to be
revoked, and you cannot un-publish it.

## Revoking

Revocation takes effect for new requests within seconds. A request made with a revoked key
gets:

```json theme={null}
{
  "error": {
    "message": "Invalid or revoked API key.",
    "type": "authentication_error",
    "code": "INVALID_API_KEY",
    "param": null
  }
}
```

A `401 INVALID_API_KEY` is not retryable — nothing about waiting changes it. It is also
never billed.

In-flight requests are not cancelled by a revocation. If you are revoking a key because it
leaked, treat anything already accepted as already served.

## What a key may do

A key inherits the account it was created in, and the account decides which models it can
reach. Asking for a model the account may not use is a `403`:

| Code                 | When                                                                               |
| -------------------- | ---------------------------------------------------------------------------------- |
| `MODEL_NOT_ALLOWED`  | The model exists, but this key's account may not call it.                          |
| `MODEL_NOT_FOUND`    | No model by that name is visible to this account at all (`404`).                   |
| `TENANT_SUSPENDED`   | The account is suspended — check billing in the console.                           |
| `AGREEMENT_REQUIRED` | Someone with authority in your organization has to accept the current terms first. |

The split between the first two matters when you are debugging: a `404` means the name is
wrong or the model was never available to you; a `403` means the name is right and the
permission is not.

## Keeping keys separate

One key per deployment, rather than one key everywhere, is worth the small amount of extra
setup:

* Revoking a leaked key takes one service offline instead of all of them.
* Per-request logs in the console show which key made each call, so you can tell staging
  traffic from production traffic without adding anything to your requests.

If you want to correlate calls with your own users on top of that, send a `user` field (or
`metadata.user_id` on `/v1/messages`). It is echoed into this request's log line and is not
kept on the usage record.

## Next

<CardGroup cols={2}>
  <Card title="Rate limits and quotas" icon="gauge" href="/rate-limits-and-quotas">
    What throttles you once the key works.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/errors">
    Every code, whether to retry it, and whether it is billed.
  </Card>
</CardGroup>
