Authentication
Vision uses OAuth 2.0 client credentials for server-to-server integrations. A client exchanges its ID and secret for a short-lived signed token, then sends that token as a Bearer credential. Machine identities are separate from people's accounts and browser sessions.
Get credentials
An API key (an API client) belongs to one Vision business. An owner or administrator makes it in Vision under Settings → Developer:
- Choose Connect a website form for the common case, or Create a custom key: a name, where it runs (its identity class) and what it can do (its scopes).
- Confirm it's you with a fresh sign-in (and two-step sign-in for live keys).
- Copy the client ID (
vcl_...) and the secret. The secret is shown once and stored by Vision only as a one-way verifier.
Every step, and replacing or revoking a key, is on API keys.
Token endpoint
POST https://app.runonvision.com/oauth/token
Content-Type: application/x-www-form-urlencoded
- Name
grant_type- Type
- required
- Description
Always
client_credentials.
- Name
client_id, client_secret- Type
- required
- Description
In the body, or as HTTP Basic authentication (Basic wins when both are sent).
- Name
scope- Type
- optional
- Description
Space-separated. Must be a subset of the client's granted scopes; otherwise
invalid_scope.
- Name
resource- Type
- optional
- Description
The audience of one App (RFC 8707). At most one value; two or more return
invalid_target. Omit it for a Platform token.
The response is { access_token, token_type: "Bearer", expires_in, scope } with Cache-Control: no-store. Lifetime is 60 to 300 seconds (300 by default). There is no refresh token: exchange again before expiry. Errors use the OAuth format { "error": "...", "error_description": "..." }; see the Quickstart for each code.
Token exchange is rate limited per client and per source. Cache tokens; see Rate limits.
Tokens for an App
A token without resource is a Platform token: its audience is Vision Core, and it works for Core's Platform API and its MCP server. Apps such as CRM or Calendar verify their own audience and reject a Platform token.
To call an App directly, add resource=<audience>. The token then carries exactly one audience and one App entitlement. Each App's audience is listed in Getting started by App.
Live status, 2026-11-03: a key you make in Settings → Developer can get an
App-audience token only for scopes that App allows keys to hold. Today that is
CRM's lead intake, crm:intake:create, with resource=vision-crm-live.
Access-token claims
Tokens are JWTs signed with ES256 (or RS256) and use the vision-service-credential.v1 vocabulary.
- Name
iss, aud, sub, jti, iat, nbf, exp- Description
Standard JWT claims.
subis the client ID;nbfis five seconds before issue.
- Name
credential_version- Description
Always
vision-service-credential.v1.
- Name
vision_org_id- Description
- The issuing Vision organization.
- Name
vision_workspace_id- Description
The workspace that owns the client.
- Name
app_workspace_ref- Description
The workspace reference the target App uses. Its format differs per App (see Getting started by App).
- Name
identity_class- Description
service,agent,workerorhuman.
- Name
app_entitlements- Description
- The Apps this token may reach.
- Name
scope- Description
- Space-delimited, App-qualified scopes.
- Name
environment- Description
- The environment the client belongs to.
Treat the token as opaque in your client. Only resource servers should decode it.
Identity classes
The client's identity class limits what its tokens can do, on top of scopes:
| Class | Typical use | What Apps allow today |
|---|---|---|
service | A server you run (website back end, sync job) | Reads everywhere; writes only where the App grants them (Directory writes; Calendar call requests and actor provisioning) |
agent | An AI agent acting for the workspace | Reads only |
worker | Health and infrastructure probes | Health reads only |
human | A person's session brokered by Vision | Everything their role allows, through Vision |
A scope you hold can still be refused for your identity class. That is a 403 with code PERMISSION_DENIED (or permission_denied), not a token error.
Public verification keys
Resource servers verify signatures with the JSON Web Key Set:
https://app.runonvision.com/.well-known/jwks.json
Pin the issuer, audience, algorithm and key ID you expect, and never fetch keys from a URL taken from the token. Keys are cached for five minutes; refetch on an unknown kid.
Credential lifecycle
- Rotate from the client page. Rotation creates a new secret version and can keep the old one valid for up to 60 minutes so you can switch without downtime.
- Revoke at once if a secret may have leaked. Revocation is checked at every token exchange, and tokens already issued expire within five minutes.
- Every create, rotate and revoke is written to the workspace audit log.