Adds composio-sdk/ with SKILL.md, AGENTS.md, and 18 rule files covering Tool Router, direct execution, triggers, and auth patterns. Source: composiohq/skills Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.3 KiB
6.3 KiB
title, impact, description, tags
| title | impact | description | tags | |||||
|---|---|---|---|---|---|---|---|---|
| Connected Accounts Management | HIGH | Comprehensive guide to CRUD operations on connected accounts with emphasis on secure authentication flows |
|
Connected Accounts Management
Using Tool Router? If you're using Tool Router, you can use
session.toolkits()to view the auth configs and connected accounts being used by the Tool Router. You only need to use the methods below if you're managing connected accounts outside of Tool Router.
Connected accounts store authentication tokens for external services. Use the connectedAccounts API for CRUD operations.
Create Connected Accounts
Recommended: link() - Composio-Hosted Authentication
Use link() for most flows. Composio handles security, OAuth, and form rendering.
const connectionRequest = await composio.connectedAccounts.link(
'user_123',
'auth_config_123',
{ callbackUrl: 'https://your-app.com/callback' }
);
// Redirect user to authentication page
window.location.href = connectionRequest.redirectUrl;
// Wait for completion
const account = await connectionRequest.waitForConnection();
Why use link():
- Handles OAuth security and form UI
- Works with 200+ services
- Whitelabel with your app name/logo (Project Settings on dashboard)
- No custom UI needed
Advanced: initiate() - Custom Authentication UI
Only use when building custom auth interfaces:
// API Key (custom form)
const connection = await composio.connectedAccounts.initiate(
'user_123',
'auth_config_456',
{
config: AuthScheme.ApiKey({ api_key: apiKey }),
}
);
// OAuth with extra params (Zendesk, PostHog, etc.)
const connection = await composio.connectedAccounts.initiate(
'user_123',
'zendesk_config',
{
config: AuthScheme.OAuth2({ subdomain: "your_subdomain" })
}
);
window.location.href = connection.redirectUrl;
AuthScheme helpers:
AuthScheme.OAuth2({ subdomain: 'example' })AuthScheme.ApiKey({ api_key: 'key123' })AuthScheme.Basic({ username: 'user', password: 'pass' })AuthScheme.BearerToken({ token: 'token123' })
Use initiate() only when:
- Building custom authentication UI
- Handling credentials directly in backend
- OAuth requires extra parameters before redirect
Read Connected Accounts
// List all
const allAccounts = await composio.connectedAccounts.list();
// Filter by user
const userAccounts = await composio.connectedAccounts.list({
userIds: ['user_123'],
});
// Filter by toolkit
const githubAccounts = await composio.connectedAccounts.list({
toolkitSlugs: ['github'],
});
// Filter by status
const activeAccounts = await composio.connectedAccounts.list({
statuses: ['ACTIVE']
});
// Filter by auth config
const configAccounts = await composio.connectedAccounts.list({
authConfigIds: ['auth_config_123']
});
// Combine filters
const filtered = await composio.connectedAccounts.list({
userIds: ['user_123'],
toolkitSlugs: ['github', 'slack'],
statuses: ['ACTIVE']
});
// Get specific account
const account = await composio.connectedAccounts.get('conn_abc123');
Available filters:
userIds- Filter by user IDstoolkitSlugs- Filter by toolkit slugsstatuses- Filter by connection statuses (see below for values)authConfigIds- Filter by auth config IDslimit- Results per pagecursor- Pagination cursororderBy- 'created_at' or 'updated_at'
Update Connected Accounts
// Enable/disable
await composio.connectedAccounts.enable('conn_abc123');
await composio.connectedAccounts.disable('conn_abc123');
// Refresh credentials (expired OAuth tokens)
await composio.connectedAccounts.refresh('conn_abc123');
Delete Connected Accounts
await composio.connectedAccounts.delete('conn_abc123');
Warning: Permanent deletion. User must re-authenticate.
Wait for Connection Completion
For async OAuth flows:
// Default timeout (60 seconds)
const account = await composio.connectedAccounts.waitForConnection('conn_123');
// Custom timeout (2 minutes)
const account = await composio.connectedAccounts.waitForConnection('conn_123', 120000);
Errors:
ComposioConnectedAccountNotFoundError- Account doesn't existConnectionRequestFailedError- Connection failed/expiredConnectionRequestTimeoutError- Timeout exceeded
Common Patterns
OAuth Flow
// Create connection
async function connectUser(userId, authConfigId) {
const request = await composio.connectedAccounts.link(
userId,
authConfigId,
{ callbackUrl: 'https://app.com/callback' }
);
return { redirectUrl: request.redirectUrl };
}
// Handle callback
async function handleCallback(connectionId) {
try {
const account = await composio.connectedAccounts.waitForConnection(
connectionId,
180000
);
return { success: true, account };
} catch (error) {
if (error.name === 'ConnectionRequestTimeoutError') {
return { error: 'Timeout. Please try again.' };
}
throw error;
}
}
Check Active Connections
// Filter by status using statuses parameter
async function getUserActiveConnections(userId) {
const accounts = await composio.connectedAccounts.list({
userIds: [userId],
statuses: ['ACTIVE']
});
return accounts.items;
}
// Check multiple statuses
async function getUserConnectionsByStatus(userId) {
const accounts = await composio.connectedAccounts.list({
userIds: [userId],
statuses: ['ACTIVE', 'EXPIRED', 'FAILED']
});
return accounts.items;
}
async function isToolkitConnected(userId, toolkit) {
const accounts = await composio.connectedAccounts.list({
userIds: [userId],
toolkitSlugs: [toolkit],
statuses: ['ACTIVE']
});
return accounts.items.length > 0;
}
Available statuses:
INITIALIZING- Connection being set upINITIATED- Connection initiated, awaiting completionACTIVE- Connection active and ready to useFAILED- Connection failedEXPIRED- Credentials expiredINACTIVE- Connection disabled
Key Points
- Prefer link() - Security, UI, and whitelabeling handled
- Store account IDs - Save in your database, associate with users
- Check status - Verify ACTIVE before use, refresh on errors
- Handle lifecycle - Disable instead of delete when possible