Skip to main content
Every request to /v1 carries a key:

Creating one

Dashboard → API keys → Create key. The secret appears once, at creation. We store an HMAC of it rather than the key itself, so there is no endpoint that can show it to you again and no support request that can recover it. A lost key is replaced, not retrieved. You can also create keys from the API, with a key that holds api_keys:write, but the first key has to come from the dashboard, because there is nothing to authenticate the request with yet.

Scopes

Keys are scoped, and there is no wildcard. agents:write does not imply agents:read; grant both if you need both.
webhooks:write can change where every event on the account is delivered. A key holding it can repoint your webhook URL at somewhere you do not control, and the events that follow will go there.
The quickstart needs sip_trunks:write, phone_numbers:write, agents:read, agents:write, calls:read, calls:write, usage:read and billing:read, which is exactly what the dashboard preselects.
billing:write authorizes spending money: it is what creates a top-up checkout. Do not put it on a key that only needs to read a balance.
phone_numbers:purchase buys numbers on your carrier account, charged by your carrier with no further step. AI clients connected over OAuth do not get it unless you tick it on the consent screen.

There is no admin scope

One cannot be created either. Everything an administrator can do is unreachable from an API key by construction, not by a permission check that could be misconfigured: the credential is stripped before it reaches those routes.

A key cannot grant what it does not hold

Creating a key with scopes the calling key lacks returns 403 scope_escalation. Otherwise a narrow key would be a step toward a broad one rather than a limit.

Rotation

A replacement is issued immediately and the old key keeps working for 24 hours. That window exists so a deploy is never caught between two keys with neither valid: ship the new one, let the old one expire.

Revocation

Immediate, with no grace period. This is what you reach for when a key has leaked: anything still using it starts failing at once.

Keys held by AI clients

When claude.ai, Claude Desktop or Claude Code connects through the MCP server with OAuth, the client receives a key of its own with exactly the scopes you approved. Those keys do not appear in the list above or on the API keys page; they are listed, and revoked, under AI clients in the console, so one credential never has two views that can disagree.

Storing them

Treat a key like a password. Environment variables or a secret manager; never a repository, a client-side bundle, or a URL. Keys carry a wv_live_ or wv_test_ prefix so a secret scanner can spot one, and so a test key pasted into production fails loudly instead of quietly operating on real data.