User Management

Manage users and their connected accounts

What are User IDs?

User IDs determine whose connected accounts and data you're accessing in Composio. Every tool execution, connection authorization, and account operation requires a userId parameter that identifies which context to use.

User IDs act as containers that group connected accounts together across toolkits. Depending on your application, you can use User IDs to represent an individual user, a team, or an entire organization.

Quick Decision Guide

How do users access connected accounts in your app?

  • Each user connects their own personal accounts? Use User IDs Use your database UUID or primary key (e.g., user.id) Example: Users connect their personal Gmail, GitHub

  • Teams share the same connected accounts? Use Organization IDs Use your organization UUID or primary key (e.g., organization.id) Example: Company Slack workspace

Patterns

User IDs (Individual Accounts)

In production applications with multiple users, where each user connects and manages their own accounts.

Choosing User IDs:

  • Recommended: Database UUID or primary key (user.id)
  • Acceptable: Unique username (user.username)
  • Avoid: Email addresses (emails can change)
# Use your database's user ID (UUID, primary key, etc.)
user_id = user.id; # e.g., "550e8400-e29b-41d4-a716-446655440000"

tools = composio.tools.get(
  user_id=user_id,
  toolkits=["GITHUB"],
)

result = composio.tools.execute(
  "GITHUB_GET_REPO",
  user_id=user_id,
  arguments={
    "owner": 'example',
    "repo": 'repo'
  }
)
// Use your database's user ID (UUID, primary key, etc.)
const const userId: stringuserId = 
const user: {
    id: string;
}
user
.id: stringid; // e.g., "550e8400-e29b-41d4-a716-446655440000"
const const tools: OpenAiToolCollectiontools = await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.get<OpenAIProvider>(userId: string, filters: ToolListParams, options?: ToolOptions | undefined): Promise<OpenAiToolCollection> (+1 overload)
Get a list of tools from Composio based on filters. This method fetches the tools from the Composio API and wraps them using the provider.
@paramuserId - The user id to get the tools for@paramfilters - The filters to apply when fetching tools@paramoptions - Optional provider options including modifiers@returnsThe wrapped tools collection@example```typescript // Get tools from the GitHub toolkit const tools = await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); // Get tools with search const searchTools = await composio.tools.get('default', { search: 'user', limit: 10 }); // Get a specific tool by slug const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); // Get a tool with schema modifications const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', { modifySchema: (toolSlug, toolkitSlug, schema) => { // Customize the tool schema return {...schema, description: 'Custom description'}; } }); ```
get
(const userId: stringuserId, {
toolkits: [string]toolkits: ['github'], }); const
const result: {
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}
result
= await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.execute(slug: string, body: ToolExecuteParams, modifiers?: ExecuteToolModifiers): Promise<ToolExecuteResponse>
Executes a given tool with the provided parameters. This method calls the Composio API or a custom tool handler to execute the tool and returns the response. It automatically determines whether to use a custom tool or a Composio API tool based on the slug. **Version Control:** By default, manual tool execution requires a specific toolkit version. If the version resolves to "latest", the execution will throw a `ComposioToolVersionRequiredError` unless `dangerouslySkipVersionCheck` is set to `true`. This helps prevent unexpected behavior when new toolkit versions are released.
@paramslug - The slug/ID of the tool to be executed@parambody - The parameters to be passed to the tool@parambody.version - The specific version of the tool to execute (e.g., "20250909_00")@parambody.dangerouslySkipVersionCheck - Skip version validation for "latest" version (use with caution)@parambody.userId - The user ID to execute the tool for@parambody.connectedAccountId - The connected account ID to use for authenticated tools@parambody.arguments - The arguments to pass to the tool@parammodifiers - Optional modifiers to transform the request or response@returns- The response from the tool execution@throws{ComposioCustomToolsNotInitializedError} If the CustomTools instance is not initialized@throws{ComposioConnectedAccountNotFoundError} If the connected account is not found@throws{ComposioToolNotFoundError} If the tool with the given slug is not found@throws{ComposioToolVersionRequiredError} If version resolves to "latest" and dangerouslySkipVersionCheck is not true@throws{ComposioToolExecutionError} If there is an error during tool execution@exampleExecute with a specific version (recommended for production) ```typescript const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio' } }); ```@exampleExecute with dangerouslySkipVersionCheck (not recommended for production) ```typescript const result = await composio.tools.execute('HACKERNEWS_GET_USER', { userId: 'default', arguments: { userId: 'pg' }, dangerouslySkipVersionCheck: true // Allows execution with "latest" version }); ```@exampleExecute with SDK-level toolkit versions configuration ```typescript // If toolkitVersions are set during Composio initialization, no need to pass version const composio = new Composio({ toolkitVersions: { github: '20250909_00' } }); const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', arguments: { owner: 'composio' } }); ```@exampleExecute with modifiers ```typescript const result = await composio.tools.execute('GITHUB_GET_ISSUES', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio', repo: 'sdk' } }, { beforeExecute: ({ toolSlug, toolkitSlug, params }) => { console.log(`Executing ${toolSlug} from ${toolkitSlug}`); return params; }, afterExecute: ({ toolSlug, toolkitSlug, result }) => { console.log(`Completed ${toolSlug}`); return result; } }); ```
execute
('GITHUB_GET_REPO', {
userId?: string | undefineduserId: const userId: stringuserId, arguments?: Record<string, unknown> | undefinedarguments: { owner: stringowner: 'example', repo: stringrepo: 'repo' }, });

