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:read and webhooks:write appear in the API’s scope list but are reserved — there are no webhook endpoints yet, so granting them does nothing.
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.

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.

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.