Authentication

A key from the cabinet, the two accepted headers, and what 401 and 402 actually mean.

There is one authentication scheme for the whole gateway: a key in a header, and nothing else. No request signing, no OAuth, no per-modality secret.

Where a key comes from

A key is created in the cabinet, on the "Keys" page (app.teamtoken.store/keys): shown in full when created, revealable there later. It starts with sk-.

An account may hold several keys — that is how services are separated: a compromised one is deleted, the others keep working. The money stays shared; a spend limit caps the key rather than handing it a wallet.

# Держи ключ в окружении, а не в коде / keep the key in your environment, not in code:
export TEAMTOKEN_KEY="sk-…"

Two headers, and both are real

A key is accepted in two forms: Authorization: Bearer sk-… (preferred, every SDK knows it) and x-api-key: sk-… (for clients that cannot send Authorization). No route behaves differently; when both are sent, Authorization wins.

POST requests require Content-Type: application/json; the rest of the rules sit next to the quickstart on /docs.

curl https://api.teamtoken.store/v1/models \
  -H "Authorization: Bearer $TEAMTOKEN_KEY"
curl https://api.teamtoken.store/v1/models \
  -H "x-api-key: $TEAMTOKEN_KEY"

What answers without a key

The catalog and the status are public: /cabinet/api/public/models, /media-models and /model-status all answer anonymously — callable even from a build script, with no key next to them.

A key spends real money

A key is access to the balance, so it belongs neither in a frontend nor in a public repository: code in a browser hands the key over with itself, and a key in the commit history works long after the line was deleted.

If a key leaks, delete it in the cabinet (app.teamtoken.store/keys) and issue a new one. Handing one over — set its spend limit before, not after the incident.

401 — no key, or not our key

On the endpoints that need a key, a 401 means: no key in the request, or the key is not ours. Check Authorization: Bearer or x-api-key.

A malformed header lands here too: "Bearer" without the space, "Token" instead of "Bearer", a key in a query parameter — indistinguishable from a request with no key.

402 — not enough for this request's worst case

A 402 means: the balance does not cover this request's worst-case price. The worst case, not an average: input tokens estimated plus the output ceiling, each at its own rate and with a margin; the ceiling comes from the request (max_tokens), else from the model's catalog row.

Consequence: on a thin balance a ceiling of thousands of tokens gets a 402 while a ceiling of two hundred goes through. The refusal body names the remainder and the request's possible price.

A 402 is about money, not the key: the key works, the balance ran out, a top-up clears the refusal. Media answers 402 the same way — per image or per second of video. One exception: a byte-identical replay inside a short TTL is served from the idempotency cache even on an empty wallet. The cache covers chat, Responses and embeddings; the Anthropic shape (/v1/messages) has none, so a repeat there is billed again.

The key, the balance and each request's cost

Money belongs to the account, not the key: however many keys are issued, they spend one balance. GET /v1/balance returns it behind the same key, in-flight holds included; each answer's own cost is on /docs/getting-started, step 4.