Never use 'default' as a User ID in production with users. This could expose other users' data.

Organization IDs (Team Accounts)

For applications where teams share connections - one admin connects accounts, all team members use them.

When to use:

  • Team tools: Slack, Microsoft Teams, Jira
  • Shared accounts: support@company.com, company GitHub org
  • Enterprise apps: IT manages connections for all employees
# Use the organization ID as userId
user_id = organization.id # e.g., "org_550e8400"

# All users in the organization share the same connected accounts
tools = composio.tools.get(
  user_id=user_id,
  toolkits=["SLACK"],
)

# Execute tools in the organization context
result = composio.tools.execute(
  "SLACK_SEND_MESSAGE",
  user_id=user_id,
  arguments={
    "channel": '#general',
    "text": 'Hello from the team!'
  }
)
// Use the organization ID as userId
const const userId: stringuserId = 
const organization: {
    id: string;
}
organization
.id: stringid; // e.g., "org_550e8400"
// All users in the organization share the same connected accounts const const tools: OpenAiToolCollectiontools = await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.get<OpenAIProvider>(userId: string, filters: ToolListParams, options?: ToolOptions | undefined): Promise<OpenAiToolCollection> (+1 overload)
Get a list of tools from Composio based on filters. This method fetches the tools from the Composio API and wraps them using the provider.
@paramuserId - The user id to get the tools for@paramfilters - The filters to apply when fetching tools@paramoptions - Optional provider options including modifiers@returnsThe wrapped tools collection@example```typescript // Get tools from the GitHub toolkit const tools = await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); // Get tools with search const searchTools = await composio.tools.get('default', { search: 'user', limit: 10 }); // Get a specific tool by slug const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); // Get a tool with schema modifications const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', { modifySchema: (toolSlug, toolkitSlug, schema) => { // Customize the tool schema return {...schema, description: 'Custom description'}; } }); ```
get
(const userId: stringuserId, {
toolkits: [string]toolkits: ['slack'], }); // Execute tools in the organization context const
const result: {
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}
result
= await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.execute(slug: string, body: ToolExecuteParams, modifiers?: ExecuteToolModifiers): Promise<ToolExecuteResponse>
Executes a given tool with the provided parameters. This method calls the Composio API or a custom tool handler to execute the tool and returns the response. It automatically determines whether to use a custom tool or a Composio API tool based on the slug. **Version Control:** By default, manual tool execution requires a specific toolkit version. If the version resolves to "latest", the execution will throw a `ComposioToolVersionRequiredError` unless `dangerouslySkipVersionCheck` is set to `true`. This helps prevent unexpected behavior when new toolkit versions are released.
@paramslug - The slug/ID of the tool to be executed@parambody - The parameters to be passed to the tool@parambody.version - The specific version of the tool to execute (e.g., "20250909_00")@parambody.dangerouslySkipVersionCheck - Skip version validation for "latest" version (use with caution)@parambody.userId - The user ID to execute the tool for@parambody.connectedAccountId - The connected account ID to use for authenticated tools@parambody.arguments - The arguments to pass to the tool@parammodifiers - Optional modifiers to transform the request or response@returns- The response from the tool execution@throws{ComposioCustomToolsNotInitializedError} If the CustomTools instance is not initialized@throws{ComposioConnectedAccountNotFoundError} If the connected account is not found@throws{ComposioToolNotFoundError} If the tool with the given slug is not found@throws{ComposioToolVersionRequiredError} If version resolves to "latest" and dangerouslySkipVersionCheck is not true@throws{ComposioToolExecutionError} If there is an error during tool execution@exampleExecute with a specific version (recommended for production) ```typescript const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio' } }); ```@exampleExecute with dangerouslySkipVersionCheck (not recommended for production) ```typescript const result = await composio.tools.execute('HACKERNEWS_GET_USER', { userId: 'default', arguments: { userId: 'pg' }, dangerouslySkipVersionCheck: true // Allows execution with "latest" version }); ```@exampleExecute with SDK-level toolkit versions configuration ```typescript // If toolkitVersions are set during Composio initialization, no need to pass version const composio = new Composio({ toolkitVersions: { github: '20250909_00' } }); const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', arguments: { owner: 'composio' } }); ```@exampleExecute with modifiers ```typescript const result = await composio.tools.execute('GITHUB_GET_ISSUES', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio', repo: 'sdk' } }, { beforeExecute: ({ toolSlug, toolkitSlug, params }) => { console.log(`Executing ${toolSlug} from ${toolkitSlug}`); return params; }, afterExecute: ({ toolSlug, toolkitSlug, result }) => { console.log(`Completed ${toolSlug}`); return result; } }); ```
execute
('SLACK_SEND_MESSAGE', {
userId?: string | undefineduserId: const userId: stringuserId, arguments?: Record<string, unknown> | undefinedarguments: { channel: stringchannel: '#general', text: stringtext: 'Hello from the team!', }, });

