The Bearer scheme
Send your key in theAuthorization header on every request:
401 unauthorized. A valid key acting outside its scopes returns 403 forbidden.
Scopes
Keys carry scopes that limit what they can do. Scopes follow aresource:action grammar (e.g. agents:write, sessions:read). Every key names at least one; grant admin for full access to the organization.
proxy:write is deliberately narrow. The proxy routes accept it or sessions:write, so a key that reaches them today keeps working — but a credential that holds only proxy:write can make model calls and cannot start, cancel or read sessions. That is what makes it safe to hand to a sandbox: a harness that runs as a process inside a micro-VM needs a credential to reach the door, and one that leaked should not be able to run sessions for your organization.webhooks:* is one scope, spelled with the star: it is the only webhook scope, and it covers both reading and managing endpoints. billing:write gates the card-charging top-up endpoint and is deliberately separate from read-only billing:read.Who is this credential?
GET /v1/me answers from the credential alone — no scope, no organization id in the request. It is
the first call any client should make, and it is how the CLI and the dashboard learn which
organization a key belongs to instead of asking you to paste an org_ id you have no way to look
up.
Response
organization is null only for a signed-in person who does not belong to one yet; user is
null whenever the caller is an API key rather than a person.
GET /v1/organizations is the list form. An API key reaches exactly the one organization it is
bound to — a one-row page, never another tenant’s row. A signed-in person reaches every
organization they are a member of. Cursor-paginated, see Pagination.
Signing a person in
A person authenticates with an email address and password and receives a short-lived bearer session token prefixedvt_. It is accepted anywhere an sk_ key is, on the same
Authorization: Bearer header, and its scopes are derived from the member’s role.
string
The account’s email address. Required with
password when signing in fresh; omitted when re-scoping an existing vt_ token.string
The account’s password. Paired with
email.string
The access token an external provider sign-in came back with. Verified against the provider on every call; never decoded here. Used instead of
email/password.string
The organization to scope the session to. With an existing
vt_ token this re-scopes the session — the org switcher. Must be an organization the person belongs to.Request
Response
vt_ token to the same route with an organization_id re-scopes the session
to another organization you belong to — that is what an organization switcher is. Passing an
organization_id you are not a member of returns 403 forbidden.
Signing in with Google
The dashboard offers “Continue with Google” wherever the deployment has the provider turned on. It is the same route and the same session at the end of it: the provider hands back an access token, and that token is exchanged for avt_.
Request
401 unauthorized, phrased exactly as a wrong
password is.
Two things are worth knowing before you build against it:
- The address must be one your provider has verified. You are matched to your organizations by email address, so an unverified address is refused rather than signed in. Google verifies the addresses it asserts, so this is invisible in practice.
- It signs you in; it does not enrol you. Signing in with Google as somebody who belongs to no
organization gives you the same session a password sign-in would:
organization_id: null, no scopes, and the choice of joining one or creating one. Your role and scopes always come from your membership — never from how you signed in, and never from anything the caller asks for.
POST /v1/auth/users registers a person:
string
required
The address to register. Must be confirmed by mail before sign-in succeeds.
string
required
At least 8 characters.
Request
Response
confirmation_required: true means the address must be confirmed by mail before
POST /v1/auth/session will succeed — the response never pretends the person is signed in.
Session tokens are for interactive clients. Machine-to-machine callers should hold a scoped API key:
a token expires and a key does not.
Password reset and confirmation
POST /v1/auth/recover starts a password reset:
string
required
The address to send the reset link to.
POST /v1/auth/confirmations re-sends the
confirmation for an address that has not been proved yet:
string
required
The address to re-send the confirmation to.
202 with the same body whether or not that address has an account — an endpoint that
answered differently would be a way to discover who your users are. Do not branch on it; there is
nothing to branch on.
Response
delivery is sent when the message was handed to a mailer and undeliverable when the deployment
has none attached. It describes the deployment, never the address, which is why it is safe to show
a caller: it lets a sign-in screen say “check your inbox” or “nothing will arrive, ask an operator”
truthfully, without either state being hard-coded.
POST /v1/auth/password finishes a reset with the token the emailed link carried:
string
required
The single-use token from the emailed link.
string
required
The new password. At least 8 characters.
Request
200 with { "object": "auth_password", "updated": true }, or 401 when the token is
expired or already spent. This one is allowed to refuse out loud — the caller already holds the
credential, so saying it is dead discloses nothing.
Both mailed routes are rate limited per address.
Create an API key
POST /v1/api_keys → 201 Created. Creates a new key for the caller’s organization. The plaintext secret is returned only in this response.
string
required
Human-readable label for the key.
string[]
required
Scopes to grant. At least one;
admin for full org access.string
The key id (e.g.
key_01H...).string
The label you supplied.
string
The plaintext key. Returned only once.
string[]
Granted scopes.
string
The non-secret prefix, shown in listings for identification.
object
The principal that created this key —
{ "type": "user" | "key" | "system", "id": "..." }, the same shape the audit log records.string | null
When the key was last used to authenticate a request;
null until first use.string
Creation timestamp.
string
The organization this key opens. Returned only at create and rotate.
string
live or test — the tier the key spends on. Returned only at create and rotate.List API keys
GET /v1/api_keys returns key metadata (never the secret). Cursor-paginated — see Pagination.
Response
Rotate & revoke
Rotation issues a new secret and invalidates the old one. Revocation deletes the key immediately.Rotate on a schedule and on any suspected exposure. Because keys are org-scoped, revoking one never affects another org’s access.
Key creation, rotation, and revocation are recorded in the audit log as
api_key.created, api_key.rotated, and api_key.revoked, attributed to the acting principal.