OAuth Connect
Initiate hosted OAuth flows for connected accounts — Supyagent handles PKCE, token exchange, and encrypted storage.
OAuth Connect
Connect sessions are the mechanism for connecting a provider (Google, Slack, etc.) to a connected account. Supyagent fully hosts the OAuth flow — you initiate a session, redirect the user to the provider, and Supyagent handles the token exchange, encryption, and storage.
Initiating a Connect Session
POST /api/v1/accounts/:id/connectCreates a connect session and returns an OAuth authorization URL.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | Yes | OAuth provider to connect (see supported providers) |
redirect_url | string | Yes | URL where the user returns after authorization |
scopes | string[] | No | Custom OAuth scopes (defaults vary by provider) |
curl -X POST https://app.supyagent.com/api/v1/accounts/550e8400-e29b-41d4-a716-446655440000/connect \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"provider": "google",
"redirect_url": "https://yourapp.com/oauth/complete",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/calendar.readonly"
]
}'Response
{
"ok": true,
"data": {
"connect_url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&state=abc123&code_challenge=xyz...",
"session_id": "660e8400-e29b-41d4-a716-446655440001",
"expires_at": "2026-02-25T10:30:00.000Z"
}
}| Field | Description |
|---|---|
connect_url | The OAuth authorization URL to open in the user's browser |
session_id | Session identifier for polling status |
expires_at | Session expiry time (30 minutes from creation) |
Errors
| Status | Error | Meaning |
|---|---|---|
| 400 | Validation error | Invalid provider or missing redirect_url |
| 403 | PARTNER_REQUIRED | No active partner profile |
| 404 | Not found | Connected account doesn't exist or doesn't belong to you |
The OAuth Flow
After you receive the connect_url, here's what happens:
Step 1: Redirect the User
Open the connect_url in the user's browser. Common patterns:
// Full-page redirect
window.location.href = connectUrl;
// Popup window
window.open(connectUrl, 'oauth', 'width=600,height=700');
// Link/button
<a href={connectUrl}>Connect Google</a>Step 2: User Authorizes
The user sees the provider's standard OAuth consent screen (e.g., "Allow Acme App to access your Gmail?"). If they approve, the provider redirects to Supyagent's callback.
Step 3: Token Exchange (Automatic)
Supyagent's callback handler (/api/v1/connect/callback) automatically:
- Validates the CSRF
stateparameter - Checks the session hasn't expired (30-minute window)
- Decrypts the stored PKCE code verifier
- Exchanges the authorization code for access and refresh tokens
- Fetches basic user info from the provider (email, name)
- Encrypts and stores the tokens in the
integrationstable - Auto-enables services based on the provider's permission registry
- Marks the connect session as
completed
Step 4: Redirect Back
The user is redirected to your redirect_url with query parameters:
Success:
https://yourapp.com/oauth/complete?status=success&provider=google&account_id=user-123Failure:
https://yourapp.com/oauth/complete?error=access_deniedThe account_id parameter contains the connected account's external_id, so you can immediately identify which user completed the flow.
Checking Session Status
GET /api/v1/accounts/:id/connect/:sessionIdPoll this endpoint to check the status of a connect session. Useful for popup-based flows where you can't rely on the redirect.
curl https://app.supyagent.com/api/v1/accounts/550e8400-e29b-41d4-a716-446655440000/connect/660e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer sk_live_your_key_here"Response
{
"ok": true,
"data": {
"session_id": "660e8400-e29b-41d4-a716-446655440001",
"provider": "google",
"status": "completed",
"created_at": "2026-02-25T10:00:00.000Z",
"expires_at": "2026-02-25T10:30:00.000Z"
}
}Session Statuses
| Status | Meaning |
|---|---|
pending | Session created, waiting for user to authorize |
completed | OAuth flow finished successfully. Tokens stored. |
expired | Session exceeded the 30-minute window |
failed | An error occurred. Check the error field for details. |
When the status is failed, an additional error field is included:
{
"status": "failed",
"error": "access_denied"
}Supported Providers
| Provider | Default Scopes |
|---|---|
google | Gmail, Calendar, Drive, Docs, Sheets, Slides (all Google scopes) |
slack | — (Slack determines scopes from app config) |
discord | Standard user scopes |
github | read:user, user:email, repo |
microsoft | Mail, Calendar, OneDrive, User |
linkedin | Profile, posts |
twitter | Standard user scopes with PKCE |
notion | Workspace access |
hubspot | CRM access |
jira | read:jira-work, write:jira-work, read:jira-user |
linear | read, write |
calendly | Event types, scheduled events |
salesforce | api, refresh_token |
pipedrive | Standard CRM access |
Custom Scopes
You can override the default scopes for any provider by passing a scopes array:
{
"provider": "google",
"redirect_url": "https://yourapp.com/callback",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly"
]
}This is useful when you only need a subset of permissions and want to request the minimum required scope from the user.
Security Details
PKCE (Proof Key for Code Exchange)
Every connect session generates a PKCE pair:
- Code verifier — random secret, encrypted and stored in the session
- Code challenge — SHA256 hash of the verifier, sent in the authorization URL
This prevents authorization code interception attacks, even if the connect_url is exposed.
CSRF Protection
Each session has a unique state parameter (32 random bytes). The callback validates that the state matches an existing pending session before processing. This prevents cross-site request forgery.
Token Encryption
All OAuth tokens (access and refresh) are encrypted with AES before storage. They are decrypted only at request time when making API calls on behalf of the connected account.
Implementation Patterns
Redirect-Based Flow
The simplest pattern — redirect the user and handle the callback on your server:
// 1. Your backend: create connect session
const res = await fetch('https://app.supyagent.com/api/v1/accounts/${accountId}/connect', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: 'google',
redirect_url: 'https://yourapp.com/integrations/callback',
}),
});
const { data } = await res.json();
// 2. Redirect user to data.connect_url
// 3. Handle callback at /integrations/callbackPopup-Based Flow with Polling
Open OAuth in a popup and poll for completion:
// 1. Create session (same as above)
const { data } = await createConnectSession(accountId, 'google');
// 2. Open popup
const popup = window.open(data.connect_url, 'oauth', 'width=600,height=700');
// 3. Poll for completion
const interval = setInterval(async () => {
const status = await checkSessionStatus(accountId, data.session_id);
if (status.data.status === 'completed') {
clearInterval(interval);
popup?.close();
// Refresh your UI to show the new integration
} else if (status.data.status === 'failed' || status.data.status === 'expired') {
clearInterval(interval);
popup?.close();
// Show error to user
}
}, 2000); // Poll every 2 secondsOAuth Redirect URI Configuration
For connect sessions to work, your OAuth app at each provider must include the following callback URL in its allowed redirect URIs:
https://app.supyagent.com/api/v1/connect/callbackThis is separate from the standard dashboard OAuth callbacks. Both must be registered.
| Environment | Callback URL |
|---|---|
| Local | http://localhost:3000/api/v1/connect/callback |
| Production | https://app.supyagent.com/api/v1/connect/callback |
Some providers (e.g., GitHub) only support one callback URL per OAuth app. In these cases, you may need separate OAuth apps for dashboard auth and connect sessions.
What's Next
- Integrations — Manage provider integrations per account
- API Reference — Complete endpoint reference
Connected Accounts API
Create, list, update, and delete connected accounts — sub-accounts that represent your end-users in the Supyagent platform.
Making API Calls
Route provider API calls to connected accounts using the X-Account-Id header — the core pattern for calling Gmail, Slack, GitHub, and other providers on behalf of your users.