Multiple Connected Accounts

A single User ID can have multiple connected accounts for the same toolkit. For example, a user might connect both their personal and work Gmail accounts.

Key concepts:

  • Each connected account gets a unique Connected Account ID
  • Multiple accounts can exist under the same User ID for any toolkit
  • You can specify which account to use when executing tools

Account selection:

  • Explicit: Specify the Connected Account ID to target a specific account
  • Default: If no Connected Account ID is provided, the most recently connected account is used

Examples

Organization-Based Application

In B2B applications, typically an admin connects accounts once and all team members share access. Here's a complete implementation:

Key concepts:

  • Admin performs the OAuth connection using organization ID
  • All team members execute tools using the same organization ID
  • Permission checks ensure users can only access their organization's connections
import { class Composio<TProvider extends BaseComposioProvider<unknown, unknown, unknown> = OpenAIProvider>
This is the core class for Composio. It is used to initialize the Composio SDK and provide a global configuration.
Composio
} from '@composio/core';
const const composio: Composio<OpenAIProvider>composio = new new Composio<OpenAIProvider>(config?: ComposioConfig<OpenAIProvider> | undefined): Composio<OpenAIProvider>
Creates a new instance of the Composio SDK. The constructor initializes the SDK with the provided configuration options, sets up the API client, and initializes all core models (tools, toolkits, etc.).
@paramconfig - Configuration options for the Composio SDK@paramconfig.apiKey - The API key for authenticating with the Composio API@paramconfig.baseURL - The base URL for the Composio API (defaults to production URL)@paramconfig.allowTracking - Whether to allow anonymous usage analytics@paramconfig.provider - The provider to use for this Composio instance (defaults to OpenAIProvider)@example```typescript // Initialize with default configuration const composio = new Composio(); // Initialize with custom API key and base URL const composio = new Composio({ apiKey: 'your-api-key', baseURL: 'https://api.composio.dev' }); // Initialize with custom provider const composio = new Composio({ apiKey: 'your-api-key', provider: new CustomProvider() }); ```
Composio
({
apiKey?: string | null | undefined
The API key for the Composio API.
@example'sk-1234567890'
apiKey
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The `process.env` property returns an object containing the user environment. See [`environ(7)`](http://man7.org/linux/man-pages/man7/environ.7.html). An example of this object looks like: ```js { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } ``` It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other `Worker` threads. In other words, the following example would not work: ```bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo ``` While the following will: ```js import { env } from 'node:process'; env.foo = 'bar'; console.log(env.foo); ``` Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. ```js import { env } from 'node:process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ``` Use `delete` to delete a property from `process.env`. ```js import { env } from 'node:process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ``` On Windows operating systems, environment variables are case-insensitive. ```js import { env } from 'node:process'; env.TEST = 1; console.log(env.test); // => 1 ``` Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
@sincev0.1.27
env
.string | undefinedCOMPOSIO_API_KEY,
}); // 1. Admin connects Slack for the entire organization async function function connectOrganizationToSlack(organizationId: string, adminUserId: string): Promise<string | null | undefined>connectOrganizationToSlack(organizationId: stringorganizationId: string, adminUserId: stringadminUserId: string) { // Use organization ID as userId in Composio const const connectionRequest: ConnectionRequestconnectionRequest = await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.toolkits: Toolkits
Retrieve toolkit metadata and authorize user connections
toolkits
.Toolkits.authorize(userId: string, toolkitSlug: string, authConfigId?: string): Promise<ConnectionRequest>
Authorizes a user to use a toolkit. This method will create an auth config if one doesn't exist and initiate a connection request.
@paramuserId - The user id of the user to authorize@paramtoolkitSlug - The slug of the toolkit to authorize@returnsThe connection request object@example```typescript const connectionRequest = await composio.toolkits.authorize(userId, 'github'); ```
authorize
(organizationId: stringorganizationId, 'slack');
// Store the connection request for the admin to complete await function storeConnectionRequest(orgId: string, adminId: string, req: ConnectionRequest): Promise<void>storeConnectionRequest(organizationId: stringorganizationId, adminUserId: stringadminUserId, const connectionRequest: ConnectionRequestconnectionRequest); return const connectionRequest: ConnectionRequestconnectionRequest.ConnectionRequestState.redirectUrl?: string | null | undefinedredirectUrl; } // 2. Any user in the organization can use the connected tools async function
function sendSlackMessage(organizationId: string, channel: string, message: string): Promise<{
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}>
sendSlackMessage
(organizationId: stringorganizationId: string, channel: stringchannel: string, message: stringmessage: string) {
return await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.execute(slug: string, body: ToolExecuteParams, modifiers?: ExecuteToolModifiers): Promise<ToolExecuteResponse>
Executes a given tool with the provided parameters. This method calls the Composio API or a custom tool handler to execute the tool and returns the response. It automatically determines whether to use a custom tool or a Composio API tool based on the slug. **Version Control:** By default, manual tool execution requires a specific toolkit version. If the version resolves to "latest", the execution will throw a `ComposioToolVersionRequiredError` unless `dangerouslySkipVersionCheck` is set to `true`. This helps prevent unexpected behavior when new toolkit versions are released.
@paramslug - The slug/ID of the tool to be executed@parambody - The parameters to be passed to the tool@parambody.version - The specific version of the tool to execute (e.g., "20250909_00")@parambody.dangerouslySkipVersionCheck - Skip version validation for "latest" version (use with caution)@parambody.userId - The user ID to execute the tool for@parambody.connectedAccountId - The connected account ID to use for authenticated tools@parambody.arguments - The arguments to pass to the tool@parammodifiers - Optional modifiers to transform the request or response@returns- The response from the tool execution@throws{ComposioCustomToolsNotInitializedError} If the CustomTools instance is not initialized@throws{ComposioConnectedAccountNotFoundError} If the connected account is not found@throws{ComposioToolNotFoundError} If the tool with the given slug is not found@throws{ComposioToolVersionRequiredError} If version resolves to "latest" and dangerouslySkipVersionCheck is not true@throws{ComposioToolExecutionError} If there is an error during tool execution@exampleExecute with a specific version (recommended for production) ```typescript const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio' } }); ```@exampleExecute with dangerouslySkipVersionCheck (not recommended for production) ```typescript const result = await composio.tools.execute('HACKERNEWS_GET_USER', { userId: 'default', arguments: { userId: 'pg' }, dangerouslySkipVersionCheck: true // Allows execution with "latest" version }); ```@exampleExecute with SDK-level toolkit versions configuration ```typescript // If toolkitVersions are set during Composio initialization, no need to pass version const composio = new Composio({ toolkitVersions: { github: '20250909_00' } }); const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', arguments: { owner: 'composio' } }); ```@exampleExecute with modifiers ```typescript const result = await composio.tools.execute('GITHUB_GET_ISSUES', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio', repo: 'sdk' } }, { beforeExecute: ({ toolSlug, toolkitSlug, params }) => { console.log(`Executing ${toolSlug} from ${toolkitSlug}`); return params; }, afterExecute: ({ toolSlug, toolkitSlug, result }) => { console.log(`Completed ${toolSlug}`); return result; } }); ```
execute
('SLACK_SEND_MESSAGE', {
userId?: string | undefineduserId: organizationId: stringorganizationId, // organization ID, not individual user ID arguments?: Record<string, unknown> | undefinedarguments: { channel: stringchannel: channel: stringchannel, text: stringtext: message: stringmessage, }, }); } // 3. Check if organization has required connections async function function getOrganizationTools(organizationId: string): Promise<OpenAiToolCollection>getOrganizationTools(organizationId: stringorganizationId: string) { return await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.get<OpenAIProvider>(userId: string, filters: ToolListParams, options?: ToolOptions | undefined): Promise<OpenAiToolCollection> (+1 overload)
Get a list of tools from Composio based on filters. This method fetches the tools from the Composio API and wraps them using the provider.
@paramuserId - The user id to get the tools for@paramfilters - The filters to apply when fetching tools@paramoptions - Optional provider options including modifiers@returnsThe wrapped tools collection@example```typescript // Get tools from the GitHub toolkit const tools = await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); // Get tools with search const searchTools = await composio.tools.get('default', { search: 'user', limit: 10 }); // Get a specific tool by slug const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); // Get a tool with schema modifications const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', { modifySchema: (toolSlug, toolkitSlug, schema) => { // Customize the tool schema return {...schema, description: 'Custom description'}; } }); ```
get
(organizationId: stringorganizationId, {
toolkits: [string, string, string]toolkits: ['slack', 'github', 'jira'], }); } // Usage in your API endpoint
const app: {
    post: (path: string, handler: (req: any, res: any) => Promise<any>) => void;
}
app
.post: (path: string, handler: (req: any, res: any) => Promise<any>) => voidpost('/api/slack/message', async (req: anyreq, res: anyres) => {
const { const channel: anychannel, const message: anymessage } = req: anyreq.body; const const organizationId: anyorganizationId = req: anyreq.user.organizationId; // Get from your auth system // Verify user has permission to send messages for this organization if (!(await function userCanSendMessages(userId: string, orgId: string): Promise<boolean>userCanSendMessages(req: anyreq.user.id, const organizationId: anyorganizationId))) { return res: anyres.status(403).json({ error: stringerror: 'Insufficient permissions' }); } try { const
const result: {
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}
result
= await
function sendSlackMessage(organizationId: string, channel: string, message: string): Promise<{
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}>
sendSlackMessage
(const organizationId: anyorganizationId, const channel: anychannel, const message: anymessage);
res: anyres.json(
const result: {
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}
result
.data: Record<string, unknown>data);
} catch (function (local var) error: unknownerror) { res: anyres.status(500).json({ error: stringerror: 'Failed to send message' }); } });

Multi-User Application

In B2C applications, each user connects and manages their own accounts. Every user goes through their own OAuth flow and their data remains completely isolated.

Key concepts:

  • Each user authorizes their own accounts using their unique user ID
  • Connections are isolated - users can only access their own connected accounts
  • No permission checks needed since users only access their own data
import { class Composio<TProvider extends BaseComposioProvider<unknown, unknown, unknown> = OpenAIProvider>
This is the core class for Composio. It is used to initialize the Composio SDK and provide a global configuration.
Composio
} from '@composio/core';
const const composio: Composio<OpenAIProvider>composio = new new Composio<OpenAIProvider>(config?: ComposioConfig<OpenAIProvider> | undefined): Composio<OpenAIProvider>
Creates a new instance of the Composio SDK. The constructor initializes the SDK with the provided configuration options, sets up the API client, and initializes all core models (tools, toolkits, etc.).
@paramconfig - Configuration options for the Composio SDK@paramconfig.apiKey - The API key for authenticating with the Composio API@paramconfig.baseURL - The base URL for the Composio API (defaults to production URL)@paramconfig.allowTracking - Whether to allow anonymous usage analytics@paramconfig.provider - The provider to use for this Composio instance (defaults to OpenAIProvider)@example```typescript // Initialize with default configuration const composio = new Composio(); // Initialize with custom API key and base URL const composio = new Composio({ apiKey: 'your-api-key', baseURL: 'https://api.composio.dev' }); // Initialize with custom provider const composio = new Composio({ apiKey: 'your-api-key', provider: new CustomProvider() }); ```
Composio
({
apiKey?: string | null | undefined
The API key for the Composio API.
@example'sk-1234567890'
apiKey
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv
The `process.env` property returns an object containing the user environment. See [`environ(7)`](http://man7.org/linux/man-pages/man7/environ.7.html). An example of this object looks like: ```js { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } ``` It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other `Worker` threads. In other words, the following example would not work: ```bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo ``` While the following will: ```js import { env } from 'node:process'; env.foo = 'bar'; console.log(env.foo); ``` Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean. ```js import { env } from 'node:process'; env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ``` Use `delete` to delete a property from `process.env`. ```js import { env } from 'node:process'; env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ``` On Windows operating systems, environment variables are case-insensitive. ```js import { env } from 'node:process'; env.TEST = 1; console.log(env.test); // => 1 ``` Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
@sincev0.1.27
env
.string | undefinedCOMPOSIO_API_KEY,
}); // 1. User initiates GitHub connection async function function connectUserToGitHub(userId: string): Promise<string | null | undefined>connectUserToGitHub(userId: stringuserId: string) { const const connectionRequest: ConnectionRequestconnectionRequest = await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.toolkits: Toolkits
Retrieve toolkit metadata and authorize user connections
toolkits
.Toolkits.authorize(userId: string, toolkitSlug: string, authConfigId?: string): Promise<ConnectionRequest>
Authorizes a user to use a toolkit. This method will create an auth config if one doesn't exist and initiate a connection request.
@paramuserId - The user id of the user to authorize@paramtoolkitSlug - The slug of the toolkit to authorize@returnsThe connection request object@example```typescript const connectionRequest = await composio.toolkits.authorize(userId, 'github'); ```
authorize
(userId: stringuserId, 'github');
return const connectionRequest: ConnectionRequestconnectionRequest.ConnectionRequestState.redirectUrl?: string | null | undefinedredirectUrl; } // 2. Get user's connected GitHub tools async function function getUserGitHubTools(userId: string): Promise<OpenAiToolCollection>getUserGitHubTools(userId: stringuserId: string) { return await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.get<OpenAIProvider>(userId: string, filters: ToolListParams, options?: ToolOptions | undefined): Promise<OpenAiToolCollection> (+1 overload)
Get a list of tools from Composio based on filters. This method fetches the tools from the Composio API and wraps them using the provider.
@paramuserId - The user id to get the tools for@paramfilters - The filters to apply when fetching tools@paramoptions - Optional provider options including modifiers@returnsThe wrapped tools collection@example```typescript // Get tools from the GitHub toolkit const tools = await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); // Get tools with search const searchTools = await composio.tools.get('default', { search: 'user', limit: 10 }); // Get a specific tool by slug const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); // Get a tool with schema modifications const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', { modifySchema: (toolSlug, toolkitSlug, schema) => { // Customize the tool schema return {...schema, description: 'Custom description'}; } }); ```
get
(userId: stringuserId, {
toolkits: [string]toolkits: ['github'], }); } // 3. Execute tool for specific user async function
function getUserRepos(userId: string): Promise<{
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}>
getUserRepos
(userId: stringuserId: string) {
return await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.execute(slug: string, body: ToolExecuteParams, modifiers?: ExecuteToolModifiers): Promise<ToolExecuteResponse>
Executes a given tool with the provided parameters. This method calls the Composio API or a custom tool handler to execute the tool and returns the response. It automatically determines whether to use a custom tool or a Composio API tool based on the slug. **Version Control:** By default, manual tool execution requires a specific toolkit version. If the version resolves to "latest", the execution will throw a `ComposioToolVersionRequiredError` unless `dangerouslySkipVersionCheck` is set to `true`. This helps prevent unexpected behavior when new toolkit versions are released.
@paramslug - The slug/ID of the tool to be executed@parambody - The parameters to be passed to the tool@parambody.version - The specific version of the tool to execute (e.g., "20250909_00")@parambody.dangerouslySkipVersionCheck - Skip version validation for "latest" version (use with caution)@parambody.userId - The user ID to execute the tool for@parambody.connectedAccountId - The connected account ID to use for authenticated tools@parambody.arguments - The arguments to pass to the tool@parammodifiers - Optional modifiers to transform the request or response@returns- The response from the tool execution@throws{ComposioCustomToolsNotInitializedError} If the CustomTools instance is not initialized@throws{ComposioConnectedAccountNotFoundError} If the connected account is not found@throws{ComposioToolNotFoundError} If the tool with the given slug is not found@throws{ComposioToolVersionRequiredError} If version resolves to "latest" and dangerouslySkipVersionCheck is not true@throws{ComposioToolExecutionError} If there is an error during tool execution@exampleExecute with a specific version (recommended for production) ```typescript const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio' } }); ```@exampleExecute with dangerouslySkipVersionCheck (not recommended for production) ```typescript const result = await composio.tools.execute('HACKERNEWS_GET_USER', { userId: 'default', arguments: { userId: 'pg' }, dangerouslySkipVersionCheck: true // Allows execution with "latest" version }); ```@exampleExecute with SDK-level toolkit versions configuration ```typescript // If toolkitVersions are set during Composio initialization, no need to pass version const composio = new Composio({ toolkitVersions: { github: '20250909_00' } }); const result = await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', arguments: { owner: 'composio' } }); ```@exampleExecute with modifiers ```typescript const result = await composio.tools.execute('GITHUB_GET_ISSUES', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio', repo: 'sdk' } }, { beforeExecute: ({ toolSlug, toolkitSlug, params }) => { console.log(`Executing ${toolSlug} from ${toolkitSlug}`); return params; }, afterExecute: ({ toolSlug, toolkitSlug, result }) => { console.log(`Completed ${toolSlug}`); return result; } }); ```
execute
('GITHUB_LIST_REPOS', {
userId?: string | undefineduserId: userId: stringuserId, arguments?: Record<string, unknown> | undefinedarguments: { per_page: numberper_page: 10, }, }); } // Usage in your API endpoint
const app: {
    get: (path: string, handler: (req: any, res: any) => Promise<any>) => void;
}
app
.get: (path: string, handler: (req: any, res: any) => Promise<any>) => voidget('/api/github/repos', async (req: anyreq, res: anyres) => {
const const userId: anyuserId = req: anyreq.user.id; // Get from your auth system try { const
const repos: {
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}
repos
= await
function getUserRepos(userId: string): Promise<{
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}>
getUserRepos
(const userId: anyuserId);
res: anyres.json(
const repos: {
    error: string | null;
    data: Record<string, unknown>;
    successful: boolean;
    logId?: string | undefined;
    sessionInfo?: unknown;
}
repos
.data: Record<string, unknown>data);
} catch (function (local var) error: unknownerror) { res: anyres.status(500).json({ error: stringerror: 'Failed to fetch repos' }); } });

