Skip to main content

OAuth 2.0 integration

Driftstack uses OAuth 2.0 with PKCE-S256 for third-party app access. This page documents the authorization code flow your app uses to act on a Driftstack customer's behalf.

Customer authorization is available on every plan, the free trial included.

Getting a client_id

OAuth client registration is admin-gated. Email [email protected] with:

We turn around new client registrations within one business day. You get back a client_id (prefixed oac_) and a client_secret (prefixed oas_). The secret is shown to you ONCE — we only store the hash on our side, so we cannot recover it for you. Treat it like a Stripe secret key.

Available scopes

ScopeWhat it grants
read:sessionsRead direct sessions and AI (agent) sessions: list, get, history.
write:sessionsCreate and close direct sessions on /v1/sessions. It does not start or drive AI (agent) sessions.
read:profilesRead browser-profile metadata.
write:profilesCreate + update browser profiles.
admin:profilesReserved — no route enforces it; profile writes use write:profiles.
read:webhooksList webhook endpoints + delivery history.
write:webhooksReserved — endpoint management requires account_owner.
admin:webhooksReserved — secret rotation and deletion require account_owner.
read:api-keysList the account’s API keys (metadata only).
admin:api-keysReserved — minting and revoking require account_owner.
read:billingRead invoices, usage, and crypto-order history.
admin:billingReserved — checkout, the billing portal and orders are done in the dashboard, and OAuth tokens are refused them (dashboard-only).
read:auditRead the account audit log.

Scopes follow the verb:resource pattern. Always request the narrowest scope set your app actually needs — broad scopes get pushed back during the human review step.

AI sessions are not available to OAuth apps

OAuth apps can't run AI sessions today; use an API key for that. Creating an AI (agent) session, sending it a task, stopping it and closing it all need the broad write scope. OAuth grants only the granular scopes in the table above, so an OAuth token gets 403 on POST /v1/agent-sessions and on every write that starts or drives an AI session (message, steps, stop, close, reopen).

One documented agent-session write takes a granular scope: the profile notes. PUT /v1/agent-sessions/{id}/profile-notes needs write:profiles. It edits the notes kept with the profile and does not start or drive anything.

With write:sessions an OAuth app can create and close direct sessions on /v1/sessions. On hosted Driftstack a direct session cannot load or drive a page, so it is not a way to browse. With read:sessions an OAuth app can read the AI sessions the customer ran with their own key or in the dashboard.

Authorization flow

1. Mint a PKCE verifier

Generate a high-entropy random string (RFC 7636 §4.1, 43–128 characters from [A-Za-z0-9-._~]). Compute the code_challenge as the URL-safe base64 of SHA-256(verifier). Keep the verifier in your app until step 3 — never send it to anyone except Driftstack at the token endpoint.

2. Redirect the customer to Driftstack

GET https://app.driftstack.io/oauth/authorize/
  ?client_id=oac_…
  &redirect_uri=https://yourapp.com/oauth/callback
  &state=<csrf-token>
  &code_challenge=<sha256(verifier) base64url>
  &code_challenge_method=S256
  &scope=read:sessions write:sessions

The customer signs into Driftstack if they aren't already, then sees the app label, requested scopes, and callback host before choosing Approve or Cancel. Approval returns them to your redirect_uri with ?code=…&state=…; cancellation returns ?error=access_denied&state=…. Verify state matches what you sent (CSRF guard) in either case.

The Dashboard validates and stages this request through GET /v1/oauth/authorize, then performs approved consent through POST /v1/oauth/authorize/complete. Those are provider-internal steps: your integration never receives or handles the intermediate authorization_id.

3. Exchange the code for an access token

POST /v1/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "...",
  "code_verifier": "<your pkce verifier>",
  "client_id": "oac_…",
  "client_secret": "oas_…",
  "redirect_uri": "https://yourapp.com/oauth/callback"
}

The response carries the token:

{
  "access_token": "oat_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": ["read:sessions", "write:sessions"]
}

Tokens live for one hour. There are no refresh tokens — when the token expires, run the full authorize → exchange dance again. This is intentional: it makes consent re-confirmation a regular event rather than a forever-grant.

4. Use the token

Bearer-token authentication against the Driftstack API:

Authorization: Bearer oat_…

The token can only call API surfaces whose required scope is a subset of the token's scope. A token with read:sessions can call GET /v1/sessions but not POST /v1/sessions. A token with write:sessions can call POST /v1/sessions but not POST /v1/agent-sessions: no OAuth scope reaches the AI session writes (see AI sessions are not available to OAuth apps).

Introspection (RFC 7662)

Check whether a token is still active + what scopes it carries:

POST /v1/oauth/introspect
Content-Type: application/json

{
  "token": "oat_…",
  "client_id": "oac_…",
  "client_secret": "oas_…"
}

→ { "active": true, "client_id": "oac_…", "account_id": "acc_…",
    "scope": ["read:sessions"], "exp": 1736600000 }

After your client credentials are authenticated, a revoked, expired, unknown, or foreign-client token returns {"active": false}. Invalid or revoked client credentials return 401 before token lookup. Use introspection sparingly and only from server-side code — the request contains your client_secret.

Revocation (RFC 7009)

Invalidate a token before its 1-hour expiry — useful when the customer signs out of your app:

POST /v1/oauth/revoke
Content-Type: application/json

{
  "token": "oat_…",
  "client_id": "oac_…",
  "client_secret": "oas_…",
  "token_type_hint": "access_token"
}

→ 200 OK

After client authentication succeeds, the endpoint returns 200 for an owned, unknown, or foreign-client token, but only a token issued to your client_id is revoked. This preserves RFC 7009 anti-enumeration behavior without allowing cross-client revocation. Invalid or revoked client credentials return 401. token_type_hint is optional and informational.

Security expectations

Rate limits

Token-issued requests count against the customer's account rate limits, not your app's. If you're going to mint many tokens for many customers, be aware that high per-customer QPS will hit the tier limits.

Sandbox

Driftstack has no separate sandbox environment. Test with a dev account (the free trial works) on the production API and use least-privilege scopes. Read-only scopes cannot create sessions, but the account still uses production data, limits, and rate-limit budgets. No OAuth scope can run an AI session, in testing or otherwise.

Support

Questions, bug reports, or scope requests: [email protected]. Reply times follow your plan's support on the pricing page: a 48-hour reply target on Starter and Pro.