Connected accounts
Learn how to manage connected accounts in Agent Auth, including user authentication, authorization status, and account lifecycle management.
Connected accounts in Agent Auth represent individual user or organization connections to third-party providers. They contain the authentication state, tokens, and permissions needed to execute tools on behalf of a specific identifier (user_id, org_id, or custom identifier).
What are connected accounts?
Section titled “What are connected accounts?”Connected accounts are the runtime instances that link your users to their third-party application accounts. Each connected account:
- Links to a connection: Uses a pre-configured connection for authentication
- Has a unique identifier: Associated with a user_id, org_id, or custom identifier
- Maintains auth state: Tracks whether the user has completed authentication
- Stores tokens: Securely holds access tokens and refresh tokens
- Manages permissions: Tracks granted scopes and permissions
Connected account lifecycle
Section titled “Connected account lifecycle”Connected accounts go through several states during their lifecycle:
Account states
Section titled “Account states”- Pending: Account created but user hasn’t completed authentication
- Active: User has authenticated and tokens are valid
- Expired: Tokens have expired and need refresh
- Revoked: User has revoked access to the application
- Error: Account has authentication or configuration errors
- Suspended: Account temporarily disabled
State transitions
Section titled “State transitions”Creating connected accounts
Section titled “Creating connected accounts”Using the dashboard
Section titled “Using the dashboard”- Navigate to connected accounts in your Agent Auth dashboard
- Click create account to start the process
- Select connection to use for authentication
- Enter identifier (user_id, email, or custom identifier)
- Configure settings such as scopes and permissions
- Generate auth URL for the user to complete authentication
- Monitor status until user completes the flow
Using the API
Section titled “Using the API”Create connected accounts programmatically:
# actions = scalekit_client.actions (initialize ScalekitClient first — see quickstart)response = actions.get_or_create_connected_account( connection_name="gmail", identifier="user_123")connected_account = response.connected_accountprint(f"Connected account: {connected_account.id}, status: {connected_account.status}")// Requires @scalekit-sdk/node@2.2.0-beta.1: npm install @scalekit-sdk/node@2.2.0-beta.1// const { connectedAccounts } = new ScalekitClient(ENV_URL, CLIENT_ID, CLIENT_SECRET)const response = await connectedAccounts.getOrCreateConnectedAccount({ connector: 'gmail', identifier: 'user_123',});
const connectedAccount = response.connectedAccount;console.log('Connected account:', connectedAccount?.id, 'status:', connectedAccount?.status);Authentication flow
Section titled “Authentication flow”OAuth 2.0 flow
Section titled “OAuth 2.0 flow”For OAuth connections, connected accounts follow the standard OAuth flow:
- Create connected account with pending status
- Generate authorization URL for the user
- User completes OAuth flow with the third-party provider
- Provider redirects back with authorization code
- Exchange code for tokens and update account status
- Account becomes active and ready for tool execution
Authorization URL generation
Section titled “Authorization URL generation”Generate URLs for users to complete authentication:
link_response = actions.get_authorization_link( connection_name="gmail", identifier="user_123")print(f"Authorization URL: {link_response.link}")# Redirect the user to link_response.link to complete OAuth// Requires @scalekit-sdk/node@2.2.0-beta.1: npm install @scalekit-sdk/node@2.2.0-beta.1const linkResponse = await connectedAccounts.getMagicLinkForConnectedAccount({ connector: 'gmail', identifier: 'user_123',});
console.log('Authorization URL:', linkResponse.link);// Redirect the user to linkResponse.link to complete OAuthHandling callbacks
Section titled “Handling callbacks”Scalekit handles the OAuth callback automatically. Once the user completes the authorization flow, Scalekit exchanges the code for tokens and updates the connected account status to ACTIVE.
Managing connected accounts
Section titled “Managing connected accounts”Account information
Section titled “Account information”Retrieve connected account details and OAuth tokens:
response = actions.get_connected_account( connection_name="gmail", identifier="user_123")connected_account = response.connected_account
# Extract OAuth tokens from authorization detailstokens = connected_account.authorization_details["oauth_token"]access_token = tokens["access_token"]refresh_token = tokens["refresh_token"]
print(f"Account ID: {connected_account.id}")print(f"Status: {connected_account.status}")const accountResponse = await connectedAccounts.getConnectedAccountByIdentifier({ connector: 'gmail', identifier: 'user_123',});
const connectedAccount = accountResponse?.connectedAccount;const authDetails = connectedAccount?.authorizationDetails;
// Extract OAuth tokens from authorization detailsconst accessToken = (authDetails && authDetails.details?.case === 'oauthToken') ? authDetails.details.value?.accessToken : undefined;const refreshToken = (authDetails && authDetails.details?.case === 'oauthToken') ? authDetails.details.value?.refreshToken : undefined;
console.log('Account ID:', connectedAccount?.id);console.log('Status:', connectedAccount?.status);Token management
Section titled “Token management”Connected accounts automatically handle token lifecycle:
Automatic token refresh:
- Tokens are refreshed automatically before expiration
- Refresh happens transparently during tool execution
- Failed refresh attempts update account status to expired
Manual token refresh:
There is no SDK method to manually trigger a token refresh. If a connected account’s status is EXPIRED or ERROR, generate a new authorization link and prompt the user to re-authorize:
response = actions.get_or_create_connected_account( connection_name="gmail", identifier="user_123")connected_account = response.connected_account
if connected_account.status != "ACTIVE": # Re-authorize the user to refresh their tokens link_response = actions.get_authorization_link( connection_name="gmail", identifier="user_123" ) print(f"Re-authorization required. Send user to: {link_response.link}")const response = await connectedAccounts.getOrCreateConnectedAccount({ connector: 'gmail', identifier: 'user_123',});
const connectedAccount = response.connectedAccount;
if (connectedAccount?.status !== 'ACTIVE') { // Re-authorize the user to refresh their tokens const linkResponse = await connectedAccounts.getMagicLinkForConnectedAccount({ connector: 'gmail', identifier: 'user_123', }); console.log('Re-authorization required. Send user to:', linkResponse.link);}Account status monitoring
Section titled “Account status monitoring”Monitor account authentication status:
response = actions.get_connected_account( connection_name="gmail", identifier="user_123")connected_account = response.connected_account
# Possible status values:# - PENDING: Waiting for user authentication# - ACTIVE: Authenticated and ready# - EXPIRED: Tokens expired, needs re-authorization# - REVOKED: User revoked access# - ERROR: Authentication errorprint(f"Account status: {connected_account.status}")const accountResponse = await connectedAccounts.getConnectedAccountByIdentifier({ connector: 'gmail', identifier: 'user_123',});
const connectedAccount = accountResponse?.connectedAccount;
// Possible status values:// - PENDING: Waiting for user authentication// - ACTIVE: Authenticated and ready// - EXPIRED: Tokens expired, needs re-authorization// - REVOKED: User revoked access// - ERROR: Authentication errorconsole.log('Account status:', connectedAccount?.status);Account permissions and scopes
Section titled “Account permissions and scopes”Scopes define what actions a connected account can perform on a user’s behalf. Understanding how scopes are configured and updated is critical to building reliable agent integrations.
Scopes are set at the connection level
Section titled “Scopes are set at the connection level”Scopes are configured at the connection level, not at the individual connected account level. When a user completes the OAuth authorization flow for a connected account, they approve exactly the scopes defined on that connection.
Scopes are read-only after a connected account is created. There is no API or SDK method to modify the granted scopes on an existing connected account after the user has completed authentication.
Add or change scopes
Section titled “Add or change scopes”To request additional scopes for an existing connected account:
-
Update the connection configuration — In the Scalekit dashboard, navigate to the connection and add the new scopes.
-
Generate a new magic link — Use the Scalekit dashboard or API to create a new authorization link for the user.
-
User approves the updated consent screen — The user visits the link and approves the expanded OAuth consent screen with the new scopes.
-
Connected account is updated — After the user approves, Scalekit updates the connected account with the new token set.
The user must go through the OAuth flow again whenever scopes change. There is no way to silently add scopes on their behalf.
Account and connector status values
Section titled “Account and connector status values”When working with connected accounts, you may encounter the following enum values from the Scalekit platform:
Connector status
| Value | Description |
|---|---|
CONNECTOR_STATUS_ACTIVE | Connector is configured and operational |
CONNECTOR_STATUS_INACTIVE | Connector is configured but not active |
CONNECTOR_STATUS_PENDING | Connector setup is incomplete |
CONNECTOR_STATUS_ERROR | Connector has a configuration or authentication error |
Connector type
| Value | Description |
|---|---|
CONNECTOR_TYPE_OAUTH2 | OAuth 2.0 connection (e.g., Gmail, Slack, GitHub) |
CONNECTOR_TYPE_API_KEY | API key-based connection (e.g., Zendesk, HubSpot) |
CONNECTOR_TYPE_BASIC_AUTH | Username and password connection |
These values are returned in API responses when listing or inspecting connections and connected accounts.
Account metadata and settings
Section titled “Account metadata and settings”Custom metadata
Section titled “Custom metadata”Custom metadata is managed via the Scalekit dashboard.
Bulk operations
Section titled “Bulk operations”Managing multiple accounts
Section titled “Managing multiple accounts”The Python SDK supports per-account retrieval via actions.get_or_create_connected_account. Bulk list and delete operations (connectedAccounts.listConnectedAccounts, connectedAccounts.deleteConnectedAccount) are available in the Node.js SDK, or via the direct API and dashboard.
# Retrieve an individual account by identifier# Note: listing all accounts for a connector is available via the dashboard or direct APIresponse = actions.get_or_create_connected_account( connection_name="gmail", identifier="user_123")connected_account = response.connected_accountprint(f"Account: {connected_account.id}, Status: {connected_account.status}")// List connected accountsconst listResponse = await connectedAccounts.listConnectedAccounts({ connector: 'gmail',});console.log('Connected accounts:', listResponse);
// Delete a connected accountawait connectedAccounts.deleteConnectedAccount({ connector: 'gmail', identifier: 'user_123',});console.log('Connected account deleted');Error handling
Section titled “Error handling”Common errors
Section titled “Common errors”Handle common connected account errors:
try: response = actions.get_connected_account( connection_name="gmail", identifier="user_123" ) connected_account = response.connected_accountexcept Exception as e: print(f"Error retrieving connected account: {e}") # Check connected_account.status for EXPIRED, REVOKED, or ERROR states # and prompt the user to re-authorize if neededtry { const accountResponse = await connectedAccounts.getConnectedAccountByIdentifier({ connector: 'gmail', identifier: 'user_123', }); const connectedAccount = accountResponse?.connectedAccount; console.log('Account status:', connectedAccount?.status);} catch (error) { console.error('Error retrieving connected account:', error); // Check connectedAccount?.status for EXPIRED, REVOKED, or ERROR states // and prompt the user to re-authorize if needed}Error recovery
Section titled “Error recovery”Implement error recovery strategies:
- Detect error - Monitor account status and API responses
- Classify error - Determine if error is recoverable
- Attempt recovery - Try token refresh or re-authentication
- Notify user - Inform user if manual action is required
- Update status - Update account status based on recovery result
Security considerations
Section titled “Security considerations”Token security
Section titled “Token security”Protect user tokens and credentials:
- Encryption: All tokens are encrypted at rest and in transit
- Token rotation: Implement regular token rotation
- Access logging: Log all token access and usage
- Secure storage: Use secure storage mechanisms for tokens
Permission management
Section titled “Permission management”Follow principle of least privilege:
- Minimal scopes: Request only necessary permissions
- Scope validation: Verify permissions before tool execution
- Regular audit: Review granted permissions regularly
- User consent: Ensure users understand granted permissions
Account isolation
Section titled “Account isolation”Ensure proper account isolation:
- Tenant isolation: Separate accounts by tenant/organization
- User isolation: Prevent cross-user data access
- Connection isolation: Separate different connection types
- Audit trail: Maintain detailed audit logs
Monitoring and analytics
Section titled “Monitoring and analytics”Account health monitoring
Section titled “Account health monitoring”Monitor the status of connected accounts by calling get_connected_account (Python) or getConnectedAccountByIdentifier (Node.js) and inspecting the status field. Accounts in a non-ACTIVE state may require re-authorization.
Usage analytics
Section titled “Usage analytics”Track usage patterns and tool execution results through the Scalekit dashboard. SDK methods for usage analytics are not currently available.
Best practices
Section titled “Best practices”Account lifecycle management
Section titled “Account lifecycle management”- Regular cleanup: Remove unused or expired accounts
- Status monitoring: Monitor account status changes
- Proactive refresh: Refresh tokens before expiration
- User notifications: Notify users of authentication issues
Performance optimization
Section titled “Performance optimization”- Connection pooling: Reuse connections efficiently
- Token caching: Cache tokens appropriately
- Batch operations: Use bulk operations when possible
- Async processing: Handle authentication flows asynchronously
User experience
Section titled “User experience”- Clear error messages: Provide helpful error messages to users
- Seamless re-auth: Make re-authentication flows smooth
- Status visibility: Show users their connection status
- Easy revocation: Allow users to easily revoke access
Testing connected accounts
Section titled “Testing connected accounts”Development testing
Section titled “Development testing”Test connected accounts in development:
# Create or retrieve a test connected accountresponse = actions.get_or_create_connected_account( connection_name="gmail", identifier="test_user")test_account = response.connected_accountprint(f"Test account: {test_account.id}, status: {test_account.status}")// Create or retrieve a test connected accountconst response = await connectedAccounts.getOrCreateConnectedAccount({ connector: 'gmail', identifier: 'test_user',});
const testAccount = response.connectedAccount;console.log('Test account:', testAccount?.id, 'status:', testAccount?.status);Integration testing
Section titled “Integration testing”Test authentication flows:
- Create test connection with test credentials
- Create connected account with test identifier
- Generate auth URL and complete OAuth flow
- Verify account status becomes active
- Test tool execution with the account
- Test token refresh and error scenarios
Next, learn how to execute tools using your connected accounts to interact with third-party applications.