Data isolation: Composio ensures each userId's connections and data are completely separate. User A can never access User B's repositories.

Hybrid Pattern

Many applications need both personal and team resources. Users might connect their personal Gmail while sharing the company Slack workspace.

Common scenarios:

  • Personal calendars + shared project management
  • Individual GitHub accounts + organization repositories
# Wrong: Using individual user ID for org-connected tool
user_tools = composio.tools.get(
    user_id="user_123",  # Individual user ID
    toolkits=["slack"]  # Fails - Slack is connected at org level
)

# Correct: Match the ID type to how the tool was connected
user_personal_tools = composio.tools.get(
    user_id="user_123",  # Individual user ID
    toolkits=["gmail"]  # User's personal Gmail
)

org_shared_tools = composio.tools.get(
    user_id="org_123",  # Organization ID
    toolkits=["slack", "jira"]  # Organization's shared tools
)
// Wrong: Using individual user ID for org-connected tool
const const userTools: OpenAiToolCollectionuserTools = await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.get<OpenAIProvider>(userId: string, filters: ToolListParams, options?: ToolOptions | undefined): Promise<OpenAiToolCollection> (+1 overload)
Get a list of tools from Composio based on filters. This method fetches the tools from the Composio API and wraps them using the provider.
@paramuserId - The user id to get the tools for@paramfilters - The filters to apply when fetching tools@paramoptions - Optional provider options including modifiers@returnsThe wrapped tools collection@example```typescript // Get tools from the GitHub toolkit const tools = await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); // Get tools with search const searchTools = await composio.tools.get('default', { search: 'user', limit: 10 }); // Get a specific tool by slug const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); // Get a tool with schema modifications const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', { modifySchema: (toolSlug, toolkitSlug, schema) => { // Customize the tool schema return {...schema, description: 'Custom description'}; } }); ```
get
(
const req: {
    user: {
        id: string;
        organizationId: string;
    };
}
req
.
user: {
    id: string;
    organizationId: string;
}
user
.id: stringid, {
toolkits: [string]toolkits: ['slack'], // Fails - Slack is connected at org level }); // Correct: Match the ID type to how the tool was connected const const userPersonalTools: OpenAiToolCollectionuserPersonalTools = await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.get<OpenAIProvider>(userId: string, filters: ToolListParams, options?: ToolOptions | undefined): Promise<OpenAiToolCollection> (+1 overload)
Get a list of tools from Composio based on filters. This method fetches the tools from the Composio API and wraps them using the provider.
@paramuserId - The user id to get the tools for@paramfilters - The filters to apply when fetching tools@paramoptions - Optional provider options including modifiers@returnsThe wrapped tools collection@example```typescript // Get tools from the GitHub toolkit const tools = await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); // Get tools with search const searchTools = await composio.tools.get('default', { search: 'user', limit: 10 }); // Get a specific tool by slug const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); // Get a tool with schema modifications const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', { modifySchema: (toolSlug, toolkitSlug, schema) => { // Customize the tool schema return {...schema, description: 'Custom description'}; } }); ```
get
(
const req: {
    user: {
        id: string;
        organizationId: string;
    };
}
req
.
user: {
    id: string;
    organizationId: string;
}
user
.id: stringid, {
toolkits: [string]toolkits: ['gmail'], // User's personal Gmail }); const const orgSharedTools: OpenAiToolCollectionorgSharedTools = await const composio: Composio<OpenAIProvider>composio.Composio<OpenAIProvider>.tools: Tools<unknown, unknown, OpenAIProvider>
List, retrieve, and execute tools
tools
.Tools<unknown, unknown, OpenAIProvider>.get<OpenAIProvider>(userId: string, filters: ToolListParams, options?: ToolOptions | undefined): Promise<OpenAiToolCollection> (+1 overload)
Get a list of tools from Composio based on filters. This method fetches the tools from the Composio API and wraps them using the provider.
@paramuserId - The user id to get the tools for@paramfilters - The filters to apply when fetching tools@paramoptions - Optional provider options including modifiers@returnsThe wrapped tools collection@example```typescript // Get tools from the GitHub toolkit const tools = await composio.tools.get('default', { toolkits: ['github'], limit: 10 }); // Get tools with search const searchTools = await composio.tools.get('default', { search: 'user', limit: 10 }); // Get a specific tool by slug const hackerNewsUserTool = await composio.tools.get('default', 'HACKERNEWS_GET_USER'); // Get a tool with schema modifications const tool = await composio.tools.get('default', 'GITHUB_GET_REPOS', { modifySchema: (toolSlug, toolkitSlug, schema) => { // Customize the tool schema return {...schema, description: 'Custom description'}; } }); ```
get
(
const req: {
    user: {
        id: string;
        organizationId: string;
    };
}
req
.
user: {
    id: string;
    organizationId: string;
}
user
.organizationId: stringorganizationId, {
toolkits: [string, string]toolkits: ['slack', 'jira'], // Organization's shared tools });

Remember: The userId must match how the account was connected. If admin connected Slack with org ID, all members must use org ID to access it.

Best Practices

Your responsibilities:

  • Pass the correct User ID for each user
  • Verify user permissions before executing organization tools
  • Never use 'default' in production with multiple users
  • Keep User IDs consistent across your application and Composio
  • Use stable identifiers that won't change over time

Data isolation: Composio ensures complete isolation between User IDs. Users cannot access another ID's connections or data.