# Rename a key, change its scopes or lower its limits

`PATCH /v1/console/keys/{key_id}` (operation id `updateKey`)

Limits can be set at or below the account caps. A revoked key cannot be changed.

Authentication: Console session cookie (`__Host-dex_session`). State-changing routes also need the `x-dex-csrf` header.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `key_id` | path | KeyId | yes | (pattern ^key_[0-9A-HJKMNP-TV-Z]{26}$) |
| `x-dex-csrf` | header | string | yes | The `csrf_token` returned by `createSession`. Required on every state-changing console route. (32 to 64 characters) |

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no | (1 to 64 characters) |
| `scopes` | array of Scope | no | (at least 1 items; unique items; each item: one of "decide", "usage:read", "balance:read") |
| `limits` | KeyLimits | no | (no other fields) |
| `limits.requests_per_minute` | integer | no | (1 to 600) |
| `limits.tokens_per_minute` | integer | no | (1,000 to 600,000) |
| `limits.concurrency` | integer | no | (1 to 32) |
| `expires_at` | string \| null | no | (format date-time) |

## Responses

### 200

The updated key.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | KeyId | yes | (pattern ^key_[0-9A-HJKMNP-TV-Z]{26}$) |
| `object` | string | yes | (always "api_key") |
| `name` | string | yes |  |
| `mode` | string | yes | (one of "live", "test") |
| `prefix` | string | yes | The mode prefix plus the first 4 characters, for recognising the key. (pattern ^dex_(live\|test)_[0-9A-Za-z]{4}$) |
| `scopes` | array of Scope | yes | (each item: one of "decide", "usage:read", "balance:read") |
| `limits` | KeyLimits | yes | (no other fields) |
| `limits.requests_per_minute` | integer | no | (1 to 600) |
| `limits.tokens_per_minute` | integer | no | (1,000 to 600,000) |
| `limits.concurrency` | integer | no | (1 to 32) |
| `created_at` | string | yes | (format date-time) |
| `last_used_at` | string \| null | yes | (format date-time) |
| `expires_at` | string \| null | yes | (format date-time) |
| `revoked_at` | string \| null | yes | (format date-time) |

### 400

The request could not be read. Codes: `invalid_json` (not JSON, not UTF-8, or a `\u` escape that is half of a
surrogate pair, such as `"\ud800"` without its low half), `duplicate_key` (a JSON object
repeats a key; `param` is the JSON pointer of the repeated member, such as `/questions/q1/options/a`),
`unsupported_media_type` (not `application/json`) and `invalid_header` (a malformed `idempotency-key` or
`x-client-request-id`; `param` names the header).


### 401

Missing, unknown, revoked or expired credentials.

### 403

The credentials are valid but not allowed to do this (missing scope, suspended account or failed CSRF check).

### 404

The model, key or route does not exist, or the model version is retired.

### 409

The idempotency key was used with a different body, or the first request with this key is still running.

### 422

The request is well-formed JSON but breaks a validation rule. `param` names the field as a dotted path.
Codes: `unknown_field`, `missing_field`, `invalid_type` (wrong JSON type), `invalid_value` (right type, value
out of range: an empty or over-long string, a state, `instructions`, `criteria` string or option description
of only whitespace, an empty `questions` or `options` object, an empty state object or array, a pattern or
allowed-value mismatch such as a label or level with outer whitespace, a state nested deeper than 32 levels, a
bad date or date range), `field_not_allowed` (`options` or `levels` on the wrong question type),
`invalid_question_id`, `too_many_questions`, `too_many_options`, `invalid_levels` (including two levels equal
after Unicode NFC normalisation and lower-casing), `invalid_min_confidence`, `duplicate_label` (two option
labels equal after Unicode NFC normalisation and lower-casing, such as `Billing` and `billing`),
`state_path_not_found`, `state_not_json` and, on the console, `top_up_limit_exceeded`.


### 500

An unexpected error. Any charge was refunded. Retry with the same idempotency key.

### 503

`maintenance`: the console's database, sign-in or payments are unavailable for a moment. Retry after
`retry-after`. Nothing was changed.


## Examples

Illustrative values. Examples show the shape of requests and responses; the numbers in them are not measured results.

Request: tighten

```json
{
  "name": "support-router",
  "limits": {
    "requests_per_minute": 120,
    "tokens_per_minute": 120000,
    "concurrency": 8
  }
}
```

Response: A live key limited below the account caps

```json
{
  "id": "key_01M54VQCG06CQ643DZVMXXQKFB",
  "object": "api_key",
  "name": "support-router",
  "mode": "live",
  "prefix": "dex_live_Q7mK",
  "scopes": [
    "decide",
    "usage:read",
    "balance:read"
  ],
  "limits": {
    "requests_per_minute": 120,
    "tokens_per_minute": 120000,
    "concurrency": 8
  },
  "created_at": "2026-10-17T08:30:00Z",
  "last_used_at": "2026-10-20T11:59:41Z",
  "expires_at": null,
  "revoked_at": null
}
```
