---
title: "Keys – Superbot API"
canonical_url: "https://superbot.gg/developers/keys"
summary: "Mint, list, edit and revoke scoped caller keys."
---

> Markdown of https://superbot.gg/developers/keys. Site index: /llms.txt.

# Keys

Mint, list, edit and revoke scoped caller keys.

Server: `https://superbot.gg`; every path below starts at it. Errors: [https://superbot.gg/developers#errors](https://superbot.gg/developers#errors). Spec: [https://superbot.gg/docs/openapi.json](https://superbot.gg/docs/openapi.json).

## Operations

- [List caller keys](https://superbot.gg/developers/keys/listKeys.md): `GET /v1/keys`
- [Create a caller key](https://superbot.gg/developers/keys/createKey.md): `POST /v1/keys`
- [Get the calling key](https://superbot.gg/developers/keys/getCurrentKey.md): `GET /v1/keys/whoami`
- [Update a caller key](https://superbot.gg/developers/keys/updateKey.md): `PATCH /v1/keys/{id}`
- [Revoke a caller key](https://superbot.gg/developers/keys/revokeKey.md): `DELETE /v1/keys/{id}`
- [Rotate a caller key](https://superbot.gg/developers/keys/rotateKey.md): `POST /v1/keys/{id}/rotate`

## List caller keys

`GET /v1/keys`

Every caller key on the account, revoked ones included. Never the hash. A caller key reads the list without `person_email`, `prompt_history` and `person_limit_usd`, which are the account owner's; a person key lists only its own person's keys.

- Operation id: `listKeys`
- Group: [Keys](https://superbot.gg/developers/keys.md)
- Auth: A caller key holding `keys:read` (`Authorization: Bearer sbc_…` or `x-api-key: sbc_…`), or a signed-in session.
- Scopes: `keys:read`

### Response 200

The keys.

`application/json`: CallerKeyList.

- `object` (string, required): One of `list`.
- `data` (array of CallerKey, required)
  - `id` (string, required)
  - `user_id` (string, required)
  - `name` (string, required): What the tool calls itself, 1-64 chars.
  - `mask` (string, required): The only form shown after mint.
  - `created_at` (string (date-time), required)
  - `last_used_at` (string (date-time))
  - `revoked_at` (string (date-time))
  - `daily_limit_tokens` (number): Daily spend cap in whole credits.
  - `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
  - `caps_unit` (string): One of `credit`.
  - `allowed_models` (array of string): Glob allow-list over model names; absent = every model. With models_listed: the exact canonical ids the key may dial, and empty = none.
  - `models_listed` (boolean): true = the listed reading: allowed_models are literal canonical ids (no globs), the key lists exactly those as its per-app picker names, and an empty list allows nothing.
  - `model_pins` (object): Canonical model id to the stable account id it is pinned to: a pinned model dials only that account, and one id pinned to two accounts gets two picker names.
  - `model_aliases` (object): Canonical model id (a member of allowed_models) to the custom name its picker shows: 1-64 characters of letters, numbers, dots, dashes or colons; no Cursor routing word (claude, gemini, anthropic, sonnet-, opus-, haiku-, grok, composer); not a house id (superbot, superbot-fast, superbot-omni); unique among this key's forwards; not another model's id. A refused name stores nothing. A custom name replaces the default name for that model.
  - `app` (string): Forwarding app this listed key's model names are spelled for: cursor, copilot-cli, cline, roo, qwen-code, hermes, openclaw, zazen, default. Set at creation, cannot be changed. Absent = cursor.
  - `default_model` (string): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni` or a model of one of your own accounts. A call that names any other model is dialled as named. Absent = a call naming `superbot` gets `superbot`.
  - `route` (string, required): One of `byo`, `credits`, `auto`.
  - `mode` (string, required): One of `model`, `agent`.
  - `incognito` (boolean, required)
  - `device_id` (string)
  - `org_id` (string): Set on a team key.
  - `person_id` (string): Set on a key minted for a person: `per_` + 16 hex. Every key minted for the same person carries the same id. A person key has no device_id and no org_id, and is billed to the owner.
  - `person_name` (string): The person's display name, 1-64 characters. Present on every person key.
  - `person_email` (string): The person's email address, when the owner gave one.
  - `person_group` (string): A free-text grouping the owner sorts people by (a team, a class), 1-64 characters.
  - `prompt_history` (PromptHistory): What the owner told a person about their prompts, and how long they are kept. Absent on a key until the owner sets it: the person's prompts are then not visible to the owner and follow the account's own retention.
    - `visible_to_owner` (boolean, required): The owner may read this person's prompts.
    - `retention_days` (integer, required): Whole days the prompts are kept, 1-3650.
    - `attested_at` (string (date-time), required): ISO 8601 time the owner attested the person was told. The owner's own claim, stored as given and not verified; the answer normalises it to UTC.
  - `paused_at` (string (date-time)): Set while the owner holds this person: until a PATCH clears it the key answers 403 `key_paused` at the model gateway, the `/v1/keys` routes, the relay door and the Cosmos door; every other route does not recognise a held key and answers 401 `unauthorized`. Held is not revoked. It is written to every live key of the person.
  - `person_limit_usd` (number): The owner's monthly spend limit for this person, in US dollars to the cent. Set by the owner's `PUT /v1/keys/person/{person_id}/limit` (the Helper), written to every live key of the person, and absent while no limit is set.
  - `scopes` (array of Scope, required): The EFFECTIVE scopes: stored list or the legacy set, team keys capped.
- `has_more` (boolean, required)
- `first_id` (string | null, required): Id of the first item on this page; pass as `before_id` for the previous page.
- `last_id` (string | null, required): Id of the last item on this page; pass as `after_id` for the next page.
- `keys` (array of CallerKey, required): Legacy: the same rows as `data`.
  - `id` (string, required)
  - `user_id` (string, required)
  - `name` (string, required): What the tool calls itself, 1-64 chars.
  - `mask` (string, required): The only form shown after mint.
  - `created_at` (string (date-time), required)
  - `last_used_at` (string (date-time))
  - `revoked_at` (string (date-time))
  - `daily_limit_tokens` (number): Daily spend cap in whole credits.
  - `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
  - `caps_unit` (string): One of `credit`.
  - `allowed_models` (array of string): Glob allow-list over model names; absent = every model. With models_listed: the exact canonical ids the key may dial, and empty = none.
  - `models_listed` (boolean): true = the listed reading: allowed_models are literal canonical ids (no globs), the key lists exactly those as its per-app picker names, and an empty list allows nothing.
  - `model_pins` (object): Canonical model id to the stable account id it is pinned to: a pinned model dials only that account, and one id pinned to two accounts gets two picker names.
  - `model_aliases` (object): Canonical model id (a member of allowed_models) to the custom name its picker shows: 1-64 characters of letters, numbers, dots, dashes or colons; no Cursor routing word (claude, gemini, anthropic, sonnet-, opus-, haiku-, grok, composer); not a house id (superbot, superbot-fast, superbot-omni); unique among this key's forwards; not another model's id. A refused name stores nothing. A custom name replaces the default name for that model.
  - `app` (string): Forwarding app this listed key's model names are spelled for: cursor, copilot-cli, cline, roo, qwen-code, hermes, openclaw, zazen, default. Set at creation, cannot be changed. Absent = cursor.
  - `default_model` (string): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni` or a model of one of your own accounts. A call that names any other model is dialled as named. Absent = a call naming `superbot` gets `superbot`.
  - `route` (string, required): One of `byo`, `credits`, `auto`.
  - `mode` (string, required): One of `model`, `agent`.
  - `incognito` (boolean, required)
  - `device_id` (string)
  - `org_id` (string): Set on a team key.
  - `person_id` (string): Set on a key minted for a person: `per_` + 16 hex. Every key minted for the same person carries the same id. A person key has no device_id and no org_id, and is billed to the owner.
  - `person_name` (string): The person's display name, 1-64 characters. Present on every person key.
  - `person_email` (string): The person's email address, when the owner gave one.
  - `person_group` (string): A free-text grouping the owner sorts people by (a team, a class), 1-64 characters.
  - `prompt_history` (PromptHistory): What the owner told a person about their prompts, and how long they are kept. Absent on a key until the owner sets it: the person's prompts are then not visible to the owner and follow the account's own retention.
    - `visible_to_owner` (boolean, required): The owner may read this person's prompts.
    - `retention_days` (integer, required): Whole days the prompts are kept, 1-3650.
    - `attested_at` (string (date-time), required): ISO 8601 time the owner attested the person was told. The owner's own claim, stored as given and not verified; the answer normalises it to UTC.
  - `paused_at` (string (date-time)): Set while the owner holds this person: until a PATCH clears it the key answers 403 `key_paused` at the model gateway, the `/v1/keys` routes, the relay door and the Cosmos door; every other route does not recognise a held key and answers 401 `unauthorized`. Held is not revoked. It is written to every live key of the person.
  - `person_limit_usd` (number): The owner's monthly spend limit for this person, in US dollars to the cent. Set by the owner's `PUT /v1/keys/person/{person_id}/limit` (the Helper), written to every live key of the person, and absent while no limit is set.
  - `scopes` (array of Scope, required): The EFFECTIVE scopes: stored list or the legacy set, team keys capped.

### Errors

Every refusal carries `error`, `message`, `doc_url` and `request_id`; the gateway routes answer in the dialect you dialed.

| Status | Codes | When |
| --- | --- | --- |
| 401 | [`unauthorized`](https://superbot.gg/developers#errors-unauthorized) | The bearer is missing, malformed or unknown. |
| 403 | [`insufficient_scope`](https://superbot.gg/developers#errors-insufficient_scope) | The key lacks a scope (insufficient_scope), is revoked, or may not call this route. |

### Examples

```sh
curl -sS -X GET "https://superbot.gg/v1/keys" \
  -H "Authorization: Bearer $SUPERBOT_API_KEY"
```

```js
const res = await fetch(`https://superbot.gg/v1/keys`, {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.SUPERBOT_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

```python
import os
import requests

res = requests.request(
    "GET",
    "https://superbot.gg/v1/keys",
    headers={
        "Authorization": f"Bearer {os.environ['SUPERBOT_API_KEY']}",
    },
)
print(res.status_code, res.json())
```

## Create a caller key

`POST /v1/keys`

Mint a key. The plaintext is returned once. A caller key can only mint keys within its own reach: no scope, route, cap or model it does not hold. With `person` the key is minted for someone else and billed to you: the row carries `person_id`, `person_name` and the rest, and `name` defaults to the person's name. A bad `person` answers 400 `invalid_person`. Only the account owner (a signed-in session, a device or the account key) mints a key for a person: a caller key that sends `person` answers 403 `caller_key_forbidden`, and so does a person key minting any key. A person key minted with no `scopes` or `preset` holds `gateway:invoke` alone. A paused caller key answers 403 `key_paused`.

- Operation id: `createKey`
- Group: [Keys](https://superbot.gg/developers/keys.md)
- Auth: A caller key holding `keys:write` (`Authorization: Bearer sbc_…` or `x-api-key: sbc_…`), or a signed-in session.
- Scopes: `keys:write`

### Request body

`application/json`, required.

- `name` (string): What the tool calls itself, 1-64 chars. Required unless `person` is given, then it defaults to the person's name.
- `person` (KeyPerson): The person a key is minted for. Without `id` it makes a new person (`name` required); with `id` it adds a key to that person. A person key stamps no device (the minting device's logout never revokes it), takes no `org_id` and is billed to the owner.
  - `name` (string): The person's display name, 1-64 printable characters (no `<`, `>`, `"` or `'`). Required for a new person; with `id` it must equal the stored name or be left out.
  - `email` (string): The person's email address. With `id` it must equal the stored one or be left out.
  - `group` (string): A free-text grouping, 1-64 characters. With `id` it must equal the stored one or be left out.
  - `id` (string): The `person_id` of a person already on this account: adds another key for them, which inherits the stored name, email, group, prompt_history and paused_at (a paused person's new key is born paused). Rotating a key is a mint with the same `id`, then a revoke of the old key.
- `route` (string): One of `byo`, `credits`, `auto`.
- `mode` (string): One of `model`, `agent`.
- `daily_limit_tokens` (number): Daily spend cap in whole credits.
- `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
- `allowed_models` (array of string): Glob allow-list over model names; with models_listed, the exact canonical ids (no globs).
- `models_listed` (boolean): true = the listed reading: the key dials exactly its allowed_models, listed as the per-app picker names; empty = none. A PATCH that omits it leaves it as it was.
- `model_pins` (object | null): Canonical model id to the stable account id it is pinned to (non-empty strings). A PATCH replaces the whole map; null clears it.
- `model_aliases` (object | null): Canonical model id (a member of allowed_models) to the custom name its picker shows (1-64 characters: letters, numbers, dots, dashes, colons). A PATCH replaces the whole map; null clears it. A refused name stores nothing and answers the reason. Removing a model from allowed_models drops its custom name.
- `default_model` (string | null): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni`, or a model of one of your own accounts (`claude-sonnet-5-5`, `gpt-5.5`, `custom/<endpoint>/<id>`). A call that names any other model is dialled as named. `null` or `superbot` clears it. A model of your own account needs a key on route `auto` or `byo` in mode `model` whose `allowed_models` admits it; a key on route `credits` takes a house model only, and a key with `models_listed` takes none. The write is judged on the key as it will be stored, so a PATCH that would leave the default unusable (route credits, mode agent, a narrower list) answers 400 `invalid_default_model`: send `default_model: null` with it. A PATCH that omits it leaves it as it was.
- `incognito` (boolean)
- `scopes` (array of Scope): Explicit scopes; wins over `preset`.
- `app` (string): Forwarding app this listed key's model names are spelled for: cursor, copilot-cli, cline, roo, qwen-code, hermes, openclaw, zazen, default. Set at creation, cannot be changed. Absent = cursor.
- `preset` (string): A named scope set. One of `gateway`, `read_only`, `full`.

### Response 201

The new key.

`application/json`: CreatedKey.

- `key` (string, required): The plaintext `sbc_…` key, shown once.
- `row` (CallerKey, required)
  - `id` (string, required)
  - `user_id` (string, required)
  - `name` (string, required): What the tool calls itself, 1-64 chars.
  - `mask` (string, required): The only form shown after mint.
  - `created_at` (string (date-time), required)
  - `last_used_at` (string (date-time))
  - `revoked_at` (string (date-time))
  - `daily_limit_tokens` (number): Daily spend cap in whole credits.
  - `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
  - `caps_unit` (string): One of `credit`.
  - `allowed_models` (array of string): Glob allow-list over model names; absent = every model. With models_listed: the exact canonical ids the key may dial, and empty = none.
  - `models_listed` (boolean): true = the listed reading: allowed_models are literal canonical ids (no globs), the key lists exactly those as its per-app picker names, and an empty list allows nothing.
  - `model_pins` (object): Canonical model id to the stable account id it is pinned to: a pinned model dials only that account, and one id pinned to two accounts gets two picker names.
  - `model_aliases` (object): Canonical model id (a member of allowed_models) to the custom name its picker shows: 1-64 characters of letters, numbers, dots, dashes or colons; no Cursor routing word (claude, gemini, anthropic, sonnet-, opus-, haiku-, grok, composer); not a house id (superbot, superbot-fast, superbot-omni); unique among this key's forwards; not another model's id. A refused name stores nothing. A custom name replaces the default name for that model.
  - `app` (string): Forwarding app this listed key's model names are spelled for: cursor, copilot-cli, cline, roo, qwen-code, hermes, openclaw, zazen, default. Set at creation, cannot be changed. Absent = cursor.
  - `default_model` (string): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni` or a model of one of your own accounts. A call that names any other model is dialled as named. Absent = a call naming `superbot` gets `superbot`.
  - `route` (string, required): One of `byo`, `credits`, `auto`.
  - `mode` (string, required): One of `model`, `agent`.
  - `incognito` (boolean, required)
  - `device_id` (string)
  - `org_id` (string): Set on a team key.
  - `person_id` (string): Set on a key minted for a person: `per_` + 16 hex. Every key minted for the same person carries the same id. A person key has no device_id and no org_id, and is billed to the owner.
  - `person_name` (string): The person's display name, 1-64 characters. Present on every person key.
  - `person_email` (string): The person's email address, when the owner gave one.
  - `person_group` (string): A free-text grouping the owner sorts people by (a team, a class), 1-64 characters.
  - `prompt_history` (PromptHistory): What the owner told a person about their prompts, and how long they are kept. Absent on a key until the owner sets it: the person's prompts are then not visible to the owner and follow the account's own retention.
    - `visible_to_owner` (boolean, required): The owner may read this person's prompts.
    - `retention_days` (integer, required): Whole days the prompts are kept, 1-3650.
    - `attested_at` (string (date-time), required): ISO 8601 time the owner attested the person was told. The owner's own claim, stored as given and not verified; the answer normalises it to UTC.
  - `paused_at` (string (date-time)): Set while the owner holds this person: until a PATCH clears it the key answers 403 `key_paused` at the model gateway, the `/v1/keys` routes, the relay door and the Cosmos door; every other route does not recognise a held key and answers 401 `unauthorized`. Held is not revoked. It is written to every live key of the person.
  - `person_limit_usd` (number): The owner's monthly spend limit for this person, in US dollars to the cent. Set by the owner's `PUT /v1/keys/person/{person_id}/limit` (the Helper), written to every live key of the person, and absent while no limit is set.
  - `scopes` (array of Scope, required): The EFFECTIVE scopes: stored list or the legacy set, team keys capped.

### Errors

Every refusal carries `error`, `message`, `doc_url` and `request_id`; the gateway routes answer in the dialect you dialed.

| Status | Codes | When |
| --- | --- | --- |
| 400 | [`invalid_request`](https://superbot.gg/developers#errors-invalid_request) | The request is malformed or a parameter is invalid; `param` names the field. |
| 401 | [`unauthorized`](https://superbot.gg/developers#errors-unauthorized) | The bearer is missing, malformed or unknown. |
| 403 | [`insufficient_scope`](https://superbot.gg/developers#errors-insufficient_scope) | The key lacks a scope (insufficient_scope), is revoked, or may not call this route. |

### Examples

```sh
curl -sS -X POST "https://superbot.gg/v1/keys" \
  -H "Authorization: Bearer $SUPERBOT_API_KEY" \
  -H "content-type: application/json" \
  -d '{"name":"your-name","person":{"name":"your-name","email":"user@example.com","group":"your-group"},"route":"byo"}'
```

```js
const res = await fetch(`https://superbot.gg/v1/keys`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SUPERBOT_API_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({"name":"your-name","person":{"name":"your-name","email":"user@example.com","group":"your-group"},"route":"byo"}),
});
console.log(res.status, await res.json());
```

```python
import os
import requests

res = requests.request(
    "POST",
    "https://superbot.gg/v1/keys",
    headers={
        "Authorization": f"Bearer {os.environ['SUPERBOT_API_KEY']}",
        "content-type": "application/json",
    },
    json={"name":"your-name","person":{"name":"your-name","email":"user@example.com","group":"your-group"},"route":"byo"},
)
print(res.status_code, res.json())
```

## Get the calling key

`GET /v1/keys/whoami`

The calling caller key’s own row and effective scopes, and its own metered usage in three windows (`usage`). A person key’s fields ride on `row`. Caller keys only; a session gets 403 caller_key_required. A revoked key gets 403 revoked_key, a paused one 403 key_paused.

- Operation id: `getCurrentKey`
- Group: [Keys](https://superbot.gg/developers/keys.md)
- Auth: Any live caller key (`Authorization: Bearer sbc_…` or `x-api-key: sbc_…`).
- Scopes: none

### Response 200

The key.

`application/json`: CurrentKey.

- `row` (CallerKey, required)
  - `id` (string, required)
  - `user_id` (string, required)
  - `name` (string, required): What the tool calls itself, 1-64 chars.
  - `mask` (string, required): The only form shown after mint.
  - `created_at` (string (date-time), required)
  - `last_used_at` (string (date-time))
  - `revoked_at` (string (date-time))
  - `daily_limit_tokens` (number): Daily spend cap in whole credits.
  - `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
  - `caps_unit` (string): One of `credit`.
  - `allowed_models` (array of string): Glob allow-list over model names; absent = every model. With models_listed: the exact canonical ids the key may dial, and empty = none.
  - `models_listed` (boolean): true = the listed reading: allowed_models are literal canonical ids (no globs), the key lists exactly those as its per-app picker names, and an empty list allows nothing.
  - `model_pins` (object): Canonical model id to the stable account id it is pinned to: a pinned model dials only that account, and one id pinned to two accounts gets two picker names.
  - `model_aliases` (object): Canonical model id (a member of allowed_models) to the custom name its picker shows: 1-64 characters of letters, numbers, dots, dashes or colons; no Cursor routing word (claude, gemini, anthropic, sonnet-, opus-, haiku-, grok, composer); not a house id (superbot, superbot-fast, superbot-omni); unique among this key's forwards; not another model's id. A refused name stores nothing. A custom name replaces the default name for that model.
  - `app` (string): Forwarding app this listed key's model names are spelled for: cursor, copilot-cli, cline, roo, qwen-code, hermes, openclaw, zazen, default. Set at creation, cannot be changed. Absent = cursor.
  - `default_model` (string): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni` or a model of one of your own accounts. A call that names any other model is dialled as named. Absent = a call naming `superbot` gets `superbot`.
  - `route` (string, required): One of `byo`, `credits`, `auto`.
  - `mode` (string, required): One of `model`, `agent`.
  - `incognito` (boolean, required)
  - `device_id` (string)
  - `org_id` (string): Set on a team key.
  - `person_id` (string): Set on a key minted for a person: `per_` + 16 hex. Every key minted for the same person carries the same id. A person key has no device_id and no org_id, and is billed to the owner.
  - `person_name` (string): The person's display name, 1-64 characters. Present on every person key.
  - `person_email` (string): The person's email address, when the owner gave one.
  - `person_group` (string): A free-text grouping the owner sorts people by (a team, a class), 1-64 characters.
  - `prompt_history` (PromptHistory): What the owner told a person about their prompts, and how long they are kept. Absent on a key until the owner sets it: the person's prompts are then not visible to the owner and follow the account's own retention.
    - `visible_to_owner` (boolean, required): The owner may read this person's prompts.
    - `retention_days` (integer, required): Whole days the prompts are kept, 1-3650.
    - `attested_at` (string (date-time), required): ISO 8601 time the owner attested the person was told. The owner's own claim, stored as given and not verified; the answer normalises it to UTC.
  - `paused_at` (string (date-time)): Set while the owner holds this person: until a PATCH clears it the key answers 403 `key_paused` at the model gateway, the `/v1/keys` routes, the relay door and the Cosmos door; every other route does not recognise a held key and answers 401 `unauthorized`. Held is not revoked. It is written to every live key of the person.
  - `person_limit_usd` (number): The owner's monthly spend limit for this person, in US dollars to the cent. Set by the owner's `PUT /v1/keys/person/{person_id}/limit` (the Helper), written to every live key of the person, and absent while no limit is set.
  - `scopes` (array of Scope, required): The EFFECTIVE scopes: stored list or the legacy set, team keys capped.
- `scopes` (array of Scope, required)
- `usage` (KeyUsage, required): This key's own metered usage in three windows: `day` is the trailing 24 hours (the daily cap's window), `week` the trailing 7 days, `month` the current UTC calendar month (the monthly cap's window). Each window reports `requests` (every metered request), `prompts` (the requests that were the person's own text), `tokens` and `billed_usd`.
  - `day` (KeyUsageWindow, required)
    - `requests` (integer, required): Distinct metered requests (a request that dialed several times counts once). Every request the key made is one, an agent's tool legs, a title helper and a probe included: the call volume, not what the person asked.
    - `prompts` (integer, required): Distinct answered prompts: requests whose last message was the person's own text and that were answered (the gateway marks the request's row only then), counted once per request id. A tool leg, a title helper, a probe and a refused or failed call are requests but not prompts. Only the dials the gateway wrote a row for are read, so the relay door's turns are not counted here.
    - `prompts_truncated` (boolean): Present (true) when `prompts` is a floor, not the window's count: the read of this key's rows behind it stopped at its row ceiling before it reached the window's start, or the read failed. `prompts` then counts the newest prompts the read reached. Absent when the read covered the whole window. One of `true`.
    - `tokens` (number, required): Every metered leg: fresh input, output and both cache legs.
    - `billed_usd` (number, required): What the window's calls are worth to the caps and the usage pages, USD: the router's settle once read back, else the metered list value.
    - `clients` (object): Distinct requests of the window made by Zapier or n8n, counted from the `x-superbot-client` tag (`zapier/<v>`, `n8n/<v>`) the request carried. A platform with no request in the window is absent, and the member is absent when neither made one.
      - `zapier` (integer)
      - `n8n` (integer)
  - `week` (KeyUsageWindow, required)
    - `requests` (integer, required): Distinct metered requests (a request that dialed several times counts once). Every request the key made is one, an agent's tool legs, a title helper and a probe included: the call volume, not what the person asked.
    - `prompts` (integer, required): Distinct answered prompts: requests whose last message was the person's own text and that were answered (the gateway marks the request's row only then), counted once per request id. A tool leg, a title helper, a probe and a refused or failed call are requests but not prompts. Only the dials the gateway wrote a row for are read, so the relay door's turns are not counted here.
    - `prompts_truncated` (boolean): Present (true) when `prompts` is a floor, not the window's count: the read of this key's rows behind it stopped at its row ceiling before it reached the window's start, or the read failed. `prompts` then counts the newest prompts the read reached. Absent when the read covered the whole window. One of `true`.
    - `tokens` (number, required): Every metered leg: fresh input, output and both cache legs.
    - `billed_usd` (number, required): What the window's calls are worth to the caps and the usage pages, USD: the router's settle once read back, else the metered list value.
    - `clients` (object): Distinct requests of the window made by Zapier or n8n, counted from the `x-superbot-client` tag (`zapier/<v>`, `n8n/<v>`) the request carried. A platform with no request in the window is absent, and the member is absent when neither made one.
      - `zapier` (integer)
      - `n8n` (integer)
  - `month` (KeyUsageWindow, required)
    - `requests` (integer, required): Distinct metered requests (a request that dialed several times counts once). Every request the key made is one, an agent's tool legs, a title helper and a probe included: the call volume, not what the person asked.
    - `prompts` (integer, required): Distinct answered prompts: requests whose last message was the person's own text and that were answered (the gateway marks the request's row only then), counted once per request id. A tool leg, a title helper, a probe and a refused or failed call are requests but not prompts. Only the dials the gateway wrote a row for are read, so the relay door's turns are not counted here.
    - `prompts_truncated` (boolean): Present (true) when `prompts` is a floor, not the window's count: the read of this key's rows behind it stopped at its row ceiling before it reached the window's start, or the read failed. `prompts` then counts the newest prompts the read reached. Absent when the read covered the whole window. One of `true`.
    - `tokens` (number, required): Every metered leg: fresh input, output and both cache legs.
    - `billed_usd` (number, required): What the window's calls are worth to the caps and the usage pages, USD: the router's settle once read back, else the metered list value.
    - `clients` (object): Distinct requests of the window made by Zapier or n8n, counted from the `x-superbot-client` tag (`zapier/<v>`, `n8n/<v>`) the request carried. A platform with no request in the window is absent, and the member is absent when neither made one.
      - `zapier` (integer)
      - `n8n` (integer)

### Errors

Every refusal carries `error`, `message`, `doc_url` and `request_id`; the gateway routes answer in the dialect you dialed.

| Status | Codes | When |
| --- | --- | --- |
| 401 | [`unauthorized`](https://superbot.gg/developers#errors-unauthorized) | The bearer is missing, malformed or unknown. |
| 403 | [`insufficient_scope`](https://superbot.gg/developers#errors-insufficient_scope) | The key lacks a scope (insufficient_scope), is revoked, or may not call this route. |
| 404 | [`not_found`](https://superbot.gg/developers#errors-not_found) | No such resource on this account. |

### Examples

```sh
curl -sS -X GET "https://superbot.gg/v1/keys/whoami" \
  -H "Authorization: Bearer $SUPERBOT_API_KEY"
```

```js
const res = await fetch(`https://superbot.gg/v1/keys/whoami`, {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.SUPERBOT_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

```python
import os
import requests

res = requests.request(
    "GET",
    "https://superbot.gg/v1/keys/whoami",
    headers={
        "Authorization": f"Bearer {os.environ['SUPERBOT_API_KEY']}",
    },
)
print(res.status_code, res.json())
```

## Update a caller key

`PATCH /v1/keys/{id}`

Edit a key’s name, route, mode, caps, models, default model or scopes. A bad `default_model` answers 400 `invalid_default_model`. On a person key, `person_name`, `person_email`, `person_group`, `prompt_history` and `paused_at` edit the person and are written to every live key of that person; on any other key they answer 400 `not_a_person_key`, and an invalid value answers 400 `invalid_person`. A person key is edited by the account owner only, and a person field is changed by the account owner only: a caller key that tries either answers 403 `caller_key_forbidden`. A person key cannot be renamed `account`. A paused caller key answers 403 `key_paused`.

- Operation id: `updateKey`
- Group: [Keys](https://superbot.gg/developers/keys.md)
- Auth: A caller key holding `keys:write` (`Authorization: Bearer sbc_…` or `x-api-key: sbc_…`), or a signed-in session.
- Scopes: `keys:write`

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | required |  |

### Request body

`application/json`, required.

- `name` (string)
- `route` (string): One of `byo`, `credits`, `auto`.
- `mode` (string): One of `model`, `agent`.
- `daily_limit_tokens` (number): Daily spend cap in whole credits.
- `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
- `allowed_models` (array of string): Glob allow-list over model names; with models_listed, the exact canonical ids (no globs).
- `models_listed` (boolean): true = the listed reading: the key dials exactly its allowed_models, listed as the per-app picker names; empty = none. A PATCH that omits it leaves it as it was.
- `model_pins` (object | null): Canonical model id to the stable account id it is pinned to (non-empty strings). A PATCH replaces the whole map; null clears it.
- `model_aliases` (object | null): Canonical model id (a member of allowed_models) to the custom name its picker shows (1-64 characters: letters, numbers, dots, dashes, colons). A PATCH replaces the whole map; null clears it. A refused name stores nothing and answers the reason. Removing a model from allowed_models drops its custom name.
- `default_model` (string | null): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni`, or a model of one of your own accounts (`claude-sonnet-5-5`, `gpt-5.5`, `custom/<endpoint>/<id>`). A call that names any other model is dialled as named. `null` or `superbot` clears it. A model of your own account needs a key on route `auto` or `byo` in mode `model` whose `allowed_models` admits it; a key on route `credits` takes a house model only, and a key with `models_listed` takes none. The write is judged on the key as it will be stored, so a PATCH that would leave the default unusable (route credits, mode agent, a narrower list) answers 400 `invalid_default_model`: send `default_model: null` with it. A PATCH that omits it leaves it as it was.
- `incognito` (boolean)
- `scopes` (array of Scope): Explicit scopes; wins over `preset`.
- `person_name` (string): Rename the person (1-64 printable characters). Cannot be cleared. Does not rename the key (`name`).
- `person_email` (string | null): Set the person's email address; null clears it.
- `person_group` (string | null): Set the person's group; null clears it.
- `prompt_history` (PromptHistory & object | null)
  - `visible_to_owner` (boolean, required): The owner may read this person's prompts.
  - `retention_days` (integer, required): Whole days the prompts are kept, 1-3650.
  - `attested_at` (string (date-time), required): ISO 8601 time the owner attested the person was told. The owner's own claim, stored as given and not verified; the answer normalises it to UTC.
- `paused_at` (boolean | string | null): `true` pauses the person now (the edge stamps the time), an ISO 8601 time pauses from then (not in the future), `false` or null resumes. A paused person's keys answer 403 `key_paused` at the model gateway, the `/v1/keys` routes, the relay door and the Cosmos door; every other route does not recognise a held key and answers 401 `unauthorized`.

### Response 200

The edited key.

`application/json`: UpdatedKey.

- `row` (CallerKey, required)
  - `id` (string, required)
  - `user_id` (string, required)
  - `name` (string, required): What the tool calls itself, 1-64 chars.
  - `mask` (string, required): The only form shown after mint.
  - `created_at` (string (date-time), required)
  - `last_used_at` (string (date-time))
  - `revoked_at` (string (date-time))
  - `daily_limit_tokens` (number): Daily spend cap in whole credits.
  - `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
  - `caps_unit` (string): One of `credit`.
  - `allowed_models` (array of string): Glob allow-list over model names; absent = every model. With models_listed: the exact canonical ids the key may dial, and empty = none.
  - `models_listed` (boolean): true = the listed reading: allowed_models are literal canonical ids (no globs), the key lists exactly those as its per-app picker names, and an empty list allows nothing.
  - `model_pins` (object): Canonical model id to the stable account id it is pinned to: a pinned model dials only that account, and one id pinned to two accounts gets two picker names.
  - `model_aliases` (object): Canonical model id (a member of allowed_models) to the custom name its picker shows: 1-64 characters of letters, numbers, dots, dashes or colons; no Cursor routing word (claude, gemini, anthropic, sonnet-, opus-, haiku-, grok, composer); not a house id (superbot, superbot-fast, superbot-omni); unique among this key's forwards; not another model's id. A refused name stores nothing. A custom name replaces the default name for that model.
  - `app` (string): Forwarding app this listed key's model names are spelled for: cursor, copilot-cli, cline, roo, qwen-code, hermes, openclaw, zazen, default. Set at creation, cannot be changed. Absent = cursor.
  - `default_model` (string): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni` or a model of one of your own accounts. A call that names any other model is dialled as named. Absent = a call naming `superbot` gets `superbot`.
  - `route` (string, required): One of `byo`, `credits`, `auto`.
  - `mode` (string, required): One of `model`, `agent`.
  - `incognito` (boolean, required)
  - `device_id` (string)
  - `org_id` (string): Set on a team key.
  - `person_id` (string): Set on a key minted for a person: `per_` + 16 hex. Every key minted for the same person carries the same id. A person key has no device_id and no org_id, and is billed to the owner.
  - `person_name` (string): The person's display name, 1-64 characters. Present on every person key.
  - `person_email` (string): The person's email address, when the owner gave one.
  - `person_group` (string): A free-text grouping the owner sorts people by (a team, a class), 1-64 characters.
  - `prompt_history` (PromptHistory): What the owner told a person about their prompts, and how long they are kept. Absent on a key until the owner sets it: the person's prompts are then not visible to the owner and follow the account's own retention.
    - `visible_to_owner` (boolean, required): The owner may read this person's prompts.
    - `retention_days` (integer, required): Whole days the prompts are kept, 1-3650.
    - `attested_at` (string (date-time), required): ISO 8601 time the owner attested the person was told. The owner's own claim, stored as given and not verified; the answer normalises it to UTC.
  - `paused_at` (string (date-time)): Set while the owner holds this person: until a PATCH clears it the key answers 403 `key_paused` at the model gateway, the `/v1/keys` routes, the relay door and the Cosmos door; every other route does not recognise a held key and answers 401 `unauthorized`. Held is not revoked. It is written to every live key of the person.
  - `person_limit_usd` (number): The owner's monthly spend limit for this person, in US dollars to the cent. Set by the owner's `PUT /v1/keys/person/{person_id}/limit` (the Helper), written to every live key of the person, and absent while no limit is set.
  - `scopes` (array of Scope, required): The EFFECTIVE scopes: stored list or the legacy set, team keys capped.

### Errors

Every refusal carries `error`, `message`, `doc_url` and `request_id`; the gateway routes answer in the dialect you dialed.

| Status | Codes | When |
| --- | --- | --- |
| 400 | [`invalid_request`](https://superbot.gg/developers#errors-invalid_request) | The request is malformed or a parameter is invalid; `param` names the field. |
| 401 | [`unauthorized`](https://superbot.gg/developers#errors-unauthorized) | The bearer is missing, malformed or unknown. |
| 403 | [`insufficient_scope`](https://superbot.gg/developers#errors-insufficient_scope) | The key lacks a scope (insufficient_scope), is revoked, or may not call this route. |
| 404 | [`not_found`](https://superbot.gg/developers#errors-not_found) | No such resource on this account. |

### Examples

```sh
curl -sS -X PATCH "https://superbot.gg/v1/keys/$ID" \
  -H "Authorization: Bearer $SUPERBOT_API_KEY" \
  -H "content-type: application/json" \
  -d '{"name":"your-name","route":"byo","mode":"model"}'
```

```js
const id = 'ID';
const res = await fetch(`https://superbot.gg/v1/keys/${id}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.SUPERBOT_API_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({"name":"your-name","route":"byo","mode":"model"}),
});
console.log(res.status, await res.json());
```

```python
import os
import requests

id = "ID"
res = requests.request(
    "PATCH",
    f"https://superbot.gg/v1/keys/{id}",
    headers={
        "Authorization": f"Bearer {os.environ['SUPERBOT_API_KEY']}",
        "content-type": "application/json",
    },
    json={"name":"your-name","route":"byo","mode":"model"},
)
print(res.status_code, res.json())
```

## Revoke a caller key

`DELETE /v1/keys/{id}`

Revoke a key. Revoking an already-revoked key is a no-op (`already: true`). A person key is revoked by the account owner only; a caller key that tries answers 403 `caller_key_forbidden`.

- Operation id: `revokeKey`
- Group: [Keys](https://superbot.gg/developers/keys.md)
- Auth: A caller key holding `keys:write` (`Authorization: Bearer sbc_…` or `x-api-key: sbc_…`), or a signed-in session.
- Scopes: `keys:write`

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | required |  |

### Response 200

Revoked.

`application/json`: RevokedKey.

- `ok` (boolean, required): One of `true`.
- `already` (boolean)

### Errors

Every refusal carries `error`, `message`, `doc_url` and `request_id`; the gateway routes answer in the dialect you dialed.

| Status | Codes | When |
| --- | --- | --- |
| 401 | [`unauthorized`](https://superbot.gg/developers#errors-unauthorized) | The bearer is missing, malformed or unknown. |
| 403 | [`insufficient_scope`](https://superbot.gg/developers#errors-insufficient_scope) | The key lacks a scope (insufficient_scope), is revoked, or may not call this route. |
| 404 | [`not_found`](https://superbot.gg/developers#errors-not_found) | No such resource on this account. |

### Examples

```sh
curl -sS -X DELETE "https://superbot.gg/v1/keys/$ID" \
  -H "Authorization: Bearer $SUPERBOT_API_KEY"
```

```js
const id = 'ID';
const res = await fetch(`https://superbot.gg/v1/keys/${id}`, {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.SUPERBOT_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

```python
import os
import requests

id = "ID"
res = requests.request(
    "DELETE",
    f"https://superbot.gg/v1/keys/{id}",
    headers={
        "Authorization": f"Bearer {os.environ['SUPERBOT_API_KEY']}",
    },
)
print(res.status_code, res.json())
```

## Rotate a caller key

`POST /v1/keys/{id}/rotate`

Replace a key with a new one of the same shape (name, scopes, route, mode, caps, models, pins, aliases, default model, incognito, the person it is for, the device that minted it) and revoke the old one. The new key is minted first and the old one revoked after, so a failure never leaves the account without a working key. The reply carries the new key in plaintext once (`key`), its row, and `revoked`, the id of the key revoked (`null` if that revoke failed after the mint: the new key is live, so revoke the old one with `DELETE /v1/keys/{id}`). No body. A key that is revoked already answers 409 `revoked`; an id this account does not hold answers 404 `not_found`; a team key answers 400 `team_key` (rotate it on the team pane). A person key is rotated by the account owner only; a caller key that tries answers 403 `caller_key_forbidden`. The new key has made no calls yet, so it lists under Zapier or n8n again with its first call.

- Operation id: `rotateKey`
- Group: [Keys](https://superbot.gg/developers/keys.md)
- Auth: A caller key holding `keys:write` (`Authorization: Bearer sbc_…` or `x-api-key: sbc_…`), or a signed-in session.
- Scopes: `keys:write`

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | required |  |

### Response 201

The new key, once.

`application/json`: RotatedKey.

- `key` (string, required): The new key in plaintext. Shown once.
- `row` (CallerKey, required)
  - `id` (string, required)
  - `user_id` (string, required)
  - `name` (string, required): What the tool calls itself, 1-64 chars.
  - `mask` (string, required): The only form shown after mint.
  - `created_at` (string (date-time), required)
  - `last_used_at` (string (date-time))
  - `revoked_at` (string (date-time))
  - `daily_limit_tokens` (number): Daily spend cap in whole credits.
  - `monthly_limit_tokens` (number): Monthly spend cap in whole credits.
  - `caps_unit` (string): One of `credit`.
  - `allowed_models` (array of string): Glob allow-list over model names; absent = every model. With models_listed: the exact canonical ids the key may dial, and empty = none.
  - `models_listed` (boolean): true = the listed reading: allowed_models are literal canonical ids (no globs), the key lists exactly those as its per-app picker names, and an empty list allows nothing.
  - `model_pins` (object): Canonical model id to the stable account id it is pinned to: a pinned model dials only that account, and one id pinned to two accounts gets two picker names.
  - `model_aliases` (object): Canonical model id (a member of allowed_models) to the custom name its picker shows: 1-64 characters of letters, numbers, dots, dashes or colons; no Cursor routing word (claude, gemini, anthropic, sonnet-, opus-, haiku-, grok, composer); not a house id (superbot, superbot-fast, superbot-omni); unique among this key's forwards; not another model's id. A refused name stores nothing. A custom name replaces the default name for that model.
  - `app` (string): Forwarding app this listed key's model names are spelled for: cursor, copilot-cli, cline, roo, qwen-code, hermes, openclaw, zazen, default. Set at creation, cannot be changed. Absent = cursor.
  - `default_model` (string): The model a call that names the bare house agent `superbot` (the default model of Zapier and n8n), or names no model, is answered by instead: `superbot-fast`, `superbot-omni` or a model of one of your own accounts. A call that names any other model is dialled as named. Absent = a call naming `superbot` gets `superbot`.
  - `route` (string, required): One of `byo`, `credits`, `auto`.
  - `mode` (string, required): One of `model`, `agent`.
  - `incognito` (boolean, required)
  - `device_id` (string)
  - `org_id` (string): Set on a team key.
  - `person_id` (string): Set on a key minted for a person: `per_` + 16 hex. Every key minted for the same person carries the same id. A person key has no device_id and no org_id, and is billed to the owner.
  - `person_name` (string): The person's display name, 1-64 characters. Present on every person key.
  - `person_email` (string): The person's email address, when the owner gave one.
  - `person_group` (string): A free-text grouping the owner sorts people by (a team, a class), 1-64 characters.
  - `prompt_history` (PromptHistory): What the owner told a person about their prompts, and how long they are kept. Absent on a key until the owner sets it: the person's prompts are then not visible to the owner and follow the account's own retention.
    - `visible_to_owner` (boolean, required): The owner may read this person's prompts.
    - `retention_days` (integer, required): Whole days the prompts are kept, 1-3650.
    - `attested_at` (string (date-time), required): ISO 8601 time the owner attested the person was told. The owner's own claim, stored as given and not verified; the answer normalises it to UTC.
  - `paused_at` (string (date-time)): Set while the owner holds this person: until a PATCH clears it the key answers 403 `key_paused` at the model gateway, the `/v1/keys` routes, the relay door and the Cosmos door; every other route does not recognise a held key and answers 401 `unauthorized`. Held is not revoked. It is written to every live key of the person.
  - `person_limit_usd` (number): The owner's monthly spend limit for this person, in US dollars to the cent. Set by the owner's `PUT /v1/keys/person/{person_id}/limit` (the Helper), written to every live key of the person, and absent while no limit is set.
  - `scopes` (array of Scope, required): The EFFECTIVE scopes: stored list or the legacy set, team keys capped.
- `revoked` (string | null, required): The id of the key that was revoked, or null if that revoke failed after the mint.

### Errors

Every refusal carries `error`, `message`, `doc_url` and `request_id`; the gateway routes answer in the dialect you dialed.

| Status | Codes | When |
| --- | --- | --- |
| 400 | [`invalid_request`](https://superbot.gg/developers#errors-invalid_request) | The request is malformed or a parameter is invalid; `param` names the field. |
| 401 | [`unauthorized`](https://superbot.gg/developers#errors-unauthorized) | The bearer is missing, malformed or unknown. |
| 403 | [`insufficient_scope`](https://superbot.gg/developers#errors-insufficient_scope) | The key lacks a scope (insufficient_scope), is revoked, or may not call this route. |
| 404 | [`not_found`](https://superbot.gg/developers#errors-not_found) | No such resource on this account. |
| 409 | [`conflict`](https://superbot.gg/developers#errors-conflict) | The resource is not in a state that allows this change. |

### Examples

```sh
curl -sS -X POST "https://superbot.gg/v1/keys/$ID/rotate" \
  -H "Authorization: Bearer $SUPERBOT_API_KEY"
```

```js
const id = 'ID';
const res = await fetch(`https://superbot.gg/v1/keys/${id}/rotate`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SUPERBOT_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

```python
import os
import requests

id = "ID"
res = requests.request(
    "POST",
    f"https://superbot.gg/v1/keys/{id}/rotate",
    headers={
        "Authorization": f"Bearer {os.environ['SUPERBOT_API_KEY']}",
    },
)
print(res.status_code, res.json())
```
