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

# Authentication

> Create, copy, send, rotate, and retire a RunInfra API key.

Use a workspace API key in an `Authorization: Bearer` header on every endpoint. The Anthropic-compatible routes, `/v1/messages` and `/v1/messages/count_tokens`, also accept `x-api-key`. So do `GET /v1/models` and `GET /v1/models/{model}`. A non-blank `x-api-key` wins when both headers are present on those routes.

## Get a key

<Steps>
  <Step title="Open the model page, or Settings">
    Select **Get API key** on a page in the [Model Library](https://runinfra.ai/inference-api), or select **Create API key** in [**Settings > API keys**](https://runinfra.ai/settings/api-keys). Both paths give you a workspace key for hosted Model APIs.
  </Step>

  <Step title="Copy the key">
    Copy it from the dialog. You can copy it again whenever you need it with the copy button on its row in [**Settings > API keys**](https://runinfra.ai/settings/api-keys). The member who created a key and the workspace owner can copy it, other members and viewers cannot, and every copy is recorded in the audit log.

    Keys made before copying existed show a disabled copy button. Create a new key, or **Rotate** this one, to get a copyable key; after a rotation the old key keeps working for up to 24 hours.
  </Step>

  <Step title="Set a spending limit or an expiration under Limits, or accept the defaults">
    A key follows your workspace's current rate allocation, including later upgrades. A key that already carries a custom per-key rate limit keeps it, clamped to the workspace maximum.

    A monthly spending limit is off until you set one (minimum \$1.00). Once the key's settled spend in the UTC month (it resets on the 1st) reaches it, pay-as-you-go requests on that key get `402 spend_limit_reached` with the limit, the spend and the reset time. Other keys and the credit balance are unaffected; change it with **Set limit** in the key's row menu.

    New keys have no scheduled expiry by default, but rotating a key gives the previous key a limited grace period. When you do turn expiration on the form starts at 90 days, and you can choose 30, 60, 90 days or 1 year.
  </Step>
</Steps>

## Coding plan and Credits only

Your plan covers chat (chat completions, messages and responses) from every workspace key except keys marked **Credits only**, and embeddings and rerank whenever those models are available.

An owner can set **Credits only** at [**Settings > API keys**](https://runinfra.ai/settings/api-keys). The setting is off by default and survives rotation. It bypasses the coding plan, Standby, and the credits cap after plan limits. Normal wallet and key controls still apply.

On a serving coding plan, a per-key spending limit blocks credits after plan limits. It does not stop requests paid by the plan or eligible Standby usage. Use **Credits only** when a key must always follow pay-as-you-go credit controls. See [Coding plan](/docs/introduction/coding-plan).

## Send it

```bash theme={"dark"}
curl https://api.runinfra.ai/v1/chat/completions \
  -H "Authorization: Bearer $RUNINFRA_GATEWAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nemotron-3-5-lightning-30b",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
```

## Key format

Every key is `rp_` followed by 40 lowercase letters or digits.

```text theme={"dark"}
rp_abcdefghijklmnopqrstuvwxyz0123456789abcd
└┬┘└──────────── 40 chars, a-z and 0-9 ───┘
prefix
```

The stored digest and encrypted copy both depend on a secret held outside the database, so a copy of the database alone cannot reveal a usable key. Revoking a key erases its encrypted copy. If Settings marks a key as an older format, rotate it from that row and the replacement uses the current one.

## Workspace keys

Use a **workspace-scoped** key with `https://api.runinfra.ai/v1` and select a hosted model with the `model` field.

A legacy key scoped to one deployment returns `400 auth_error` here. Replace it with a workspace key.

## Rotate, revoke, expire

<div className="block dark:hidden">
  <svg viewBox="0 0 540 180" width="100%" role="img" aria-label="The API key lifecycle: create a key and copy it whenever you need it, rotate to issue a new one, run both while traffic drains, then revoke the old key which answers 403; expiration separately makes a key answer 401 after the expiry you set." fill="none" xmlns="http://www.w3.org/2000/svg"><text x="24" y="16" fill="#6e6d64" fontFamily="Consolas, Menlo, monospace" fontSize="9" fontWeight="500" letterSpacing="0.3">Key lifecycle</text><text x="516" y="16" fill="#78786f" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="end">no downtime to replace a key</text><line x1="24" y1="72" x2="516" y2="72" stroke="#e8e8e3" strokeWidth="1" strokeDasharray="3 3" /><rect x="21.5" y="69.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><rect x="513.5" y="69.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><rect x="83.5" y="69.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><line x1="86" y1="75" x2="86" y2="88" stroke="#e8e8e3" strokeWidth="1" strokeDasharray="3 3" /><text x="86" y="102" fill="#0f0f0e" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">create</text><text x="86" y="119" fill="#78786f" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">copy it again any time</text><rect x="206.5" y="69.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><line x1="209" y1="75" x2="209" y2="88" stroke="#e8e8e3" strokeWidth="1" strokeDasharray="3 3" /><text x="209" y="102" fill="#0f0f0e" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">rotate</text><text x="209" y="119" fill="#78786f" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">a new secret is issued</text><rect x="329.5" y="69.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><line x1="332" y1="75" x2="332" y2="88" stroke="#e8e8e3" strokeWidth="1" strokeDasharray="3 3" /><text x="332" y="102" fill="#0f0f0e" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">both valid</text><text x="332" y="119" fill="#78786f" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">deploy and drain traffic</text><rect x="452.5" y="69.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><line x1="455" y1="75" x2="455" y2="88" stroke="#e8e8e3" strokeWidth="1" strokeDasharray="3 3" /><text x="455" y="102" fill="#0f0f0e" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">revoke old</text><text x="455" y="119" fill="#78786f" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">403 on the old key</text><line x1="24" y1="142" x2="516" y2="142" stroke="#e8e8e3" strokeWidth="1" strokeDasharray="3 3" /><rect x="21.5" y="139.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><rect x="513.5" y="139.5" width="5" height="5" fill="#bbb9b1" shapeRendering="crispEdges" /><rect x="24" y="156" width="5" height="5" fill="#b07f24" shapeRendering="crispEdges" /><text x="36" y="164" fill="#b07f24" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0">expiration</text><text x="108" y="164" fill="#52524c" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0">runs on its own clock. After the expiry you set, the key answers 401.</text></svg>
</div>

<div className="hidden dark:block">
  <svg viewBox="0 0 540 180" width="100%" role="img" aria-label="The API key lifecycle: create a key and copy it whenever you need it, rotate to issue a new one, run both while traffic drains, then revoke the old key which answers 403; expiration separately makes a key answer 401 after the expiry you set." fill="none" xmlns="http://www.w3.org/2000/svg"><text x="24" y="16" fill="#8f8e83" fontFamily="Consolas, Menlo, monospace" fontSize="9" fontWeight="500" letterSpacing="0.3">Key lifecycle</text><text x="516" y="16" fill="#9a998e" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="end">no downtime to replace a key</text><line x1="24" y1="72" x2="516" y2="72" stroke="#383833" strokeWidth="1" strokeDasharray="3 3" /><rect x="21.5" y="69.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><rect x="513.5" y="69.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><rect x="83.5" y="69.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><line x1="86" y1="75" x2="86" y2="88" stroke="#383833" strokeWidth="1" strokeDasharray="3 3" /><text x="86" y="102" fill="#f0efe2" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">create</text><text x="86" y="119" fill="#9a998e" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">copy it again any time</text><rect x="206.5" y="69.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><line x1="209" y1="75" x2="209" y2="88" stroke="#383833" strokeWidth="1" strokeDasharray="3 3" /><text x="209" y="102" fill="#f0efe2" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">rotate</text><text x="209" y="119" fill="#9a998e" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">a new secret is issued</text><rect x="329.5" y="69.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><line x1="332" y1="75" x2="332" y2="88" stroke="#383833" strokeWidth="1" strokeDasharray="3 3" /><text x="332" y="102" fill="#f0efe2" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">both valid</text><text x="332" y="119" fill="#9a998e" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">deploy and drain traffic</text><rect x="452.5" y="69.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><line x1="455" y1="75" x2="455" y2="88" stroke="#383833" strokeWidth="1" strokeDasharray="3 3" /><text x="455" y="102" fill="#f0efe2" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0" textAnchor="middle">revoke old</text><text x="455" y="119" fill="#9a998e" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0" textAnchor="middle">403 on the old key</text><line x1="24" y1="142" x2="516" y2="142" stroke="#383833" strokeWidth="1" strokeDasharray="3 3" /><rect x="21.5" y="139.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><rect x="513.5" y="139.5" width="5" height="5" fill="#6e6d64" shapeRendering="crispEdges" /><rect x="24" y="156" width="5" height="5" fill="#d9a64a" shapeRendering="crispEdges" /><text x="36" y="164" fill="#d9a64a" fontFamily="Consolas, Menlo, monospace" fontSize="10.5" letterSpacing="0">expiration</text><text x="108" y="164" fill="#c8c7ba" fontFamily="'Helvetica Neue', Helvetica, Arial, sans-serif" fontSize="10.5" letterSpacing="0">runs on its own clock. After the expiry you set, the key answers 401.</text></svg>
</div>

Rotation keeps the previous key working for up to 24 hours by default, or until its own expiry if that comes sooner, and **Revoke** ends that grace period. For no downtime, choose **Rotate** from the key's row menu with **Revoke previous key immediately** clear, deploy the new key, let traffic drain, then choose **Revoke** on the previous key. Check **Revoke previous key immediately** when the previous key must stop at once.

| Response | Meaning | What to do |
| - | - | - |
| `403` `API key is deactivated` | The key was revoked. It stays deactivated after archival. | Use the new key. |
| `401` `Invalid API key` | The key is unknown or permanently deleted. | Create a key. |
| `401` `API key has expired` | The key passed the expiry you set. | Rotate it or create a new key. |
| `400` `api_key_rotation_overlap_limit` | The rotation would leave more than 20 live keys, twice the cap of 10. Nothing changed. | Revoke an unused key, then retry. |
| `503` `api_key_mint_busy` with `Retry-After: 2` | Another key operation is still running. Nothing changed. | Wait 2 seconds and send the same request again. |

Branch on codes, never on message text: the `401` and `403` carry `error.type` and `error.code`, the `400` and `503` key operations a top-level `code`. During a rate-limit store outage, a revoked key can keep working for up to about 30 seconds. A rotation also revokes the earlier keys in its own rotation history that are still live; other older keys stay live and count toward the 20. Expiration is checked on every request, so an expired key stops working the moment it expires.

## Environment variables

```bash theme={"dark"}
export RUNINFRA_GATEWAY_KEY=rp_...
export RUNINFRA_API_KEY=$RUNINFRA_GATEWAY_KEY
export RUNINFRA_BASE_URL=https://api.runinfra.ai/v1
export OPENAI_API_KEY=$RUNINFRA_GATEWAY_KEY
export OPENAI_BASE_URL=$RUNINFRA_BASE_URL
export ANTHROPIC_BASE_URL=https://api.runinfra.ai
export ANTHROPIC_AUTH_TOKEN=$RUNINFRA_GATEWAY_KEY
```

`RUNINFRA_API_KEY` is the name the [RunInfra CLI](/docs/tools-sdks/connect) and most coding-agent integrations read. Mistral Vibe, Oh My Pi and Goose read their own variable instead, such as `RUNINFRA_GOOSE_API_KEY`, and so does Crush set up with `--key-source env`; setup writes it for you. It is not an alias for `RUNINFRA_GATEWAY_KEY`, so set both, as the block above does.

Most OpenAI clients pick up `OPENAI_API_KEY` on their own, so the block above maps it, with `OPENAI_BASE_URL`, to RunInfra. Anthropic clients use the bare host because they append `/v1`. `ANTHROPIC_AUTH_TOKEN` sends Bearer auth. `ANTHROPIC_API_KEY` also works through `x-api-key` on the Messages routes and `GET /v1/models`. Use a separate key per environment so one leak has a bounded blast radius.

[RightNow Agent](/docs/tools-sdks/rightnow) reads `RIGHTNOW_API_KEY` first, then `RUNINFRA_GATEWAY_KEY`. A nonempty environment credential overrides its stored key.

## Security posture

<Columns cols={2}>
  <Card title="Hashed and encrypted at rest" icon="shield-check">
    A versioned HMAC-SHA-256 digest authenticates requests, and an AES-256-GCM copy bound to its key lets you copy it again. The plaintext is never stored unencrypted.
  </Card>

  <Card title="Constant-time compare" icon="shield-check">
    Lookup is by indexed hash and the match is constant-time, so timing cannot enumerate keys.
  </Card>

  <Card title="Audit log" icon="file-text">
    Key lifecycle changes, every copy of a key, and attributable authentication failures are logged for SOC 2 CC6.6.
  </Card>

  <Card title="Per-key rate limit" icon="gauge">
    A sliding 60 second window per key, or a fixed 60 second window on each server during a rate-limit store outage. If the rate-limit store is not configured, the request is refused with `503 limiter_unavailable` rather than admitted, and nothing is charged.
  </Card>

  <Card title="Per-key spending limit" icon="wallet">
    An optional monthly budget on one key, enforced on settled spend and reset on the first of the month (UTC). The refusal is `402 spend_limit_reached` with the limit, the spend, and the reset instant.
  </Card>
</Columns>

## Related

<Columns cols={3}>
  <Card title="Model APIs quickstart" icon="rocket" href="/docs/api-reference/model-apis-quickstart">
    Make your first call with the key you just created.
  </Card>

  <Card title="Chat completions" icon="braces" href="/docs/api-reference/chat-completions">
    The full request contract.
  </Card>

  <Card title="Rate limits" icon="gauge" href="/docs/api-reference/rate-limits">
    What the per-key limit you set actually governs.
  </Card>
</Columns>


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