skip to content
superbot.gg

Create a caller key

Part of Keys, 6 operations. The overview covers keys, scopes and errors.

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.

A caller key holding keys:write (Authorization: Bearer sbc_… or x-api-key: sbc_…), or a signed-in session.

Operation id createKey in Keys.

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.

    4 fields
    • 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. One of gateway:invoke, usage:read, billing:read, keys:read, keys:write, endpoints:read, endpoints:write, teams:read, teams:write, devices:read, devices:write, runs:read, runs:write, mcp:invoke, turns:read.

  • 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
    29 fields
    • 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.

      3 fields
      • 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. One of gateway:invoke, usage:read, billing:read, keys:read, keys:write, endpoints:read, endpoints:write, teams:read, teams:write, devices:read, devices:write, runs:read, runs:write, mcp:invoke, turns:read.

Errors

StatusCodeWhat to change
400invalid_request

A parameter or body field is invalid; param names it and issues lists every problem.

401unauthorized

Send a live caller key or session as Authorization: Bearer … or x-api-key. No key yet: create one at /account/api, or sign a user in at /auth.md.

403insufficient_scope

The key lacks the scope in required_scopes; mint a key that holds it.

view .mdopenapi
Request

curl

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":"[email protected]","group":"your-group"},"route":"byo"}'

JavaScript

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":"[email protected]","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":"[email protected]","group":"your-group"},"route":"byo"},
)
print(res.status_code, res.json())
Response

201 The new key.

{
  "key": "your-key",
  "row": {
    "id": "ck_9f2c41d0",
    "user_id": "your-user-id",
    "name": "your-name",
    "mask": "sbc_ab12…cd34",
    "created_at": "2026-09-01T00:00:00Z",
    "last_used_at": "2026-09-01T00:00:00Z",
    "revoked_at": "2026-09-01T00:00:00Z",
    "daily_limit_tokens": 0,
    "monthly_limit_tokens": 0,
    "caps_unit": "credit",
    "allowed_models": [
      "your-allowed-models"
    ],
    "models_listed": true,
    "model_pins": {},
    "model_aliases": {},
    "app": "your-app",
    "default_model": "your-default-model",
    "route": "byo",
    "mode": "model",
    "incognito": true,
    "device_id": "your-device-id",
    "org_id": "your-org-id",
    "person_id": "per_9f2c41d0a7b35e68",
    "person_name": "your-person-name",
    "person_email": "[email protected]",
    "person_group": "your-person-group",
    "prompt_history": {
      "visible_to_owner": true,
      "retention_days": 0,
      "attested_at": "2026-09-01T00:00:00Z"
    },
    "paused_at": "2026-09-01T00:00:00Z",
    "person_limit_usd": 0,
    "scopes": [
      "gateway:invoke"
    ]
  }
}

400 The request is malformed or a parameter is invalid; `param` names the field.

{
  "error": "invalid_request",
  "message": "A parameter or body field is invalid; param names it and issues lists every problem.",
  "doc_url": "https://superbot.gg/developers#errors-invalid_request",
  "request_id": "req_5f1c0a9e2b7d4c3a1e0f9b8d"
}

401 The bearer is missing, malformed or unknown.

{
  "error": "unauthorized",
  "message": "Send a live caller key or session as Authorization: Bearer … or x-api-key. No key yet: create one at /account/api, or sign a user in at /auth.md.",
  "doc_url": "https://superbot.gg/developers#errors-unauthorized",
  "request_id": "req_5f1c0a9e2b7d4c3a1e0f9b8d"
}

403 The key lacks a scope (insufficient_scope), is revoked, or may not call this route.

{
  "error": "insufficient_scope",
  "message": "The key lacks the scope in required_scopes; mint a key that holds it.",
  "doc_url": "https://superbot.gg/developers#errors-insufficient_scope",
  "request_id": "req_5f1c0a9e2b7d4c3a1e0f9b8d"
}