Connected accounts
Connected accounts solve a problem that turns up as soon as an application or an agent has to act against a third-party API on a user’s behalf: somebody has to run the OAuth dance, store the refresh token, refresh it before it expires, and keep it out of everything that does not need it.
Here, the platform does all four. Your org registers its OAuth app, users grant access once through a brokered flow, tokens are stored envelope-encrypted, and your services ask the management API for a live access token when they need one. The refresh token never leaves the vault.
Register a provider
Section titled “Register a provider”Requires connect:write. Named kinds come with their endpoints already set:
POST /v1/organizations/org_01J…/connect/providersAuthorization: Bearer sk_live_…
{ "name": "GitHub (issues)", "kind": "github", "client_id": "Iv1.…", "client_secret": "…", "scopes": ["repo", "read:org"]}{ "provider_id": "cp_…", "name": "GitHub (issues)", "kind": "github", "client_id": "Iv1.…", "authorization_endpoint": "https://github.com/login/oauth/authorize", "scopes": ["repo", "read:org"], "created_at": 1750000000000}kind | Endpoints |
|---|---|
github | Preset |
google | Preset |
slack | Preset |
custom | You supply authorization_endpoint and token_endpoint |
The client_secret is stored encrypted and never returned. Both endpoints are checked against the
egress policy before the provider is created.
These are your org’s OAuth apps — you register them with the provider, and the redirect URI you configure there points back at the tenant host.
The user grants access
Section titled “The user grants access”A signed-in user sees what they have linked and what they could link:
GET /connectCookie: <tenant session>{ "accounts": [ { "account_id": "ca_…", "provider_id": "cp_…", "scopes": ["repo"], "status": "active", "expires_at": 1750003600000 } ], "available": [ { "provider_id": "cp_…", "name": "Slack", "kind": "slack", "scopes": ["chat:write"] } ]}Starting a link at /connect/:providerId/start redirects the user to the provider with PKCE and a
single-use state; the provider returns to /connect/:providerId/callback, where the code is
exchanged server-side and the tokens are encrypted and stored.
What a user is shown about a provider is deliberately narrower than what an operator sees: the
client id and authorization endpoint are integration detail, while the scopes — what they are being
asked to grant — are not. Users can disconnect their own accounts at /connect/:accountId/revoke.
These are hosted, same-origin endpoints backed by the session cookie; they are not callable cross-origin. See authentication.
Fetch a live token
Section titled “Fetch a live token”This is the point of the vault. Requires connect:tokens:
POST /v1/organizations/org_01J…/connect/accounts/ca_…/tokenAuthorization: Bearer sk_live_…{ "access_token": "gho_…", "expires_at": 1750003600000, "scopes": ["repo", "read:org"]}If the stored token is within a minute of expiry, it is refreshed against the provider during this
call and the fresh one is returned — your service never has to hold refresh logic, or a refresh
token. If the token has expired and no refresh token is held, the call returns 502 token_unavailable rather than handing back something dead.
Every retrieval writes an account_token_retrieved audit event naming the API key that made it.
Token egress is the most sensitive read in the system and is treated as such —
alert on it.
Listing and revoking
Section titled “Listing and revoking”GET /v1/organizations/:orgId/connect/accounts?user_id=usr_… # connect:readPOST /v1/organizations/:orgId/connect/accounts/:accountId/revoke # connect:writeGET /v1/organizations/:orgId/connect/providersDELETE /v1/organizations/:orgId/connect/providers/:providerIdRevoking marks the account inactive and stops token retrieval. The user can link the provider again
afterwards — a revoked account still occupies its provider slot, which is why it reappears under
available once revoked rather than being silently duplicated.
How this differs from SSO
Section titled “How this differs from SSO”Both involve a third-party identity provider, and they are not the same thing:
- OIDC federation / SAML — how a user signs in to your application. The result is a session and an ID token.
- Connected accounts — how your application acts against another API as that user. The result is a stored, refreshable third-party access token.
A customer may well use both, with the same upstream provider, for different purposes.