Skip to main content
The Vetta API authenticates with organization-scoped API keys. Every key belongs to a single organization and can act only on that org’s resources and credit balance.

The Bearer scheme

Send your key in the Authorization header on every request:
A missing or invalid key returns 401 unauthorized. A valid key acting outside its scopes returns 403 forbidden.
Live keys are prefixed sk_live_ and are shown once at creation. Store them in a secret manager — they cannot be retrieved again, only rotated.

Scopes

Keys carry scopes that limit what they can do. Scopes follow a resource: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 prefixed vt_. 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
Presenting an existing 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 a vt_.
Request
The token is verified against the provider on every call — it is never decoded or taken at face value, so a forged, expired or revoked one is 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.
Both answer 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
It answers 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_keys201 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.