# Authenticating Tools The first step in authenticating your users is to create an **Auth Config**. Every toolkit has its own authentication method such as `OAuth`, `API key`, `Basic Auth`, or custom schemes. An **Auth Config** is a blueprint that defines how authentication works for a toolkit across all your users. It defines: 1. **Authentication method** - `OAuth2`, `Bearer token`, `API key`, or `Basic Auth` 2. **Scopes** - what actions your tools can perform 3. **Credentials** - whether you'll use your own app credentials or Composio's managed auth ## Creating an auth config ### Using the Dashboard ### Selecting a toolkit Navigate to [Auth Configs](https://platform.composio.dev?next_page=%2Fauth-configs) tab in your dashboard and click "**Create Auth Config**". Find and select the toolkit you want to integrate (e.g., **Gmail**, **Slack**, **GitHub**). ### Selecting the Authentication method Each toolkit supports different authentication methods such as **OAuth**, **API Key**, **Bearer Token**. Select from the available options for your toolkit. ### Configure scopes Depending on your authentication method, you may need to configure scopes: * **OAuth2**: Configure scopes for what data and actions your integration can access. * **API Key/Bearer Token**: Permissions are typically fixed based on the key's access level. ### Authentication Management **For OAuth toolkits:** * **Development/Testing**: Use Composio's managed authentication (no setup required) * **Production**: Generate your own OAuth credentials from the toolkit's developer portal **For custom authentication schemes:** You must provide your own credentials regardless of environment. Want to remove Composio branding from OAuth screens? See [Custom Auth Configs](/docs/custom-auth-configs#white-labeling-the-oauth-consent-screen) for white-labeling options. ### You are all set! Click "**Create Auth Configuration**" button and you have completed your first step! Now you can move ahead to authenticating your users by [Connecting an Account](#connecting-an-account). Auth configs contain your developer credentials and app-level settings (*scopes*, *authentication method*, etc.). Once created, you can reuse the same auth config for all your users. ### When to create multiple auth configs? You should create multiple auth configs for the same toolkit when you need: * **Different authentication methods** - One OAuth config and one API key config * **Different scopes** - Separate configs for read-only vs full access * **Different OAuth apps** - Using separate client credentials for different environments * **Different permission levels** - Limiting actions for specific use cases For managing auth configs across multiple projects, you can create them programmatically via the API Remove Composio branding from OAuth screens for a fully white-labeled authentication experience ## Connecting an account With an auth config created, you're ready to authenticate your users! You can either use [**Connect Link**](#hosted-authentication-connect-link) for a hosted authentication flow, or use [**Direct SDK Integration**](#direct-sdk-integration). User authentication requires a User ID - a unique identifier that groups connected accounts together. Learn more about [User Management](/docs/user-management) to understand how to structure User IDs for your application. **Choose the section below that matches your toolkit's authentication method:** ### Hosted Authentication (Connect Link) Redirect users to a Composio-hosted URL that handles the entire authentication process—OAuth flows, API key collection, or custom fields like subdomain. You can specify a callback URL to control where users return after authentication.
```python from composio import Composio composio = Composio(api_key="your_api_key") # Use the "AUTH CONFIG ID" from your dashboard auth_config_id = "your_auth_config_id" # Use a unique identifier for each user in your application user_id = 'user-1349-129-12' connection_request = composio.connected_accounts.link( user_id=user_id, auth_config_id=auth_config_id, callback_url='https://your-app.com/callback' ) redirect_url = connection_request.redirect_url print(f"Visit: {redirect_url} to authenticate your account") ``` ```typescript import { Composio } from '@composio/core'; const composio = new Composio({apiKey: "your_api_key"}); // Use the "AUTH CONFIG ID" from your dashboard const authConfigId = 'your_auth_config_id'; // Use a unique identifier for each user in your application const userId = 'user-1349-129-12'; const connectionRequest = await composio.connectedAccounts.link(userId, authConfigId, { callbackUrl: 'https://your-app.com/callback' }); const redirectUrl = connectionRequest.redirectUrl; console.log(`Visit: ${redirectUrl} to authenticate your account`); ``` #### Customizing Connect Link By default, users will see a Composio-branded authentication experience when connecting their accounts. To customize this interface with your application's branding: 1. Navigate to your Project Settings and select [Auth Screen](https://platform.composio.dev?next_page=/settings/auth-screen) 2. Configure your **Logo** and **App Title** These settings will apply to all authentication flows using Connect Link, providing a white-labeled experience that maintains your brand identity throughout the authentication process. For complete white-labeling including OAuth consent screens (removing Composio's domain), see [Custom Auth Configs - White-labeling](/docs/custom-auth-configs#white-labeling-the-oauth-consent-screen). ### Direct SDK Integration **Choose the section below that matches your toolkit's authentication method:** #### OAuth Connections For OAuth flows, you'll redirect users to complete authorization. You can specify a callback URL to control where users return after authentication: ```python from composio import Composio composio = Composio(api_key="YOUR_COMPOSIO_API_KEY") # Use the "AUTH CONFIG ID" from your dashboard auth_config_id = "your_auth_config_id" # Use a unique identifier for each user in your application user_id = "user-1349-129-12" connection_request = composio.connected_accounts.initiate( user_id=user_id, auth_config_id=auth_config_id, config={"auth_scheme": "OAUTH2"}, callback_url="https://www.yourapp.com/callback" ) print(f"Redirect URL: {connection_request.redirect_url}") connected_account = connection_request.wait_for_connection() # Alternative: if you only have the connection request ID # connected_account = composio.connected_accounts.wait_for_connection( # connection_request.id) # Recommended when the connection_request object is no longer available print(f"Connection established: {connected_account.id}") ``` ```typescript import { Composio } from '@composio/core'; const composio = new Composio({apiKey: "YOUR_COMPOSIO_API_KEY"}); // Use the "AUTH CONFIG ID" from your dashboard const authConfigId = 'your_auth_config_id'; // Use a unique identifier for each user in your application const userId = 'user_4567'; const connRequest = await composio.connectedAccounts.initiate( userId, authConfigId, { callbackUrl: 'https://www.yourapp.com/callback', } ); console.log(`Redirect URL: ${connRequest.redirectUrl}`); const connectedAccount = await connRequest.waitForConnection(); // Alternative: if you only have the connection request ID // const connectedAccount = await composio.connectedAccounts // .waitForConnection(connRequest.id); // Recommended when the connRequest object is no longer available console.log(`Connection established: ${connectedAccount.id}`); ``` #### Services with Additional Parameters Some services like Zendesk require additional parameters such as `subdomain`: ```python # For Zendesk - include subdomain connection_request = composio.connected_accounts.initiate( user_id=user_id, auth_config_id=auth_config_id, config=auth_scheme.oauth2(subdomain="mycompany") # For mycompany.zendesk.com ) ``` ```typescript import { AuthScheme } from '@composio/core'; // For Zendesk - include subdomain const connRequest = await composio.connectedAccounts.initiate(userId, authConfigId, { config: AuthScheme.OAuth2({ subdomain: 'mycompany', }), }); ``` #### API Key Connections For API key authentication, you can either collect API keys from each user or use your own API key for all users. Popular toolkits that use API keys include Stripe, Perplexity, etc. Here is how to initiate the flow: ```python from composio import Composio composio = Composio(api_key="your_api_key") # Use the "AUTH CONFIG ID" from your dashboard auth_config_id = "your_auth_config_id" # Use a unique identifier for each user in your application user_id = "user_12323" # API key provided by the user (collected from your app's UI) # or use your own key user_api_key = "user_api_key_here" connection_request = composio.connected_accounts.initiate( user_id=user_id, auth_config_id=auth_config_id, config={ "auth_scheme": "API_KEY", "val": {"api_key": user_api_key} } ) print(f"Connection established: {connection_request.id}") ``` ```typescript import { Composio, AuthScheme } from '@composio/core'; const composio = new Composio({ apiKey: 'your_api_key' }); // Use the "AUTH CONFIG ID" from your dashboard const authConfigId = 'your_auth_config_id'; // Use a unique identifier for each user in your application const userId = 'user12345678'; // API key provided by the user (collected from your app's UI) const userApiKey = 'user_api_key_here'; const connectionRequest = await composio.connectedAccounts.initiate(userId, authConfigId, { config: AuthScheme.APIKey({ api_key: userApiKey, }), }); console.log(`Connection established: ${connectionRequest.id}`); ``` ## Fetching the required config parameters for an Auth Config When working with any toolkit, you can inspect an auth config to understand its authentication requirements and expected parameters. Here is how you would fetch the authentication method and input fields: ```python from composio import Composio composio = Composio(api_key="your_api_key") # Use the "AUTH CONFIG ID" from your dashboard auth_config_id = "your_auth_config_id" # Fetch the auth configuration details auth_config = composio.auth_configs.get(auth_config_id) # Check what authentication method this config uses print(f"Authentication method: {auth_config.auth_scheme}") # See what input fields are required print(f"Required fields: {auth_config.expected_input_fields}") ``` ```typescript import { Composio } from '@composio/core'; const composio = new Composio({ apiKey: 'your_api_key' }); // Use the "AUTH CONFIG ID" from your dashboard const authConfigId = 'your_auth_config_id'; // Fetch the auth configuration details const authConfig = await composio.authConfigs.get(authConfigId); console.log(`Authentication method: ${authConfig.authScheme}`); console.log(`Required fields:`, authConfig.expectedInputFields); ``` ## Other Authentication Methods Composio also supports a wide range of other auth schemas: **Bearer Token** - Similar to API keys, provide the user's bearer token directly when creating the connection. **Basic Auth** - Provide username and password credentials for services that use HTTP Basic Authentication. **Custom Schemes** - Some toolkits use their own custom authentication methods. Follow the toolkit-specific requirements for such cases. For any of these methods, [fetch the config parameter](#fetching-the-required-config-parameters-for-an-auth-config) to determine the exact fields required. Every toolkit has its own requirements, and understanding these is essential for successfully creating connections. Learn how to [Manage connected accounts](/docs/connected-accounts) after users authenticate. ## Connection Statuses After creating a connection, it will have one of the following statuses that indicates its current state: | Status | Description | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | **ACTIVE** | Connection is established and working. You can execute tools with this connection. | | **INACTIVE** | Connection is temporarily disabled. Re-enable it to use the connection again. | | **PENDING** | Connection is being processed. Wait for it to become active. | | **INITIATED** | Connection request has started but not yet completed. User may still need to complete authentication. | | **EXPIRED** | Connection credentials have expired. Composio automatically attempts to refresh credentials before marking as expired. Re-authenticate to restore access. | | **FAILED** | Connection attempt failed. Check error details and try creating a new connection. | When credentials expire for OAuth connections, Composio automatically attempts to refresh them using the refresh token. The connection is only marked as **EXPIRED** after multiple refresh attempts have failed. ### Waiting for Connection Establishment The `waitForConnection` method allows you to poll for a connection to become active after initiating authentication. This is useful when you need to ensure a connection is ready before proceeding. ```python # Wait for the connection to be established connected_account = connection_request.wait_for_connection() print(connected_account.id) # Alternative: Wait with custom timeout # connected_account = connection_request.wait_for_connection(120) # 2 minute timeout # Alternative: If you only have the connection request ID (e.g., stored in database) # connection_id = connection_request.id # You can store this ID in your database # connected_account = composio.connected_accounts.wait_for_connection(connection_id, 60) ``` ```typescript // Wait for the connection to be established const connectedAccount = await connectionRequest.waitForConnection(); console.log(connectedAccount.id); // Alternative: Wait with custom timeout // const connectedAccount = await connectionRequest.waitForConnection(120000); // 2 minutes // Alternative: If you only have the connection request ID (e.g., stored in database) // const connectionId = connectionRequest.id; // You can store this ID in your database // const connectedAccount = await composio.connectedAccounts.waitForConnection(connectionId, 60000); ``` The method continuously polls the Composio API until the connection: * Becomes **ACTIVE** (returns the connected account) * Enters a terminal state like **FAILED** or **EXPIRED** (throws an error) * Exceeds the specified timeout (throws a timeout error) ### Checking Connection Status You can check the status of a connected account programmatically: ```python # Get a specific connected account connected_account = composio.connected_accounts.get("your_connected_account_id") print(f"Status: {connected_account.status}") # Filter connections by user_id, auth_config_id, and status (only active accounts) filtered_connections = composio.connected_accounts.list( user_ids=["user_123"], auth_config_ids=["your_auth_config_id"], statuses=["ACTIVE"] ) for connection in filtered_connections.items: print(f"{connection.id}: {connection.status}") ``` ```typescript // Get a specific connected account by its nanoid const connectedAccount = await composio.connectedAccounts.get('your_connected_account_id'); console.log(`Status: ${connectedAccount.status}`); // Filter connections by user_id, auth_config_id, and status (only active accounts) const filteredConnections = await composio.connectedAccounts.list({ userIds: ['user_123'], authConfigIds: ['your_auth_config_id'], statuses: ['ACTIVE'] }); filteredConnections.items.forEach(connection => { console.log(`${connection.id}: ${connection.status}`); }); ``` Only connections with **ACTIVE** status can be used to execute tools. If a connection is in any other state, you'll need to take appropriate action (re-authenticate, wait for processing, etc.) before using it. ## Next Step With authentication set up, you can now fetch and execute tools. See [Executing Tools](/docs/executing-tools) to get started. # Capabilities } title="Tool Router" description="Search, authenticate, and execute across all tools from one interface. Built-in Workbench and CodeAct solves context window limitations!" href="/tool-router/overview" /> } title="Managed Authentication" description="OAuth, API keys, token refresh—handled automatically." href="/docs/authenticating-tools" /> } title="Tools" description="10,000+ actions across 250+ apps. GitHub, Slack, Gmail, and more." href="/docs/fetching-tools" /> } title="MCP" description="Works with Claude Desktop, Cursor, and any MCP-compatible client." href="/docs/mcp-quickstart" /> } title="Triggers" description="React to events in real-time. Webhooks, new emails, GitHub events." href="/docs/using-triggers" /> # CLI The Composio CLI helps you generate type-safe code and manage your Composio workspace. ## Installation Install the Composio CLI using the installation script: ```bash curl -fsSL https://composio.dev/install | bash ``` Or using wget: ```bash wget -qO- https://composio.dev/install | bash ``` ## Authentication Manage your Composio authentication directly from the terminal. ### Login Authenticate with your Composio account: ```bash composio login ``` This opens your browser to complete authentication and stores your API key locally. To authenticate without opening a browser (useful for SSH/remote sessions): ```bash composio login --no-browser ``` This displays a URL to manually open in your browser. ### Check authentication status Verify your current authentication: ```bash composio whoami ``` This displays your current API key or indicates if you're not authenticated. ### Logout Remove stored authentication: ```bash composio logout ``` ## Generate type definitions Generate TypeScript or Python type definitions for all Composio tools. These types provide type safety when using direct tool execution (`composio.tools.execute()`), helping you pass the correct parameters and catch errors early. ### Auto-detect and generate The CLI auto-detects your project language. In your project directory: ```bash composio generate ``` For TypeScript projects only, include individual tool types: ```bash composio generate --type-tools ``` The CLI automatically: * Detects your project type (Python or TypeScript) * Generates appropriate type definitions ### Specify output directory ```bash composio generate --output-dir ./my-types ``` ### Language-specific commands For explicit control, use language-specific commands: Basic generation: ```bash composio ts generate ``` Generate as single file: ```bash composio ts generate --compact ``` Include individual tool types: ```bash composio ts generate --type-tools ``` Generate both .ts and .js files: ```bash composio ts generate --transpiled ``` Custom output directory: ```bash composio ts generate --output-dir ./my-types ``` Basic generation: ```bash composio py generate ``` Custom output directory: ```bash composio py generate --output-dir ./my_types ``` # Connected Accounts Connected accounts are authenticated connections between your users and toolkits. After users authenticate (see [Authenticating tools](/docs/authenticating-tools)), you can manage these accounts throughout their lifecycle. Composio automatically handles token refresh and credential management. This guide covers manual operations: listing, retrieving, refreshing, enabling, disabling, and deleting accounts. ## List accounts Retrieve all connected accounts with optional filters: ```python # List all accounts for a user accounts = composio.connected_accounts.list( user_ids=[user_id] ) # Filter by status active_accounts = composio.connected_accounts.list( user_ids=[user_id], statuses=["ACTIVE"] ) ``` ```typescript // List all accounts for a user const accounts = await composio.connectedAccounts.list({ userIds: [userId] }); // Filter by status const activeAccounts = await composio.connectedAccounts.list({ userIds: [userId], statuses: ['ACTIVE'] }); ``` ## Get account details Retrieve a connected account by ID: ```python account = composio.connected_accounts.get(connected_account_id) print(f"Status: {account.status}") print(f"Toolkit: {account.toolkit.slug}") ``` ```typescript const account = await composio.connectedAccounts.get(connectedAccountId); console.log('Status:', account.status); console.log('Toolkit:', account.toolkit.slug); ``` ### Get account credentials Get account credentials for use with your own tools: ```python # Get the connected account's authentication state if account.state: # The state contains the auth scheme and credentials auth_scheme = account.state.auth_scheme credentials = account.state.val print(f"Auth scheme: {auth_scheme}") print(f"Credentials: {credentials}") ``` ```typescript // Get the connected account's authentication state if (account.state) { // The state contains the auth scheme and credentials const authScheme = account.state.authScheme; const credentials = account.state.val; console.log('Auth scheme:', authScheme); console.log('Credentials:', credentials); } ``` If credentials return as `REDACTED` instead of actual values, the **Mask Connected Account Secrets** setting is enabled on your account. This setting redacts all sensitive credential data (tokens, secrets, API keys) for security purposes. To view actual credentials, navigate to **Settings → Project Settings → Project Configuration** and disable "Mask Connected Account Secrets". ## Refresh credentials Manually refresh credentials for a connected account: ```python try: refreshed = composio.connected_accounts.refresh(connected_account_id) print(f"Redirect URL: {refreshed.redirect_url}") # Wait for the connection to be established composio.connected_accounts.wait_for_connection(refreshed.id) except Exception as e: print(f"Failed to refresh tokens: {e}") ``` ```typescript try { const refreshed = await composio.connectedAccounts.refresh(connectedAccountId); console.log('Redirect URL:', refreshed.redirect_url); // Wait for the connection to be established await composio.connectedAccounts.waitForConnection(refreshed.id); } catch (error) { console.error('Failed to refresh tokens:', error); } ``` ## Enable and disable accounts Change account status without deleting. Set to INACTIVE to pause access, or ACTIVE to restore. Useful for: * Pausing access during subscription lapses * Temporary disconnection ```python # Disable an account disabled = composio.connected_accounts.disable(connected_account_id) print(f"Account disabled status: {disabled.success}") # Re-enable when needed enabled = composio.connected_accounts.enable(connected_account_id) print(f"Account enabled status: {enabled.success}") ``` ```typescript // Disable an account const disabled = await composio.connectedAccounts.disable(connectedAccountId); console.log('Account disabled status:', disabled.success); // Re-enable when needed const enabled = await composio.connectedAccounts.enable(connectedAccountId); console.log('Account enabled status:', enabled.success); ``` INACTIVE accounts cannot execute tools. Tool execution will fail until the status is changed. ## Delete accounts Permanently remove a connected account and revoke all credentials: ```python # Delete a connected account composio.connected_accounts.delete(connected_account_id) print("Account deleted successfully") ``` ```typescript // Delete a connected account await composio.connectedAccounts.delete(connectedAccountId); console.log('Account deleted successfully'); ``` Deletion is permanent. Users must re-authenticate to reconnect. ## Multiple accounts Users can connect multiple accounts for the same toolkit (e.g., personal and work Gmail). Use `link()` for creating accounts, as it provides hosted authentication and allows multiple accounts by default. See [Connect Link authentication](/docs/authenticating-tools#hosted-authentication-connect-link). ```python # First account try: first_account = composio.connected_accounts.initiate( user_id=user_id, auth_config_id=auth_config_id ) print(f"First account redirect URL: {first_account.redirect_url}") connected_first_account = first_account.wait_for_connection() print(f"First account status: {connected_first_account.status}") except Exception as e: print(f"Error initiating first account: {e}") # Second account - must explicitly allow multiple try: second_account = composio.connected_accounts.initiate( user_id=user_id, auth_config_id=auth_config_id, allow_multiple=True # Required for additional accounts ) print(f"Second account redirect URL: {second_account.redirect_url}") connected_second_account = second_account.wait_for_connection() print(f"Second account status: {connected_second_account.status}") except Exception as e: print(f"Error initiating second account: {e}") ``` ```typescript // First account try { const firstAccount = await composio.connectedAccounts.initiate( userId, authConfigId ); console.log('First account redirect URL:', firstAccount.redirectUrl); const connectedFirstAccount = await firstAccount.waitForConnection(); console.log('First account status:', connectedFirstAccount.status); } catch (error) { console.error('Error initiating first account:', error); } // Second account - must explicitly allow multiple try { const secondAccount = await composio.connectedAccounts.initiate( userId, authConfigId, { allowMultiple: true // Required for additional accounts } ); console.log('Second account redirect URL:', secondAccount.redirectUrl); const connectedSecondAccount = await secondAccount.waitForConnection(); console.log('Second account status:', connectedSecondAccount.status); } catch (error) { console.error('Error initiating second account:', error); } ``` ### Execute with a specific account When you have multiple accounts, specify which one to use with `connected_account_id`: ```python # Execute tool with a specific connected account result = composio.tools.execute( "GMAIL_GET_PROFILE", user_id=user_id, connected_account_id=connected_account_id, # Specify which account to use version="20251111_00", arguments={} ) print(f"Tool executed: {result}") ``` ```typescript // Execute tool with a specific connected account const result = await composio.tools.execute('GMAIL_GET_PROFILE', { userId: userId, connectedAccountId: connectedAccountId, // Specify which account to use version: '20251111_00', arguments: {} }); console.log('Tool executed:', result); ``` # Custom Auth Configs Many toolkits support a level of customization for the auth config, specifically OAuth applications. This guide will walk you through the process of customizing the auth config for toolkits where you can configure your own developer app. ## Creating a custom auth config To create a custom auth config, click **Create Auth Config** in your dashboard, then navigate to **Authentication management** → **Manage authentication with custom credentials**. You'll need to customize the auth config when you want to use different values than the defaults - such as your own subdomain, base URL, client ID, client secret, etc. You may change the subdomain for the PostHog toolkit to match your own instance.
For Hubspot you may customize everything here. For each auth scheme there is a different set of fields. If you choose to use your own developer app for the OAuth2 scheme, you will have to provide the client ID and client secret.
Toolkits that support OAuth2 allow using your own developer app. This is the recommended approach for most cases. We recommend using your own developer app for the OAuth2 scheme as it is more suited for production usage with many users and more granular control over scopes. However, getting OAuth approvals takes time, so Composio provides a default developer app! ## OAuth2 Auth Configs ### Generate the OAuth Client ID and Client Secret To set up a custom OAuth config, you'll need the OAuth Client ID and Client Secret. You can generate the client ID and client secret from your provider's OAuth configuration page. Examples for Google and GitHub:
### Set the Authorized Redirect URI When creating your OAuth app, make sure to configure the Authorized Redirect URI to point to the Composio callback URL below: ``` https://backend.composio.dev/api/v3/toolkits/auth/callback ``` ### Create the auth config Once you have the OAuth credentials, you can add them to the auth config in the dashboard. 1. Select the OAuth2 scheme. 2. Select the scopes to request from users. Default scopes are pre-filled for most apps. 3. Add the OAuth client ID and client secret for your developer app. Keep the redirect URL as is for now! 4. Click Create!
This auth config is now ready to be used in your application! ```python # Create a new connected account connection_request = composio.connected_accounts.initiate( user_id="user_id", auth_config_id="ac_1234", ) print(connection_request) # Wait for the connection to be established connected_account = connection_request.wait_for_connection() print(connected_account) ``` ```typescript const connReq = await composio.connectedAccounts.initiate(userId, "ac_1234"); console.log(connReq.redirectUrl); const connection = await composio.connectedAccounts.waitForConnection( connReq.id ); console.log(connection); ``` ### White-labeling the OAuth Consent Screen By default the users will see an OAuth screen like the one below:
The OAuth redirect URL is surfaced in some OAuth providers' consent screens. This may cause confusion for some users as that URL is not of the same domain as the application. To remediate this: ### Set the Authorized Redirect URI Specify the Authorized Redirect URI to your own domain in the OAuth configuration. For example: ``` https://yourdomain.com/api/composio-redirect ``` ### Create a redirect logic Create a redirect logic, either through your DNS or in your application to redirect that endpoint to `https://backend.composio.dev/api/v3/toolkits/auth/callback` **Example: API Route for OAuth Redirect** ```python from fastapi import FastAPI from fastapi.responses import RedirectResponse from composio import Composio # Create a FastAPI app app = FastAPI() # Create a Composio client composio = Composio() @app.get("/authorize/{toolkit}") def authorize_app(toolkit: str): # retrieve the user id from your app user_id = "" # retrieve the auth config id from your app auth_config_id = "" # initiate the connection request connection_request = composio.connected_accounts.initiate( user_id=user_id, auth_config_id=auth_config_id, ) return RedirectResponse(url=connection_request.redirect_url) ``` ```typescript import type { NextApiRequest, NextApiResponse } from 'next'; export default function handler(req: NextApiRequest, res: NextApiResponse) { // The target Composio endpoint that handles OAuth callbacks const composioEndpoint = 'https://backend.composio.dev/api/v3/toolkits/auth/callback'; // Extract and preserve all query parameters const queryParams = new URLSearchParams(); Object.entries(req.query).forEach(([key, value]) => { if (typeof value === 'string') { queryParams.append(key, value); } }); // Redirect to Composio with all query parameters intact const redirectUrl = `${composioEndpoint}?${queryParams.toString()}`; res.redirect(302, redirectUrl); } ``` ### Create the auth config Specify your custom redirect URI in the auth config settings!
With this setup, you can use `https://yourdomain.com/api/composio-redirect` as your OAuth redirect URI, which will create a better user experience by keeping users on your domain during the OAuth flow. The custom OAuth config allows you to use your own domain in the OAuth consent screen instead of Composio's domain. Here's the core difference: ```mermaid flowchart TD A[Your App initiates OAuth] --> B[User redirected to OAuth Provider] B --> C{Redirect URI Configuration} C -->|Direct Setup| D[Provider redirects to
backend.composio.dev] C -->|Custom Domain| E[Provider redirects to
yourdomain.com/api/composio-redirect] E --> F[Your endpoint forwards to
backend.composio.dev] D --> G[Composio exchanges code for token] F --> G G --> H[Connection established] style E fill:#e1f5fe style F fill:#e1f5fe style C fill:#fff3e0 ``` **Key Benefits:** * **Custom Domain**: Users see your domain in OAuth consent screens, not Composio's * **Same Security**: Your domain just forwards the OAuth callback - no token handling * **Better UX**: Maintains brand consistency throughout the auth flow The custom redirect endpoint is a simple passthrough that preserves all OAuth parameters while keeping users on your domain.
# Custom Auth Parameters In cases where Composio is not being used for managing the auth but only for the tools, it is possible to use the `beforeExecute` hook to inject custom auth headers or parameters for a toolkit. ## Setup and Initialization First, initialize the Composio SDK with your API key: ```python from composio import Composio composio = Composio() ``` ```typescript import { Composio } from "@composio/core"; const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, }); ``` ## Creating the Auth Modifier Function Define a function that modifies authentication parameters for specific toolkits. This function checks the toolkit name and adds custom authentication headers when needed. Before Execute Modifiers are a way to modify the parameters of a tool before it is executed. In this case, they are useful for adding custom authentication headers or parameters to a tool. ```python from composio import before_execute from composio.types import ToolExecuteParams @before_execute(toolkits=["NOTION"]) def add_custom_auth( tool: str, toolkit: str, params: ToolExecuteParams, ) -> ToolExecuteParams: if params["custom_auth_params"] is None: params["custom_auth_params"] = {"parameters": []} params["custom_auth_params"]["parameters"].append( { "name": "x-api-key", "value": os.getenv("NOTION_API_KEY"), "in": "header", } ) return params ``` ```typescript const authModifier = (toolSlug: string, toolkitSlug: string, params: any) => { // Add authentication parameters for specific toolkits if (toolkitSlug === "NOTION") { if (!params.customAuthParams) { params.customAuthParams = {}; } if (!params.customAuthParams.parameters) { params.customAuthParams.parameters = []; } // Add an API key to the headers params.customAuthParams.parameters.push({ in: "header", name: "X-API-Key", value: process.env.CUSTOM_API_KEY, }); } return params; }; ``` ## Executing Tools with Custom Auth Execute the tool using the custom authentication modifier. The `beforeExecute` hook allows you to modify parameters before the tool runs. Following is an example of how to execute a tool with a custom authentication modifier for Completion Providers. For Agentic Providers, read about [Modifying tool inputs](/docs/modify-tool-behavior/before-execution-modifiers). ```python result = composio.tools.execute( slug="NOTION_GET_DATABASE_ITEMS", user_id="default", arguments={}, modifiers=[ add_custom_auth, ], ) print(result) ``` ```typescript const result = await composio.tools.execute( "NOTION_GET_DATABASE_ITEMS", { userId: "sid", arguments: { database_id: "1234567890", }, }, { beforeExecute: authModifier, } ); console.log(JSON.stringify(result, null, 2)); ``` # Creating Custom Tools Custom tools allow you to create your own tools that can be used with Composio. 1. **Standalone tools** - Simple tools that don't require any authentication 2. **Toolkit-based tools** - Tools that require authentication and can use toolkit credentials ## Creating a Custom Tool ### Standalone Tool A standalone tool is the simplest form of custom tool. It only requires input parameters and an execute function: ```python from pydantic import BaseModel, Field from composio import Composio from composio.types import ExecuteRequestFn composio = Composio() class AddTwoNumbersInput(BaseModel): a: int = Field( ..., description="The first number to add", ) b: int = Field( ..., description="The second number to add", ) # function name will be used as slug @composio.tools.custom_tool def add_two_numbers(request: AddTwoNumbersInput) -> int: """Add two numbers.""" return request.a + request.b ``` ```typescript const tool = await composio.tools.createCustomTool({ slug: 'CALCULATE_SQUARE', name: 'Calculate Square', description: 'Calculates the square of a number', inputParams: z.object({ number: z.number().describe('The number to calculate the square of'), }), execute: async input => { const { number } = input; return { data: { result: number * number }, error: null, successful: true, }; }, }); ``` ### Toolkit-based Tool A toolkit-based tool has access to two ways of making authenticated requests: **1. Using `executeToolRequest`** - The recommended way to make authenticated requests to the toolkit's API endpoints. Composio automatically handles credential injection and baseURL resolution: ```python class GetIssueInfoInput(BaseModel): issue_number: int = Field( ..., description="The number of the issue to get information about", ) # function name will be used as slug @composio.tools.custom_tool(toolkit="github") def get_issue_info( request: GetIssueInfoInput, execute_request: ExecuteRequestFn, auth_credentials: dict, ) -> dict: """Get information about a GitHub issue.""" response = execute_request( endpoint=f"/repos/composiohq/composio/issues/{request.issue_number}", method="GET", parameters=[ { "name": "Accept", "value": "application/vnd.github.v3+json", "type": "header", }, { "name": "Authorization", "value": f"Bearer {auth_credentials['access_token']}", "type": "header", }, ], ) return {"data": response.data} ``` ```typescript const tool = await composio.tools.createCustomTool({ slug: 'GITHUB_STAR_COMPOSIOHQ_REPOSITORY', name: 'Github star composio repositories', toolkitSlug: 'github', description: 'Star any specified repo of `composiohq` user', inputParams: z.object({ repository: z.string().describe('The repository to star'), page: z.number().optional().describe('Pagination page number'), customHeader: z.string().optional().describe('Custom header'), }), execute: async (input, connectionConfig, executeToolRequest) => { // This method makes authenticated requests to the relevant API // You can use relative paths! // Composio will automatically inject the baseURL const result = await executeToolRequest({ endpoint: `/user/starred/composiohq/${input.repository}`, method: 'PUT', body: {}, // Add custom headers or query parameters parameters: [ // Add query parameters { name: 'page', value: input.page?.toString() || '1', in: 'query', }, // Add custom headers { name: 'x-custom-header', value: input.customHeader || 'default-value', in: 'header', }, ], }); return result; }, }); ``` **2. Using `connectionConfig`** - For making direct API calls when needed: ```python import requests @composio.tools.custom_tool(toolkit="github") def get_issue_info_direct( request: GetIssueInfoInput, execute_request: ExecuteRequestFn, auth_credentials: dict, ) -> dict: """Get information about a GitHub issue.""" response = requests.get( f"https://api.github.com/repos/composiohq/composio/issues/{request.issue_number}", headers={ "Accept": "application/vnd.github.v3+json", "Authorization": f"Bearer {auth_credentials['access_token']}", }, ) return {"data": response.json()} ``` ```typescript const tool = await composio.tools.createCustomTool({ slug: 'GITHUB_DIRECT_API', name: 'Direct GitHub API Call', description: 'Makes direct calls to GitHub API', toolkitSlug: 'github', inputParams: z.object({ repo: z.string().describe('Repository name'), }), execute: async (input, connectionConfig, executeToolRequest) => { // Use connectionConfig for direct API calls const result = await fetch(`https://api.github.com/repos/${input.repo}`, { headers: { Authorization: `Bearer ${connectionConfig.access_token}`, }, }); return { data: await result.json(), error: null, successful: true, }; }, }); ``` ### Using Custom Headers and Query Parameters You can add custom headers and query parameters to your toolkit-based tools using the `parameters` option in `executeToolRequest`: ```python @composio.tools.custom_tool(toolkit="github") def get_issue_info( request: GetIssueInfoInput, execute_request: ExecuteRequestFn, auth_credentials: dict, ) -> dict: """Get information about a GitHub issue.""" response = execute_request( endpoint=f"/repos/composiohq/composio/issues/{request.issue_number}", method="GET", parameters=[ { "name": "Accept", "value": "application/vnd.github.v3+json", "type": "header", }, { "name": "Authorization", "value": f"Bearer {auth_credentials['access_token']}", "type": "header", }, { "name": 'X-Custom-Header', "value": 'custom-value', "type": 'header', }, ], ) return {"data": response.data} ``` ```typescript const tool = await composio.tools.createCustomTool({ slug: 'GITHUB_SEARCH_REPOSITORIES', name: 'Search GitHub Repositories', description: 'Search for repositories with custom parameters', toolkitSlug: 'github', inputParams: z.object({ query: z.string().describe('Search query'), perPage: z.number().optional().describe('Results per page'), acceptType: z.string().optional().describe('Custom accept header'), }), execute: async (input, connectionConfig, executeToolRequest) => { const result = await executeToolRequest({ endpoint: '/search/repositories', method: 'GET', parameters: [ // Add query parameters for pagination { name: 'q', value: input.query, in: 'query', }, { name: 'per_page', value: (input.perPage || 30).toString(), in: 'query', }, // Add custom headers { name: 'Accept', value: input.acceptType || 'application/vnd.github.v3+json', in: 'header', }, { name: 'X-Custom-Header', value: 'custom-value', in: 'header', }, ], }); return result; }, }); ``` ## Executing Custom Tools You can execute custom tools just like any other tool: ```python response = composio.tools.execute( user_id="default", slug="TOOL_SLUG", # For the tool above you can use `get_issue_info.slug` arguments={"issue_number": 1}, ) ``` ```typescript const result = await composio.tools.execute('TOOL_SLUG', { arguments: { // Tool input parameters }, userId: 'user-id', connectedAccountId: 'optional-account-id', // Required for toolkit-based tools }); ``` ## Best Practices 1. Use descriptive names and slugs for your tools 2. Always provide descriptions for input parameters using `describe()` 3. Handle errors gracefully in your execute function 4. For toolkit-based tools: * Prefer `executeToolRequest` over direct API calls when possible * Use relative paths with `executeToolRequest` - Composio will automatically inject the correct baseURL * Use the `parameters` option to add custom headers or query parameters: ```typescript parameters: [ { name: 'page', value: '1', in: 'query' }, // Adds ?page=1 to URL { name: 'x-custom', value: 'value', in: 'header' }, // Adds header ]; ``` * Remember that `executeToolRequest` can only call tools from the same toolkit * Use `executeToolRequest` to leverage Composio's automatic credential handling * Only use `connectionConfig` when you need to make direct API calls or interact with different services 5. Chain multiple toolkit operations using `executeToolRequest` for better maintainability ## Limitations 1. Custom tools are stored in memory and are not persisted 2. They need to be recreated when the application restarts 3. Toolkit-based tools require a valid connected account with the specified toolkit 4. `executeToolRequest` can only execute tools from the same toolkit that the custom tool belongs to 5. Each toolkit-based tool can only use one connected account at a time # Debugging Info Your debugging info is tied to your project and it helps us trace what happened and debug the issue faster. ## Finding Your Debugging Info Navigate to your project settings to find your debugging information:
## What to Share When reaching out for support, share these identifiers: ``` @project_id: pr_xxxxxxxxxxxxx @org_id: ok_xxxxxxxxxxxxx @org_member_email: your-email@example.com ``` The identifiers with `pr_` (project) and `ok_` (organization) prefixes let us quickly check your logs inside our internal tracing tools, helping us resolve issues faster. ## Getting Help import { MessageCircle, Github, Bug, Mail } from 'lucide-react'; }> Get basic support from the community and Composio team }> Request new tools and share feedback }> Report SDK issues and bugs }> Reach out for dedicated support channels on Growth and Enterprise plans # Executing Tools LLMs on their own can only do generation. Tool calling changes that by letting them interact with external services. Instead of just drafting an email, the model can call `GMAIL_SEND_EMAIL` to actually send it. The tool's results feed back to the LLM, closing the loop so it can decide, act, observe, and adapt. In Composio, every **tool** is a single API action—fully described with schema, parameters, and return type. Tools live inside **toolkits** like Gmail, Slack, or GitHub, and Composio handles authentication and user scoping. **User Scoping**: All tools are scoped to a specific user - that's why every example includes a `user_id`. Learn how to structure User IDs in [User Management](/docs/user-management). Each user must authenticate with their respective services (Gmail, Calendar, etc.) - see [Authentication](/docs/authenticating-tools). ## Using Chat Completions Use the Composio SDK with providers like OpenAI, Anthropic, and Google AI. To learn how to set up these providers, see [Providers](/docs/providers/openai). ```python from composio import Composio from composio_openai import OpenAIProvider from openai import OpenAI from datetime import datetime # Use a unique identifier for each user in your application user_id = "user-k7334" # Create composio client composio = Composio(provider=OpenAIProvider(), api_key="your_composio_api_key") # Create openai client openai = OpenAI() # Get calendar tools for this user tools = composio.tools.get( user_id=user_id, tools=["GOOGLECALENDAR_EVENTS_LIST"] ) # Ask the LLM to check calendar result = openai.chat.completions.create( model="gpt-4o-mini", tools=tools, messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": f"What's on my calendar for the next 7 days?"} ] ) # Handle tool calls result = composio.provider.handle_tool_calls(user_id=user_id, response=result) print(result) ``` ```typescript import { Composio } from '@composio/core'; import { AnthropicProvider } from '@composio/anthropic'; import { Anthropic } from '@anthropic-ai/sdk'; // Use a unique identifier for each user in your application const userId = 'user-k7334'; // Create anthropic client const anthropic = new Anthropic(); // Create Composio client const composio = new Composio({ apiKey: "your-composio-api-key", provider: new AnthropicProvider(), }); // Get calendar tools for this user const tools = await composio.tools.get(userId, { tools: ['GOOGLECALENDAR_EVENTS_LIST'], }); // Ask the LLM to check calendar const msg = await anthropic.messages.create({ model: 'claude-sonnet-4-20250514', tools: tools, messages: [ { role: 'user', content: `What's on my calendar for the next 7 days?`, }, ], max_tokens: 1024, }); // Handle tool calls const result = await composio.provider.handleToolCalls(userId, msg); console.log('Results:', JSON.stringify(result, null, 2)); ``` ## Using Agentic Frameworks Agentic frameworks automatically handle the tool execution loop. Composio provides support for frameworks like this by making sure the tools are formatted into the correct objects for the agentic framework to execute. ```python import asyncio from agents import Agent, Runner from composio import Composio from composio_openai_agents import OpenAIAgentsProvider # Use a unique identifier for each user in your application user_id = "user-k7334" # Initialize Composio toolset composio = Composio(provider=OpenAIAgentsProvider(), api_key="your_composio_api_key") # Get all tools for the user tools = composio.tools.get( user_id=user_id, toolkits=["COMPOSIO_SEARCH"], ) # Create an agent with the tools agent = Agent( name="Deep Researcher", instructions="You are an investigative journalist.", tools=tools, ) async def main(): result = await Runner.run( starting_agent=agent, input=("Do a thorough DEEP research on Golden Gate Bridge"), ) print(result.final_output) # Run the agent asyncio.run(main()) ``` ```typescript import { Composio } from '@composio/core'; import { generateText } from 'ai'; import { anthropic } from '@ai-sdk/anthropic'; import { VercelProvider } from '@composio/vercel'; // Use a unique identifier for each user in your application const userId = 'user-k7334'; // Initialize Composio toolset const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, provider: new VercelProvider(), }); // Get all tools for the user const tools = await composio.tools.get(userId, { toolkits: ['HACKERNEWS_GET_LATEST_POSTS'], limit: 10, }); // Generate text with tool use const { text } = await generateText({ model: anthropic('claude-sonnet-4-20250514'), messages: [ { role: 'user', content: 'Do a thorough DEEP research on the top articles on Hacker News about Composio', }, ], tools, }); console.log(text); ``` ## Direct Tool Execution If you just want to call a tool without using any framework or LLM provider, you can use the `execute` method directly. **Finding tool parameters and types:** **Platform UI**: [Auth Configs](https://platform.composio.dev?next_page=/auth-configs) → Select your toolkit → Tools & Triggers → Select the tool to see its required and optional parameters **CLI**: For Python and TypeScript projects, run `composio generate` to generate types. [Learn more →](/docs/cli#generate-type-definitions) ```python from composio import Composio user_id = "user-k7334" # Configure toolkit versions at SDK level composio = Composio( api_key="your_composio_key", toolkit_versions={"github": "20251027_00"} ) # Find available arguments for any tool in the Composio dashboard result = composio.tools.execute( "GITHUB_LIST_STARGAZERS", user_id=user_id, arguments={"owner": "ComposioHQ", "repo": "composio", "page": 1, "per_page": 5} ) print(result) ``` ```typescript import { Composio } from "@composio/core"; const userId = "user-k7334"; // Configure toolkit versions at SDK level const composio = new Composio({ apiKey: "your_composio_key", toolkitVersions: { github: "20251027_00" } }); // Find available arguments for any tool in the Composio dashboard const result = await composio.tools.execute("GITHUB_LIST_STARGAZERS", { userId, arguments: { "owner": "ComposioHQ", "repo": "composio", "page": 1, "per_page": 5 }, }); console.log('GitHub stargazers:', JSON.stringify(result, null, 2)); ``` The examples above configure toolkit versions at SDK initialization. You can also pass versions per-execution or use environment variables. See [toolkit versioning](/docs/toolkit-versioning) for all configuration options. ### Proxy Execute You can proxy requests to any supported toolkit API and let Composio inject the authentication state. This is useful when you need an API endpoint that isn't available as a predefined tool. The `endpoint` can be a relative path or absolute URL. Composio uses the `connected_account_id` to determine the toolkit and resolve relative paths against the appropriate base URL. ```python # Send a proxy request to the endpoint response = composio.tools.proxy( endpoint="/repos/composiohq/composio/issues/1", method="GET", connected_account_id="ca_jI6********", # use connected account for github parameters=[ { "name": "Accept", "value": "application/vnd.github.v3+json", "type": "header", }, ], ) print(response) ``` ```typescript // Send a proxy request to the endpoint const { data } = await composio.tools.proxyExecute({ endpoint:'/repos/composiohq/composio/issues/1', method: 'GET', connectedAccountId: 'ca_jI*****', // use connected account for github parameters:[ { "name": "Accept", "value": "application/vnd.github.v3+json", "in": "header", }, ], }); console.log(data); ``` Need an API that isn't supported by any Composio toolkit, or want to extend an existing one? Learn how to [create custom tools](/docs/custom-tools). ## Automatic File Handling Composio handles file operations automatically. Pass file paths to tools that need them, and get local file paths back from tools that return files. ### File Upload Pass local file paths, URLs, or File objects to tools that accept files: ```python # Upload a local file to Google Drive result = composio.tools.execute( slug="GOOGLEDRIVE_UPLOAD_FILE", user_id="user-1235***", arguments={"file_to_upload": os.path.join(os.getcwd(), "document.pdf")}, ) print(result) # Print Google Drive file details ``` ```typescript // Upload a local file to Google Drive const result = await composio.tools.execute('GOOGLEDRIVE_UPLOAD_FILE', { userId: 'user-4235***', arguments: { file_to_upload: path.join(__dirname, 'document.pdf') } }); console.log(result.data); // Contains Google Drive file details ``` ### File Download When tools return files, Composio downloads them to the local directory and provides the file path in the response: ```python composio = Composio( api_key="your_composio_key", file_download_dir="./downloads" # Optional: Specify download directory ) result = composio.tools.execute( "GOOGLEDRIVE_DOWNLOAD_FILE", user_id="user-1235***", arguments={"file_id": "your_file_id"}, ) # Result includes local file path print(result) ``` ```typescript // Download a file from Google Drive const result = await composio.tools.execute('GOOGLEDRIVE_DOWNLOAD_FILE', { userId: 'user-1235***', arguments: { file_id: 'your-file-id' } }); // Result includes local file path console.log(result); ``` ### Disabling Auto File Handling You can disable automatic file handling when initializing the TypeScript SDK. When disabled, handle file uploads and downloads manually using `files.upload` and `files.download`: ```typescript const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, autoUploadDownloadFiles: false }); // Now you need to handle files manually using composio.files API const fileData = await composio.files.upload({ filePath: path.join(__dirname, 'document.pdf'), toolSlug: 'GOOGLEDRIVE_UPLOAD_FILE', toolkitSlug: 'googledrive' }); ``` # Fetching tools and schemas Fetch specific tools, filter by permissions or search, and inspect schemas for type information. Tools are automatically formatted for your provider. ## Basic usage ```python tools = composio.tools.get( user_id, toolkits=["GITHUB"] ) ``` ```typescript const tools = await composio.tools.get(userId, { toolkits: ["GITHUB"] }); ``` Returns top 20 tools by default. Tools require a `user_id` because they're scoped to authenticated accounts. See [User management](/docs/user-management) and [Authentication](/docs/authenticating-tools). ## Tool schemas Inspect tool parameters and types without a user\_id: ```python tool = composio.tools.get_raw_composio_tool_by_slug("GMAIL_SEND_EMAIL") ``` ```typescript const tool = await composio.tools.getRawComposioToolBySlug("GMAIL_SEND_EMAIL"); ``` Generate type-safe code for direct SDK execution with [`composio generate`](/docs/cli#generate-type-definitions). This creates TypeScript or Python types from tool schemas. View tool parameters and schemas visually in the [Composio platform](https://platform.composio.dev). Navigate to any toolkit and select a tool to see its input/output parameters. ## Filtering tools ### By toolkit Get tools from specific apps. Returns top 20 tools by default. ```python # Fetch with limit for a specific user tools = composio.tools.get( user_id, toolkits=["GITHUB"], limit=5 # Get top 5 tools ) # Same filter but without user_id (for schemas) raw_tools = composio.tools.get_raw_composio_tools( toolkits=["GITHUB"], limit=5 ) ``` ```typescript // Fetch with limit for a specific user const limitedTools = await composio.tools.get(userId, { toolkits: ["GITHUB"], limit: 5 // Get top 5 tools }); // Same filter but without userId (for schemas) const rawTools = await composio.tools.getRawComposioTools({ toolkits: ["GITHUB"], limit: 5 }); ``` ### By name Fetch specific tools when you know their names. ```python # Fetch specific tools by name tools = composio.tools.get( user_id, tools=["GITHUB_CREATE_ISSUE", "GITHUB_CREATE_PULL_REQUEST"] ) # Get schemas without user_id raw_tools = composio.tools.get_raw_composio_tools( tools=["GITHUB_CREATE_ISSUE", "GITHUB_CREATE_PULL_REQUEST"] ) ``` ```typescript // Fetch specific tools by name const specificTools = await composio.tools.get(userId, { tools: ["GITHUB_LIST_STARGAZERS", "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"] }); // Get schemas without userId const specificRawTools = await composio.tools.getRawComposioTools({ tools: ["GITHUB_LIST_STARGAZERS", "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"] }); ``` ### By scopes Filter OAuth tools by permission level. Only works with a single toolkit. ```python # Filter by OAuth scopes (single toolkit only) tools = composio.tools.get( user_id, toolkits=["GITHUB"], scopes=["write:org"] ) ``` ```typescript // Filter by OAuth scopes (single toolkit only) const scopedTools = await composio.tools.get(userId, { toolkits: ["GITHUB"], scopes: ["write:org"] }); ``` ### By search (experimental) Find tools semantically. ```python # Search tools semantically tools = composio.tools.get( user_id, search="create calendar event" ) # Search schemas without user_id raw_tools = composio.tools.get_raw_composio_tools( search="create calendar event" ) # Search within a specific toolkit tools = composio.tools.get( user_id, search="issues", toolkits=["GITHUB"], ) # Search toolkit schemas without user_id raw_tools = composio.tools.get_raw_composio_tools( search="issues", toolkits=["GITHUB"] ) ``` ```typescript // Search tools semantically const searchResults = await composio.tools.get(userId, { search: "create google calendar event" }); // Search schemas without userId const searchRawTools = await composio.tools.getRawComposioTools({ search: "create google calendar event" }); // Search within a specific toolkit const toolkitSearch = await composio.tools.get(userId, { search: "star a repository", toolkits: ["GITHUB"] }); // Search toolkit schemas without userId const toolkitSearchRaw = await composio.tools.getRawComposioTools({ search: "star a repository", toolkits: ["GITHUB"] }); ``` Use specific toolkit versions in production to prevent breaking changes. See [toolkit versioning](/docs/toolkit-versioning). # Welcome Single MCP server and Tool that can search, authenticate, and execute across all Composio tools. [Learn more →](/tool-router/overview) } title="Managed authentication" href="/docs/authenticating-tools" description="Handle OAuth, API keys, and custom auth flows automatically" /> } title="Tool execution" href="/docs/executing-tools" description="Execute actions across 500+ toolkits with support for most AI frameworks" /> } title="MCP server" href="/docs/mcp-quickstart" description="Hosted MCP servers for all 500+ toolkits" /> } title="Triggers" href="/docs/using-triggers" description="Subscribe to external events and trigger workflows automatically" /> ## Get started } title="Quickstart" href="/docs/quickstart" description="Build your first agent" /> Python TypeScript
## Why Composio? Composio is the fastest way to enable your AI agents to take real-world actions—without dealing with individual API integrations, authentication flows, or complex tool formatting. * **Access 500+ toolkits** out of the box across popular apps like Slack, GitHub, Notion, and more. [Browse toolkits →](/toolkits/introduction) * **Enforce strict access and data controls** with [fine-grained permissions](/docs/authenticating-tools) for each tool and user. * **Trigger agents and workflows** using [external events](/docs/using-triggers) (e.g., new message in Slack, new issue in GitHub). * **Use MCP servers** for all 500+ toolkits, compatible with any [MCP client](/docs/mcp-quickstart). * **Search, plan and authenticate** across all tools with [Tool Router](/tool-router/overview). ## Providers Composio works with any AI framework. Pick your preferred SDK: } languages={["Python", "TypeScript"]} /> ## For AI tools Access Composio documentation in Markdown format for use with Cursor, Copilot, Claude, and other AI tools: * [composio.dev/llms.txt](/llms.txt) - Documentation index with links * [composio.dev/llms-full.txt](/llms-full.txt) - Complete documentation in one file ``` Documentation: {paste documentation from composio.dev/llms-full.txt} --- Based on the above documentation, answer the following: {your question} ``` ## Community Join our [Discord](https://discord.gg/composio) community! # Providers Integrate your MCP servers with popular AI SDKs and frameworks. Composio MCP servers only support Streamable HTTP transport. ## Anthropic SDK Use MCP servers with the Anthropic Claude API. ```python from anthropic import Anthropic from composio import Composio # Initialize clients composio = Composio() anthropic = Anthropic(api_key="your-anthropic-api-key") # Create MCP server with GitHub and Linear tools server = composio.mcp.create( name="dev-workflow-server", toolkits=[ {"toolkit": "github", "auth_config": "ac_github_id"}, {"toolkit": "linear", "auth_config": "ac_linear_id"} ], allowed_tools=["GITHUB_LIST_PRS", "GITHUB_CREATE_COMMENT", "LINEAR_CREATE_ISSUE"] ) # Generate MCP instance for user instance = server.generate("user@example.com") # Use MCP with Anthropic to manage development workflow response = anthropic.beta.messages.create( model="claude-sonnet-4-5", system="You are a helpful assistant with access to GitHub and Linear tools. Use these tools to help manage development workflows. Do not ask for confirmation before using the tools.", max_tokens=1000, messages=[{ "role": "user", "content": "Check my GitHub PRs for review comments, create Linear tasks for any requested changes, and update the PR descriptions with task links" }], mcp_servers=[{ "type": "url", "url": instance['url'], "name": "composio-mcp-server" }], betas=["mcp-client-2025-04-04"] # Enable MCP beta ) print(response.content) ``` ```typescript import Anthropic from '@anthropic-ai/sdk'; import { Composio } from '@composio/core'; // Initialize clients const composio = new Composio(); const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, }); // Create MCP server with Google Sheets tools const server = await composio.mcp.create( "analytics-server", { toolkits: [ { toolkit: "googlesheets", authConfigId: "ac_sheets_id" } ], allowedTools: ["GOOGLESHEETS_GET_DATA", "GOOGLESHEETS_UPDATE_DATA", "GOOGLESHEETS_CREATE_SHEET"] } ); // Generate MCP instance for user const instance = await server.generate("user@example.com"); // Use MCP with Anthropic for spreadsheet operations const response = await anthropic.beta.messages.create({ model: "claude-sonnet-4-5", system: "You are a helpful assistant with access to Google Sheets tools. Use these tools to analyze and manage spreadsheet data. Do not ask for confirmation before using the tools.", max_tokens: 1000, messages: [{ role: "user", content: "Analyze the sales data in my Google Sheets 'Q4 Revenue' spreadsheet, calculate month-over-month growth, and add a new summary sheet with visualizations" }], mcp_servers: [{ type: "url", url: instance.url, name: "composio-mcp-server" }], betas: ["mcp-client-2025-04-04"] // Enable MCP beta }); console.log(response.content); ``` ## OpenAI SDK Integrate MCP servers with OpenAI GPT models. ```python from openai import OpenAI from composio import Composio # Initialize clients composio = Composio() openai = OpenAI(api_key="your-openai-api-key") # Create MCP server with Google Sheets and Notion tools server = composio.mcp.create( name="data-docs-server", toolkits=[ {"toolkit": "googlesheets", "auth_config": "ac_sheets_id"}, {"toolkit": "notion", "auth_config": "ac_notion_id"} ], allowed_tools=["GOOGLESHEETS_GET_DATA", "GOOGLESHEETS_UPDATE_DATA", "NOTION_CREATE_PAGE"] ) # Generate MCP instance for user instance = server.generate("user@example.com") # Use MCP with OpenAI for data management response = openai.responses.create( model="gpt-5", tools=[ { "type": "mcp", "server_label": "composio-server", "server_description": "Composio MCP server with Google Sheets and Notion integrations", "server_url": instance['url'], "require_approval": "never", }, ], input="Export the Q4 metrics from Google Sheets and create a comprehensive Notion page with charts and analysis", ) print("OpenAI MCP Response:", response.output_text) ``` ```typescript import OpenAI from 'openai'; import { Composio } from '@composio/core'; // Initialize clients const composio = new Composio(); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); // Create MCP server with Linear and Notion tools const server = await composio.mcp.create( "project-docs-server", { toolkits: [ { toolkit: "linear", authConfigId: "ac_linear_id" }, { toolkit: "notion", authConfigId: "ac_notion_id" } ], allowedTools: ["LINEAR_LIST_ISSUES", "LINEAR_GET_ISSUE", "NOTION_CREATE_PAGE"] } ); // Generate MCP instance for user const instance = await server.generate("user@example.com"); // Use MCP with OpenAI for project documentation const response = await openai.responses.create({ model: "gpt-5", tools: [ { type: "mcp", server_label: "composio-server", server_description: "Composio MCP server with Linear and Notion integrations", server_url: instance.url, require_approval: "never", }, ], input: "Find all completed Linear issues from this sprint and create a Notion page documenting the release notes", }); console.log("OpenAI MCP Response:", response.output_text); ``` ## Mastra Use MCP servers with Mastra framework. ```typescript import { MCPClient } from "@mastra/mcp"; import { openai } from "@ai-sdk/openai"; import { Agent } from "@mastra/core/agent"; import { Composio } from "@composio/core"; // Initialize Composio const composio = new Composio(); // Create MCP server with GitHub, Linear, and Notion tools const server = await composio.mcp.create( "dev-automation-server", { toolkits: [ { toolkit: "github", authConfigId: "ac_github_id" }, { toolkit: "linear", authConfigId: "ac_linear_id" }, { toolkit: "notion", authConfigId: "ac_notion_id" } ], allowedTools: [ "GITHUB_LIST_ISSUES", "GITHUB_CREATE_ISSUE", "LINEAR_CREATE_ISSUE", "LINEAR_UPDATE_ISSUE", "NOTION_CREATE_PAGE", "NOTION_UPDATE_PAGE" ] } ); // Generate MCP instance for user const instance = await server.generate("user@example.com"); // Create MCP client with Composio server export const mcpClient = new MCPClient({ id: "composio-mcp-client", servers: { composio: { url: new URL(instance.url) }, } }); // Create a development workflow agent export const devAgent = new Agent({ name: "Dev Assistant", description: "AI assistant for development workflow automation", instructions: "Help manage GitHub repos, Linear issues, and Notion documentation.", model: openai("gpt-4-turbo"), tools: await mcpClient.getTools() }); // Example: Automate development workflow (async () => { const response = await devAgent.generate( "Review open GitHub issues, create Linear tasks for bugs labeled 'priority', and update the Notion roadmap page" ); console.log(response.text); })(); ``` # Quickstart [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is an open-source standard for connecting AI applications to external tools. All the 500+ tools available in Composio are also available as MCP servers. Composio lets you create MCP servers that handle authentication (OAuth, API keys), generate unique URLs for each user, and control which tools are exposed. You can combine multiple toolkits in a single server. Composio MCP servers only support Streamable HTTP transport. ## Install the SDK First, install the Composio SDK for your preferred language: ```bash pip install composio ``` ```bash npm install @composio/core ``` ## Create an MCP server ### Initialize Composio ```python from composio import Composio # Initialize Composio composio = Composio(api_key="YOUR_API_KEY") ``` ```typescript import { Composio } from '@composio/core'; // Initialize Composio const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY }); ``` ### Create server configuration **Before you begin:** [Create an auth configuration](/docs/authenticating-tools#creating-an-auth-config) for your toolkit. Create an MCP server with your auth config. You can also set list of specific tools to enable across all toolkits. ```python # Create MCP server with multiple toolkits server = composio.mcp.create( name="mcp-config-73840", # Pick a unique name for your MCP server toolkits=[ { "toolkit": "gmail", "auth_config": "ac_xyz123" # Your Gmail auth config ID }, { "toolkit": "googlecalendar", "auth_config": "ac_abc456" # Your Google Calendar auth config ID } ], allowed_tools=["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL", "GOOGLECALENDAR_EVENTS_LIST"] ) print(f"Server created: {server.id}") print(server.id) ``` ```typescript // Create MCP server with multiple toolkits const server = await composio.mcp.create("mcp-config-73840", { // Pick a unique name for your MCP server toolkits: [ { authConfigId: "ac_xyz123", // Your Gmail auth config ID toolkit: "gmail" }, { authConfigId: "ac_abc456", // Your Google Calendar auth config ID toolkit: "googlecalendar" } ], allowedTools: ["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL", "GOOGLECALENDAR_EVENTS_LIST"] }); console.log(`Server created: ${server.id}`); console.log(server.id); ``` **Alternative:** You can also create and manage MCP configs directly from the [Composio dashboard → MCP Configs](https://platform.composio.dev?next_page=/mcp-configs). ### Generate user URLs **Before generating URLs:** Users must authenticate with the toolkits configured in your MCP server. See [hosted authentication](/docs/authenticating-tools#hosted-authentication-connect-link) for how to connect user accounts. Get server URLs for your users to connect: ```python # Generate server instance for user instance = composio.mcp.generate(user_id="user-73840", mcp_config_id=server.id) # Use the user ID for which you created the connected account print(f"MCP Server URL: {instance['url']}") ``` ```typescript // Generate server instance for user const instance = await composio.mcp.generate("user-73840", server.id); // Use the user ID for which you created the connected account console.log("MCP Server URL:", instance.url); ``` If users haven't authenticated, the MCP server will still generate a URL but tools requiring authentication won't work until the user connects their accounts. ### Use with AI providers Use the MCP server URL with your AI provider: ```python from openai import OpenAI # Initialize OpenAI client client = OpenAI(api_key="your-openai-api-key") # Use the MCP server URL you generated mcp_server_url = "https://backend.composio.dev/v3/mcp/YOUR_SERVER_ID?include_composio_helper_actions=true&user_id=YOUR_USER_ID" # Use MCP with OpenAI Responses API response = client.responses.create( model="gpt-5", tools=[ { "type": "mcp", "server_label": "composio-server", "server_description": "Composio MCP server for Gmail and Calendar integrations", "server_url": mcp_server_url, "require_approval": "never", }, ], input="What meetings do I have tomorrow? Also check if I have any urgent emails", ) print("OpenAI MCP Response:", response.output_text) ``` ```python from anthropic import Anthropic # Initialize Anthropic client client = Anthropic(api_key="your-anthropic-api-key") # Use the MCP server URL you generated mcp_server_url = "https://backend.composio.dev/v3/mcp/YOUR_SERVER_ID?include_composio_helper_actions=true&user_id=YOUR_USER_ID" # Use MCP with Anthropic (beta feature) response = client.beta.messages.create( model="claude-sonnet-4-5", system="You are a helpful assistant with access to various tools through MCP. Use these tools to help the user. Do not ask for confirmation before using the tools.", max_tokens=1000, messages=[{ "role": "user", "content": "What meetings do I have tomorrow? Also check if I have any urgent emails" }], mcp_servers=[{ "type": "url", "url": mcp_server_url, "name": "composio-gmail-calendar-mcp-server" }], betas=["mcp-client-2025-04-04"] # Enable MCP beta ) print(response.content) ``` ```typescript import { MCPClient } from "@mastra/mcp"; import { openai } from "@ai-sdk/openai"; import { Agent } from "@mastra/core/agent"; // Use the MCP server URL you generated const MCP_URL = "https://backend.composio.dev/v3/mcp/YOUR_SERVER_ID?include_composio_helper_actions=true&user_id=YOUR_USER_ID"; export const client = new MCPClient({ id: "docs-mcp-client", servers: { composio: { url: new URL(MCP_URL) }, } }); export const agent = new Agent({ name: "Assistant", description: "Helpful AI with MCP tools", instructions: "Use the MCP tools to answer.", model: openai("gpt-4-turbo"), tools: await client.getTools() }); (async () => { const res = await agent.generate("What meetings do I have tomorrow? Also check if I have any urgent emails"); console.log(res.text); })(); ``` ## Next steps Create and manage MCP servers Use with Anthropic, OpenAI, and other frameworks Need help? Join our [Discord](https://discord.com/invite/composio) or [raise an issue on GitHub](https://github.com/ComposioHQ/composio/issues/new?labels=bug). # Server management This guide covers all server management operations for MCP servers. ## Create a server ```python server = composio.mcp.create( name="my-gmail-server", toolkits=[{ "toolkit": "gmail", "auth_config": "ac_xyz123" }], allowed_tools=["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL"] ) print(f"Created server: {server.id}") ``` ```typescript const server = await composio.mcp.create( "my-gmail-server", { toolkits: [ { authConfigId: "ac_xyz123", toolkit: "gmail" } ], allowedTools: ["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL"], manuallyManageConnections: false } ); console.log(`Created server: ${server.id}`); ``` ## List servers Find and filter your MCP servers. ```python # List all servers servers = composio.mcp.list() print(f"Found {len(servers['items'])} servers") # Filter by toolkit gmail_servers = composio.mcp.list( toolkits="gmail", limit=20 ) # Filter by name production_servers = composio.mcp.list( name="production" ) ``` ```typescript // List all servers const servers = await composio.mcp.list({ limit: 10, page: 1, authConfigs: [], toolkits: [] }); console.log(`Found ${servers.items.length} servers`); // Filter by toolkit const gmailServers = await composio.mcp.list({ limit: 20, page: 1, authConfigs: [], toolkits: ["gmail"] }); // Filter by name const productionServers = await composio.mcp.list({ limit: 10, page: 1, authConfigs: [], toolkits: [], name: "production" }); ``` ## Get server details ```python # Get by ID server = composio.mcp.get("mcp_server_id") print(f"Server name: {server.name}") print(f"Toolkits: {server.toolkits}") ``` ```typescript // Get by ID const server = await composio.mcp.get("mcp_server_id"); console.log(`Server: ${server.name}`); console.log(`Toolkits: ${server.toolkits}`); ``` ## Update a server Modify server configuration, tools, or auth configs. ```python updated = composio.mcp.update( server_id="mcp_server_id", name="updated-name", allowed_tools=["GMAIL_FETCH_EMAILS", "GMAIL_SEARCH_EMAILS"] ) ``` ```typescript const updated = await composio.mcp.update( "mcp_server_id", { name: "updated-name", toolkits: [ { toolkit: "gmail", authConfigId: "ac_new_config" } ], allowedTools: ["GMAIL_FETCH_EMAILS", "GMAIL_SEARCH_EMAILS"], manuallyManageConnections: false } ); console.log(updated); ``` ## Delete a server ```python result = composio.mcp.delete("mcp_server_id") if result['deleted']: print("Server deleted successfully") ``` ```typescript const result = await composio.mcp.delete("mcp_server_id"); if (result.deleted) { console.log("Server deleted successfully"); } ``` ## Multi-toolkit servers Combine multiple services in a single MCP server. ```python server = composio.mcp.create( name="productivity-suite", toolkits=[ {"toolkit": "gmail", "auth_config": "ac_gmail"}, {"toolkit": "slack", "auth_config": "ac_slack"}, {"toolkit": "github", "auth_config": "ac_github"} ], allowed_tools=[ "GMAIL_FETCH_EMAILS", "SLACK_SEND_MESSAGE", "GITHUB_CREATE_ISSUE" ] ) ``` ```typescript const server = await composio.mcp.create( "productivity-suite", { toolkits: [ { authConfigId: "ac_gmail", toolkit: "gmail" }, { authConfigId: "ac_slack", toolkit: "slack" }, { authConfigId: "ac_github", toolkit: "github" } ], allowedTools: [ "GMAIL_FETCH_EMAILS", "SLACK_SEND_MESSAGE", "GITHUB_CREATE_ISSUE" ], manuallyManageConnections: false } ); ``` ## Generate user URLs After creating a server, generate unique URLs for each user. ```python # Generate instance for user instance = server.generate("user@example.com") print(f"URL: {instance['url']}") print(f"Tools: {instance['allowed_tools']}") # Chat-based authentication is enabled by default # Set manually_manage_connections=True to disable it: instance_manual = server.generate( "user@example.com", manually_manage_connections=True ) ``` ```typescript // Generate instance for user const instance = await server.generate("user@example.com"); console.log("URL:", instance.url); console.log("Tools:", instance.allowedTools); // Chat-based authentication is enabled by default // Set manuallyManageConnections: true to disable it: const instanceManual = await composio.mcp.generate( "user@example.com", server.id, { manuallyManageConnections: true } ); ``` **Chat-based authentication:** * **TypeScript**: Enabled by default (set `manuallyManageConnections=false` or omit it) * **Python**: Enabled by default (set `manually_manage_connections=False` or omit it) When enabled, the agent can authenticate users dynamically during conversation if they're missing required connections. Set `manuallyManageConnections=true` (TypeScript) or `manually_manage_connections=True` (Python) to disable it. ## Best practices * **Use descriptive names** - Makes servers easy to find * **Limit tools appropriately** - Only include necessary tools * **Reuse servers** - Don't create duplicates for same configuration * **Store server IDs** - Keep track of created server IDs # Programmatic Auth Configs Auth configs are created once and reused many times. However, when managing multiple toolkits, you may want to create auth configs programmatically. * When creating and destroying auth configs multiple times in your app's lifecycle. * When creating auth configs for your users' users. ## OAuth2 based apps ### Using Composio Default Auth Since OAuth2 is the most common authentication type for applications, Composio provides managed auth for most OAuth2 based applications. This is to speed up development and prototyping. This means you don't have to provide your own OAuth credentials. ```python from composio import Composio composio = Composio() # Use composio managed auth auth_config = composio.auth_configs.create( toolkit="github", options={ "type": "use_composio_managed_auth", }, ) print(auth_config) ``` ```typescript import { Composio } from "@composio/core"; const composio = new Composio(); const authConfig = await composio.authConfigs.create("GITHUB", { name: "GitHub", type: "use_composio_managed_auth", }); console.log(authConfig); ``` The returned `auth_config_id` should be stored securely in your database for future use to be created and destroyed multiple times. You can also provide your own authentication details. The required `credentials` and `authScheme` depend on the auth type. ### Using your own OAuth2 credentials Setting up and using your own OAuth2 credentials is the recommended way when going to production or expecting high usage. In this example, we're using our own OAuth2 client ID and secret to create the auth config for Notion. ```python # Use custom auth auth_config = composio.auth_configs.create( toolkit="notion", options={ "name": "Notion Auth", "type": "use_custom_auth", "auth_scheme": "OAUTH2", "credentials": { "client_id": "1234567890", "client_secret": "1234567890", "oauth_redirect_uri": "https://backend.composio.dev/api/v3/toolkits/auth/callback", }, }, ) print(auth_config) ``` ```typescript const authConfig = await composio.authConfigs.create("NOTION", { name: "Notion", type: "use_custom_auth", credentials: { client_id: "1234567890", client_secret: "1234567890", oauth_redirect_uri: "https://backend.composio.dev/api/v3/toolkits/auth/callback", }, authScheme: "OAUTH2", }); console.log(authConfig); ``` You can specify a custom redirect URI by including the `oauth_redirect_uri` parameter in the credentials object. If not provided, Composio uses the default redirect URI. **Specifying the authorized redirect URI** The process of setting up your own OAuth2 credentials usually involves generating a client ID and secret and specifying the **authorized redirect URI** in the OAuth configuration. The **authorized redirect URI** is the URI that captures the OAuth code that is returned to the app. While doing so, you must ensure to set the **authorized redirect URI** in the OAuth configuration to: ``` https://backend.composio.dev/api/v3/toolkits/auth/callback ```
### Specifying scopes Composio requests a set of appropriate default OAuth2 scopes for each toolkit wherever possible. However, you can override or modify these scopes by passing a `scopes` field to the `credentials` object. ```python from composio import Composio composio = Composio() response = composio.auth_configs.create( toolkit="HUBSPOT", options={ "name": "HubspotConfig", "authScheme": "OAUTH2", "type": "use_composio_managed_auth", "credentials": { "scopes": "sales-email-read,tickets" } } ) print(response.id) ``` ```typescript import { Composio } from "@composio/core"; const composio = new Composio(); const authConfig = await composio.authConfigs.create("HUBSPOT", { name: "HubspotConfig", type: "use_composio_managed_auth", credentials: { scopes: "sales-email-read,tickets", }, }); console.log(authConfig); ``` ## Other auth types Composio supports many applications that use different authentication types like API keys, Bearer tokens, JWT and even no authentication at all. Generating the auth config for other auth types only has minor differences: * `use_custom_auth` is used instead of `use_composio_managed_auth` * The `credentials` field is used to pass the authentication details * The `authScheme` field is used to specify the auth type ```python # Use custom auth auth_config = composio.auth_configs.create( toolkit="perplexityai", options={ "type": "use_custom_auth", "auth_scheme": "API_KEY", "credentials": {} }, ) print(auth_config) ``` ```typescript const authConfig = await composio.authConfigs.create('PERPLEXITYAI', { name: 'Perplexity AI', type: 'use_custom_auth', credentials: {}, authScheme: 'API_KEY', }); console.log(authConfig); ``` ## Programmatically inspecting fields In cases where you need to dynamically discover the exact field names and handle different auth schemes programmatically, you can inspect the auth config details first. This works for all auth types. ```python required_fields = composio.toolkits.get_auth_config_creation_fields( toolkit="NOTION", auth_scheme="OAUTH2", required_only=True, ) print(required_fields) ``` ```typescript const toolkits = await composio.toolkits.get("NOTION"); // Extract field names from authConfigDetails const authFields = await composio.toolkits.getAuthConfigCreationFields('NOTION', 'OAUTH2', { requiredOnly: true, }); console.log("Required auth config fields:", authFields); ``` Then inspect the required fields and specify them in the `credentials` object. # Quickstart This guide walks you through **authenticated tool calling**—the foundation of how Composio connects your AI agents to real-world actions. You'll learn how to: 1. **Discover and add tools** relevant to your use case (e.g., Slack, GitHub, Notion) to your AI agent 2. **Authenticate tools** securely on behalf of a specific user, with fine-grained access control 3. **Enable your LLM** (like OpenAI, Claude, or LangChain) to invoke these tools reliably using structured tool call formats ## Prerequisites Before you begin, ensure you have: 1. **A Composio account** - [Sign up here](https://platform.composio.dev) if you haven't already 2. **Python 3.10+** or **Node.js 18+** installed on your system 3. **Your API key** - Get it from the [developer dashboard](https://platform.composio.dev?next_page=/settings) and set it as an environment variable: ```bash export COMPOSIO_API_KEY=your_api_key ``` ## Install the SDK First, install the Composio SDK for your preferred language: ```bash pip install composio ``` ```bash npm install @composio/core ``` ## Authorize Tools & Run Them with an Agent Composio supports multiple LLM providers. Here's how to use Composio with some of the most popular ones: Install the OpenAI Agents provider: ```bash pip install composio openai-agents composio-openai-agents ``` ```python import asyncio from composio import Composio from agents import Agent, Runner from composio_openai_agents import OpenAIAgentsProvider composio = Composio(api_key="your-api-key", provider=OpenAIAgentsProvider()) # Id of the user in your system externalUserId = "pg-test-6dadae77-9ae1-40ca-8e2e-ba2d1ad9ebc4" # Create an auth config for gmail from the dashboard or programmatically auth_config_id = "your-auth-config-id" connection_request = composio.connected_accounts.link( user_id=externalUserId, auth_config_id=auth_config_id, ) # Redirect user to the OAuth flow redirect_url = connection_request.redirect_url print( f"Please authorize the app by visiting this URL: {redirect_url}" ) # Print the redirect url to the user # Wait for the connection to be established connected_account = connection_request.wait_for_connection() print( f"Connection established successfully! Connected account id: {connected_account.id}" ) # Get Gmail tools that are pre-configured tools = composio.tools.get(user_id=externalUserId, tools=["GMAIL_SEND_EMAIL"]) agent = Agent( name="Email Manager", instructions="You are a helpful assistant", tools=tools ) # Run the agent async def main(): result = await Runner.run( starting_agent=agent, input="Send an email to soham.g@composio.dev with the subject 'Hello from composio' and the body 'Congratulations on sending your first email using AI Agents and Composio!'", ) print(result.final_output) asyncio.run(main()) ``` Install the Composio Anthropic provider: ```bash npm i @composio/core @composio/anthropic @anthropic-ai/sdk ``` ```typescript import { Composio } from "@composio/core"; import { AnthropicProvider } from "@composio/anthropic"; import Anthropic from "@anthropic-ai/sdk"; // env: ANTHROPIC_API_KEY const anthropic = new Anthropic(); const composio = new Composio({ apiKey: "your-api-key", provider: new AnthropicProvider(), toolkitVersions: { "gmail": "20251111_00", } }); // Id of the user in your system const externalUserId = "pg-test-6dadae77-9ae1-40ca-8e2e-ba2d1ad9ebc4"; // Create an auth config for gmail from the dashboard or programmatically const authConfigId = "your-auth-config-id"; const connectionRequest = await composio.connectedAccounts.link( externalUserId, authConfigId ); // redirect the user to the OAuth flow const redirectUrl = connectionRequest.redirectUrl; console.log(`Please authorize the app by visiting this URL: ${redirectUrl}`); // wait for connection to be established const connectedAccount = await connectionRequest.waitForConnection(); console.log( `Connection established successfully! Connected account id: ${connectedAccount.id}` ); // Fetch tools for your user and execute const tools = await composio.tools.get(externalUserId, { tools: ["GMAIL_SEND_EMAIL"], }); console.log(tools); const msg = await anthropic.messages.create({ model: "claude-sonnet-4-5", messages: [ { role: "user", content: `Send an email to soham.g@composio.dev with the subject 'Hello from composio' and the body 'Congratulations on sending your first email using AI Agents and Composio!'`, }, ], tools: tools, max_tokens: 1000, }); const res = await composio.provider.handleToolCalls(externalUserId, msg); console.log("Email sent successfully!"); ``` Install the Composio Vercel Provider: ```bash npm i @composio/core @composio/vercel ai @ai-sdk/openai ``` ```typescript import { Composio } from "@composio/core"; import { VercelProvider } from "@composio/vercel"; import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; const composio = new Composio({ apiKey: "your-api-key", provider: new VercelProvider(), }); // Id of the user in your system const externalUserId = "pg-test-6dadae77-9ae1-40ca-8e2e-ba2d1ac9ebc4"; // Create an auth config for gmail from the dashboard or programmatically const authConfigId = "your-auth-config-id"; const connectionRequest = await composio.connectedAccounts.link( externalUserId, authConfigId ); // redirect the user to the OAuth flow const redirectUrl = connectionRequest.redirectUrl; console.log(`Please authorize the app by visiting this URL: ${redirectUrl}`); // wait for connection to be established const connectedAccount = await connectionRequest.waitForConnection(); const tools = await composio.tools.get(externalUserId, "GMAIL_SEND_EMAIL"); // env: OPENAI_API_KEY const { text } = await generateText({ model: openai("gpt-5"), messages: [ { role: "user", content: `Send an email to soham.g@composio.dev with the subject 'Hello from composio' and the body 'Congratulations on sending your first email using AI Agents and Composio!'`, }, ], tools: tools }); console.log("Email sent successfully!", { text }); ``` You just: 1. Authorized a user account with Composio 2. Passed those tool permissions into an LLM framework 3. Let the LLM securely call real tools on the user's behalf All OAuth flows and tool execution were automatically handled by Composio. ## Next steps } title="Use Providers" href="/docs/providers/openai" description="Learn how to use Composio with various agent SDK and frameworks." /> } title="Work with tools" href="/docs/executing-tools" description="Learn how to work with tools and tool calling on a deeper level with Composio." /> } title="Manage authentication" href="/docs/custom-auth-configs" description="Authorize tools for multiple users." /> } title="Triggers" href="/docs/using-triggers" description="Listen for external events to trigger actions in your agents." /> # Toolkit Versioning Toolkit versioning ensures your tools behave consistently across deployments. You can pin specific versions in production, test new releases in development, and roll back when needed. Starting from Python SDK v0.9.0 and TypeScript SDK v0.2.0, specifying versions is required for manual tool execution. ## Configuration methods Configure toolkit versions using one of three methods: ### SDK initialization ```python from composio import Composio # Pin specific versions for each toolkit composio = Composio( api_key="YOUR_API_KEY", toolkit_versions={ "github": "20251027_00", "slack": "20251027_00", "gmail": "20251027_00" } ) ``` ```typescript import { Composio } from "@composio/core"; // Pin specific versions for each toolkit const composio = new Composio({ apiKey: "YOUR_API_KEY", toolkitVersions: { github: "20251027_00", slack: "20251027_00", gmail: "20251027_00" } }); ``` ### Environment variables ```bash # Set versions for specific toolkits export COMPOSIO_TOOLKIT_VERSION_GITHUB="20251027_00" export COMPOSIO_TOOLKIT_VERSION_SLACK="20251027_00" export COMPOSIO_TOOLKIT_VERSION_GMAIL="20251027_00" ``` ### Per-execution override ```python from composio import Composio composio = Composio(api_key="YOUR_API_KEY") # Specify version directly in execute call result = composio.tools.execute( "GITHUB_LIST_STARGAZERS", arguments={ "owner": "ComposioHQ", "repo": "composio" }, user_id="user-k7334", version="20251027_00" # Override version for this execution ) print(result) ``` ```typescript import { Composio } from "@composio/core"; const composio = new Composio({ apiKey: "YOUR_API_KEY" }); // Specify version directly in execute call const result = await composio.tools.execute("GITHUB_LIST_STARGAZERS", { userId: "user-k7334", arguments: { owner: "ComposioHQ", repo: "composio" }, version: "20251027_00" // Override version for this execution }); console.log(result); ``` ## Version format Versions follow the format `YYYYMMDD_NN`: * `YYYYMMDD`: Release date * `NN`: Sequential release number ```python # Production toolkit_versions = {"github": "20251027_00"} # Development toolkit_versions = {"github": "latest"} ``` Never use `latest` in production. It can introduce breaking changes. ## Version resolution order 1. Per-execution version (highest priority) 2. SDK initialization version 3. Environment variable (toolkit-specific) ## Managing versions Check available versions using: ```python # Get toolkit information including available versions toolkit = composio.toolkits.get(slug="github") # Extract and print version information print(f"Toolkit: {toolkit.name}") print(f"Current Version: {toolkit.meta.version}") print(f"Available Versions: {toolkit.meta.available_versions}") ``` ```typescript // Get toolkit information including available versions const toolkit = await composio.toolkits.get("github"); // Extract and print version information console.log("Toolkit:", toolkit.name); console.log("Current Version:", toolkit.meta.version); console.log("Available Versions:", toolkit.meta.availableVersions); console.log("Latest Version:", toolkit.meta.availableVersions[0]); // First one is usually the latest ``` # User Management ## 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) ```python # 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' } ) ``` ```typescript // Use your database's user ID (UUID, primary key, etc.) const userId = user.id; // e.g., "550e8400-e29b-41d4-a716-446655440000" const tools = await composio.tools.get(userId, { toolkits: ['github'], }); const result = await composio.tools.execute('GITHUB_GET_REPO', { userId: userId, arguments: { owner: 'example', repo: '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](mailto:support@company.com), company GitHub org * Enterprise apps: IT manages connections for all employees ```python # 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!' } ) ``` ```typescript // Use the organization ID as userId const userId = organization.id; // e.g., "org_550e8400" // All users in the organization share the same connected accounts const tools = await composio.tools.get(userId, { toolkits: ['slack'], }); // Execute tools in the organization context const result = await composio.tools.execute('SLACK_SEND_MESSAGE', { userId: userId, arguments: { channel: '#general', text: '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 ```typescript import { Composio } from '@composio/core'; const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, }); // 1. Admin connects Slack for the entire organization async function connectOrganizationToSlack(organizationId: string, adminUserId: string) { // Use organization ID as userId in Composio const connectionRequest = await composio.toolkits.authorize(organizationId, 'slack'); // Store the connection request for the admin to complete await storeConnectionRequest(organizationId, adminUserId, connectionRequest); return connectionRequest.redirectUrl; } // 2. Any user in the organization can use the connected tools async function sendSlackMessage(organizationId: string, channel: string, message: string) { return await composio.tools.execute('SLACK_SEND_MESSAGE', { userId: organizationId, // organization ID, not individual user ID arguments: { channel: channel, text: message, }, }); } // 3. Check if organization has required connections async function getOrganizationTools(organizationId: string) { return await composio.tools.get(organizationId, { toolkits: ['slack', 'github', 'jira'], }); } // Usage in your API endpoint app.post('/api/slack/message', async (req, res) => { const { channel, message } = req.body; const organizationId = req.user.organizationId; // Get from your auth system // Verify user has permission to send messages for this organization if (!(await userCanSendMessages(req.user.id, organizationId))) { return res.status(403).json({ error: 'Insufficient permissions' }); } try { const result = await sendSlackMessage(organizationId, channel, message); res.json(result.data); } catch (error) { res.status(500).json({ error: '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 ```typescript import { Composio } from '@composio/core'; const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, }); // 1. User initiates GitHub connection async function connectUserToGitHub(userId: string) { const connectionRequest = await composio.toolkits.authorize(userId, 'github'); return connectionRequest.redirectUrl; } // 2. Get user's connected GitHub tools async function getUserGitHubTools(userId: string) { return await composio.tools.get(userId, { toolkits: ['github'], }); } // 3. Execute tool for specific user async function getUserRepos(userId: string) { return await composio.tools.execute('GITHUB_LIST_REPOS', { userId: userId, arguments: { per_page: 10, }, }); } // Usage in your API endpoint app.get('/api/github/repos', async (req, res) => { const userId = req.user.id; // Get from your auth system try { const repos = await getUserRepos(userId); res.json(repos.data); } catch (error) { res.status(500).json({ error: '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 ```python # 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 ) ``` ```typescript // Wrong: Using individual user ID for org-connected tool const userTools = await composio.tools.get(req.user.id, { toolkits: ['slack'], // Fails - Slack is connected at org level }); // Correct: Match the ID type to how the tool was connected const userPersonalTools = await composio.tools.get(req.user.id, { toolkits: ['gmail'], // User's personal Gmail }); const orgSharedTools = await composio.tools.get(req.user.organizationId, { 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. # Using Triggers When events occur in connected apps (like new Slack messages or GitHub commits), triggers automatically send the event data to your application. Each event is delivered as a structured payload to your webhook endpoint (via webhooks or WebSockets), enabling your applications or AI agents to respond proactively.
Before proceeding, ensure you've created an [auth config](/docs/authenticating-tools#creating-an-auth-config) and [established a connection](/docs/authenticating-tools#hosted-authentication-connect-link) to an app (e.g., Slack, GitHub). Triggers are scoped to specific users - learn about [User Management](/docs/user-management) for guidance on structuring User IDs. ## Creating a trigger You can create triggers using either the Composio dashboard or programmatically via the SDK. ### Using Dashboard To create triggers through the dashboard: 1. Navigate to the [Auth Configs](https://platform.composio.dev?next_page=%2Fauth-configs) page 2. Select the auth config 3. Click "Add Trigger" and navigate to "Active Triggers" tab to fetch the trigger ID