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

# API keys

> Create, scope, rotate, and revoke application keys per organization, application, and environment.

Create one application key per organization, application, and environment. This provides independent access, limits, budgets, attribution, revocation, and migration rollback.

Administrative requests use the private operator origin and an admin bearer key.

```bash theme={null}
export GATEWAY_ADMIN_URL='http://127.0.0.1:4000'
export GATEWAY_ADMIN_KEY='...'
```

## Create a key

In the operator console, open **API Keys**, select **Create key**, and choose the public models or aliases the application may use. The form also scopes endpoint access, RPM/TPM, expiry, and an optional budget. The raw token appears once.

For automation, call the Admin API:

```bash theme={null}
curl -sS -X POST "$GATEWAY_ADMIN_URL/admin/keys" \
  -H "Authorization: Bearer $GATEWAY_ADMIN_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "invoice-agent-prod",
    "name": "Invoice agent production",
    "org_id": "customer-123",
    "app_id": "invoice-agent",
    "environment": "prod",
    "allowed_models": ["fast", "vision"],
    "allowed_endpoints": [
      "/v1/gateway/info",
      "/v1/models",
      "/v1/model/info",
      "/v1/chat/completions",
      "/v1/responses"
    ],
    "rpm_limit": 60,
    "tpm_limit": 100000,
    "budget_usd": "30.00",
    "budget_duration": "30d",
    "expires_at": "2027-01-01T00:00:00Z"
  }'
```

The response returns key metadata, `gateway_url`, `openai_base_url`, and a raw `token`. The raw token is returned once. Store it immediately in your application secret manager.

Creating a key with `rpm_limit` or `tpm_limit` requires Redis shared state. A single-process local deployment can instead acknowledge process-local enforcement with `shared_state.allow_process_local_throttling: true`; without Redis or that explicit acknowledgement, creation returns `409 distributed_rate_limits_required`.

### Fields

| Field                            | Required | Notes                                                                                                     |
| -------------------------------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `id`                             | No       | Generated when omitted; 1-128 letters, digits, `.`, `_`, `:`, `-`                                         |
| `name`                           | No       | Human-readable name                                                                                       |
| `org_id`                         | Yes      | Required organization owner for every admin-created key; deployment bootstrap keys are the only exception |
| `app_id` + `environment`         | Together | Environment is `local`, `dev`, `stage`, or `prod`                                                         |
| `role`                           | No       | `user` by default; use `admin` only for operators                                                         |
| `allowed_models`                 | No       | Exact active public models/aliases or matching prefix wildcards; default `*`; unknown scopes are rejected |
| `allowed_endpoints`              | No       | Exact paths or prefix wildcards; default `*`                                                              |
| `rpm_limit`                      | No       | Positive requests-per-minute limit                                                                        |
| `tpm_limit`                      | No       | Positive token-reservation-per-minute limit                                                               |
| `budget_usd` + `budget_duration` | Together | Duration is `1d`, `7d`, or `30d`                                                                          |
| `expires_at`                     | No       | ISO-8601 timestamp                                                                                        |

## List keys

```bash theme={null}
curl -sS "$GATEWAY_ADMIN_URL/admin/keys" \
  -H "Authorization: Bearer $GATEWAY_ADMIN_KEY"
```

The list contains metadata and status, never raw tokens or token hashes.

## Update a key

In the operator console, open **API Keys** and select **Edit** on an active key. Identity fields are read-only; the editable policy includes the display name, model and endpoint scopes, RPM/TPM limits, expiry, and budget.

For automation, send only the fields that should change:

```bash theme={null}
curl -sS -X PATCH \
  "$GATEWAY_ADMIN_URL/admin/keys/invoice-agent-prod" \
  -H "Authorization: Bearer $GATEWAY_ADMIN_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "allowed_models": ["fast", "vision"],
    "allowed_endpoints": [
      "/v1/gateway/info",
      "/v1/models",
      "/v1/model/info",
      "/v1/chat/completions",
      "/v1/responses"
    ],
    "rpm_limit": 120,
    "tpm_limit": 200000,
    "budget_usd": "45.00",
    "budget_duration": "30d"
  }'
```

`budget_usd` and `budget_duration` must be supplied together. Set `rpm_limit`, `tpm_limit`, or `expires_at` to `null` to clear that limit. Budget enforcement cannot currently be removed from a key; change its amount instead.

An update preserves the key ID, raw token, token hashes, tenant/application identity, role, status, budget history, spent amount, reserved amount, and current period dates. No raw token is returned. Changing the duration affects the next period; it does not move the current period deadline.

If a budget is reduced below `spent_usd + reserved_usd`, available budget becomes zero and new reservations fail with `budget_exceeded`. Existing reservations can still settle, so accounting history remains correct. Raising the limit makes the remaining amount available immediately. The console shows this impact and asks for confirmation before applying a limit below committed usage.

Editing a deployment-owned key transfers it to database ownership. Later YAML reloads do not overwrite the edited policy. Key identity fields (`id`, `org_id`, `app_id`, `environment`, and `role`) remain immutable; create a replacement key when those fields must change.

## Rotate a key

Rotation updates the credential in one database transaction and returns the replacement token once:

```bash theme={null}
curl -sS -X POST \
  "$GATEWAY_ADMIN_URL/admin/keys/invoice-agent-prod/rotate" \
  -H "Authorization: Bearer $GATEWAY_ADMIN_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"overlap_seconds": 300}'
```

`overlap_seconds` defaults to 300 and is bounded from 0 through 86,400. During overlap, current and immediately previous tokens authenticate as the same key with identical tenant, model, endpoint, rate, and budget policy. A later rotation invalidates any earlier generation. Store the new token, update the application secret, verify traffic, and then rotate again with zero overlap or revoke if immediate invalidation is required. Rotation of a deployment-owned bootstrap key transfers ownership to Postgres so YAML reload cannot restore the old token.

Raw current/previous hashes and tokens never appear in list or audit responses. `previous_token_valid_until` reports only the overlap deadline.

## Revoke a key

```bash theme={null}
curl -sS -X POST \
  "$GATEWAY_ADMIN_URL/admin/keys/invoice-agent-prod/revoke" \
  -H "Authorization: Bearer $GATEWAY_ADMIN_KEY"
```

Revocation is immediate for both the current token and any overlapping prior token. Blocked keys cannot be edited or unblocked; create a replacement key if access must be restored.

## Least-privilege recommendations

* Never share one key across production applications.
* Grant only the aliases and endpoint paths the application uses.
* Keep admin keys out of applications and CI logs.
* Add expiry to temporary and test keys.
* Set RPM/TPM and budget limits before distributing a production token.
