Skip to main content
Every Vanta API request is authenticated with a short-lived OAuth 2.0 bearer token. Which OAuth flow you use — and the shape of the credentials you exchange — depends on the application type you create in the Developer Console. This page explains the underlying model so you can pick the right flow, understand the tradeoffs, and avoid the foot-guns that aren’t obvious from the per-quickstart instructions.

Overview

Every Vanta API client is registered in the Developer Console as an Application. Each Application has a client_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 typeGrant typeToken is scoped toRefresh tokens
Manage Vantaclient_credentialsYour own Vanta tenantNo
Build Integrations (Private)client_credentialsYour own Vanta tenantNo
Build Integrations (Public)authorization_codeA specific customer’s tenantYes
Auditor APIclient_credentialsYour auditor firm’s auditsNo
Used by Manage Vanta, Build Integrations (Private), and Auditor API apps. There is no end-user; your server holds the client_id and client_secret and exchanges them directly for an access token.
Response:
Notice there’s no 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 appsvanta-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 appsauditor-api.audit:read|write, auditor-api.auditor:read|write.
Always request the minimum scopes your tool needs. Scopes are validated at token-exchange time, so an over-scoped token is a real attack surface even when most of your code paths only read.

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.
Vanta only allows a single active access_token per Application. Requesting a new token with the same client_id and client_secret immediately revokes the previous one — any in-flight requests using the old token will fail with 401 Unauthorized.
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_credentials apps, request the token at the start of a sync run and reuse it for the entire run. Don’t refetch per request.
  • For authorization_code apps, this rule applies per source_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 your client_id and client_secret for a new token. There’s no refresh token; the credentials are the refresh.
  • authorization_code — exchange the refresh_token you received during initial authorization for a new access_token / refresh_token pair.

Refresh token rotation

Applies only to Build Integrations (Public) apps. client_credentials apps have no refresh token — see Token expiration above.
Every successful refresh returns a new refresh_token. The previous refresh token stays valid for 3 hours after first use, then expires.
Persist the new refresh_token immediately on every refresh. If your refresh handler crashes after issuing the request but before saving the result, the old token still works for up to 3 hours — long enough to recover without forcing the customer to re-authorize, but only if you actually retry.
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.
After Vanta redirects the customer back to your 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

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, or refresh_token in source control or client-side code. All token exchanges must happen on a server you control.
  • Rotate client_secret when 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 state validation as mandatory. Compare the returned state against 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.com for standard Vanta tenants — US, EU, and AU alike
  • https://api.vanta-gov.com for Vanta Gov tenants
api.eu.vanta.com and api.aus.vanta.com exist but only return an HTTP 308 redirect to https://api.vanta.com — don’t call them directly. Many HTTP and OAuth libraries don’t replay a POST body through a redirect, so a token request to the regional hostname can fail with a confusing plain-text 308 response instead of a normal error.
The browser-facing app host (/oauth/authorize, the Developer Console, the customer’s dashboard) is regional — each region is a genuinely separate host, not a redirect:
RegionApp host
United Stateshttps://app.vanta.com
Europehttps://app.eu.vanta.com
Australiahttps://app.aus.vanta.com
Vanta Govhttps://app.vanta-gov.com
For Build Integrations (Public) apps: redirect the customer to their own regional app host, not always app.vanta.com. Sending an EU or AU customer to app.vanta.com to authorize can fail outright or hit domain/session issues specific to that region.
Everything else — grant types, scopes, token lifetimes, refresh semantics — is identical across regions and deployments. See the API reference for the canonical base URL table.

Common error patterns

SymptomLikely causeApplies to
401 invalid_client on token exchangeWrong client_id / client_secret, or rotated secret with stale value cached.All apps
400 invalid_request on token exchangeBody sent as application/x-www-form-urlencoded instead of application/json.All apps
400 invalid_scopeRequested scope isn’t allowed for this app type.All apps
Intermittent 401 mid-runTwo processes minting tokens for the same Application — they’re invalidating each other. Centralize token issuance.All apps
401 after token refreshYour 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 flowingYou didn’t call the Suspend API on disconnect. Wire it into your uninstall handler.Build Integrations (Public) only
429 on /oauth/tokenYou’re minting more than 5 tokens/minute — request once per run, not per request.All apps
308 / non-JSON response from /oauth/tokenYou 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 screenYou 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

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.