Overview
Every Vanta API client is registered in the Developer Console as an Application. Each Application has aclient_id and a client_secret, plus an app type that determines which OAuth grant types and scopes it can use. Clients exchange those credentials at a single token endpoint — POST https://api.vanta.com/oauth/token — for an access_token, then send that token as Authorization: Bearer <access_token> on every API request. Tokens last one hour. After that you either request a fresh one (machine-to-machine flows) or refresh it (per-customer OAuth flows).
Vanta does not support API keys, basic auth, or session cookies for the API. Every authenticated request goes through the OAuth bearer token model below.
Application types
Vanta exposes four application types, mapped to two OAuth 2.0 grant types. The grant type — not the app type — determines the shape of the auth flow.| Application type | Grant type | Token is scoped to | Refresh tokens |
|---|---|---|---|
| Manage Vanta | client_credentials | Your own Vanta tenant | No |
| Build Integrations (Private) | client_credentials | Your own Vanta tenant | No |
| Build Integrations (Public) | authorization_code | A specific customer’s tenant | Yes |
| Auditor API | client_credentials | Your auditor firm’s audits | No |
- Client Credentials
Used by Manage Vanta, Build Integrations (Private), and Auditor API apps. There is no end-user; your server holds the Response:Notice there’s no
client_id and client_secret and exchanges them directly for an access token.refresh_token — when the access token expires, you just exchange your client credentials again for a fresh one. Most integrations request a new token at the top of each scheduled run.Scopes
All applications authenticate against the same/oauth/token endpoint, but the scopes you can request depend on your app type. Requesting a scope that doesn’t match your app type returns an invalid_scope error.
The full per-app scope matrix lives in the API reference. At a high level:
- Manage Vanta apps —
vanta-api.all:read,vanta-api.all:write,vanta-api.documents:upload, plus vendor-specific scopes. - Build Integrations apps (private and public) —
connectors.self:read-resource,connectors.self:write-resource,self:read-document,self:write-document. - Auditor apps —
auditor-api.audit:read|write,auditor-api.auditor:read|write.
Token lifecycle
The OAuth flows themselves are standard. The behavior around those flows is where most production integrations get bitten. The rules below apply to every app type. Concretely, this means:- Don’t run two processes that both mint tokens for the same Application. They’ll race, mutually invalidate each other, and you’ll see intermittent
401s. - For
client_credentialsapps, request the token at the start of a sync run and reuse it for the entire run. Don’t refetch per request. - For
authorization_codeapps, this rule applies persource_id(per customer authorization), not globally — different customers get independent tokens.
Token expiration
expires_in is 3600 seconds (1 hour). After that, requests return 401. The refresh path differs by grant type:
client_credentials— re-exchange yourclient_idandclient_secretfor a new token. There’s no refresh token; the credentials are the refresh.authorization_code— exchange therefresh_tokenyou received during initial authorization for a newaccess_token/refresh_tokenpair.
Refresh token rotation
Applies only to Build Integrations (Public) apps.
client_credentials apps have no refresh token — see Token expiration above.refresh_token. The previous refresh token stays valid for 3 hours after first use, then expires.
The 3-hour window is specifically designed to tolerate transient failures. Configure automatic retries on 5xx responses and network errors during refresh; if you don’t, a single bad request can lock a customer out and force a full re-auth.
Authorization code expiration
Applies only to Build Integrations (Public) apps —
client_credentials apps never exchange a code.redirect_uri with a code, you have 30 seconds to exchange it at /oauth/token. After that the code is dead and the customer has to start the OAuth flow over.
In practice this is only a problem if your callback handler is slow (cold-start Lambdas, blocking on a database write before the token exchange). Do the token exchange first, then persist.
Request body encoding
Vanta’s
/oauth/token endpoint expects a Content-Type: application/json body. Most off-the-shelf OAuth client libraries default to application/x-www-form-urlencoded per RFC 6749 — you’ll need to override that. If you see invalid_request or invalid_client errors despite using correct credentials, check the body encoding first.Rate limits
/oauth/token is limited to 5 requests per minute across all app types. This is shared across token issuance, refresh, and (for Public) the suspend endpoint. It’s deliberately low because a healthy integration rarely needs more than one token per hour — if you’re hitting the limit, you’re probably re-minting per request rather than per run.
Revoking access
- Client Credentials
To revoke access, rotate the
client_secret in the Developer Console (which immediately invalidates any active token issued with the old secret) or delete the Application entirely.Credential hygiene
Rules that apply across every app type:- Never put
client_secret,access_token, orrefresh_tokenin source control or client-side code. All token exchanges must happen on a server you control. - Rotate
client_secretwhen team members leave or any time you suspect a leak. Rotation is supported from the Developer Console. For Build Integrations (Public) apps specifically, rotating the secret does not invalidate active customer access or refresh tokens — only new token mints.
The following two rules apply only to Build Integrations (Public) apps, since non-public apps have no per-customer tokens and never touch the browser redirect.
- Store per-customer tokens encrypted at rest. A breach of one customer’s tokens shouldn’t compromise others.
- Treat
statevalidation as mandatory. Compare the returnedstateagainst the value you stored in the user’s session before exchanging the code; abort if they don’t match. This is your CSRF protection.
Base URLs and regions
The API host and the browser-facing app host follow different rules — don’t assume they’re regional the same way. Token exchange and API calls always go to the same host, regardless of the tenant’s region:https://api.vanta.comfor standard Vanta tenants — US, EU, and AU alikehttps://api.vanta-gov.comfor Vanta Gov tenants
/oauth/authorize, the Developer Console, the customer’s dashboard) is regional — each region is a genuinely separate host, not a redirect:
| Region | App host |
|---|---|
| United States | https://app.vanta.com |
| Europe | https://app.eu.vanta.com |
| Australia | https://app.aus.vanta.com |
| Vanta Gov | https://app.vanta-gov.com |
Common error patterns
| Symptom | Likely cause | Applies to |
|---|---|---|
401 invalid_client on token exchange | Wrong client_id / client_secret, or rotated secret with stale value cached. | All apps |
400 invalid_request on token exchange | Body sent as application/x-www-form-urlencoded instead of application/json. | All apps |
400 invalid_scope | Requested scope isn’t allowed for this app type. | All apps |
Intermittent 401 mid-run | Two processes minting tokens for the same Application — they’re invalidating each other. Centralize token issuance. | All apps |
401 after token refresh | Your refresh handler isn’t persisting the new refresh_token. Without persistence you fall back to the old one, which expires 3 hours after first reuse. | Build Integrations (Public) only |
| Customer “uninstalled” but data still flowing | You didn’t call the Suspend API on disconnect. Wire it into your uninstall handler. | Build Integrations (Public) only |
429 on /oauth/token | You’re minting more than 5 tokens/minute — request once per run, not per request. | All apps |
308 / non-JSON response from /oauth/token | You called a regional hostname (api.eu.vanta.com, api.aus.vanta.com) instead of https://api.vanta.com. Call api.vanta.com directly — see Base URLs and regions. | All apps |
| Authorization fails, or a domain/session error on the Vanta authorize screen | You redirected an EU or AU customer to app.vanta.com instead of their region’s app host (app.eu.vanta.com / app.aus.vanta.com). The app host is regional even though the API host isn’t — see Base URLs and regions. | Build Integrations (Public) only |
Related
Manage Vanta quickstart
Get a
client_credentials token and call your first endpoint.Build a Public Integration
The full
authorization_code flow, including refresh and Suspend.Build a Private Integration
Single-tenant
client_credentials flow for homegrown systems.Conduct an Audit quickstart
Auditor-scoped
client_credentials flow against customer audits.API scopes reference
The full per-app-type scope matrix.
Postman setup
Import the collection and start testing requests in seconds.