How to Manage Connection Requests for Third-Party App Authorizations in Composio

Managing connection requests in Composio involves creating a ConnectionRequest object via ConnectedAccounts.link() or initiate(), redirecting users to the provided redirectUrl, and polling for completion using waitForConnection() until the account status becomes ACTIVE or a specific error is thrown.

When building integrations with external services like GitHub, Google, or Snowflake, orchestrating OAuth flows reliably is essential for production applications. The Composio SDK provides a structured pattern to manage connection requests for third-party app authorizations, encapsulating the authentication handshake, state polling, and error handling. This implementation is exposed through the ConnectedAccounts class in ts/packages/core/src/models/ConnectedAccounts.ts and the ConnectionRequest model in ts/packages/core/src/models/ConnectionRequest.ts.

Creating a Connection Request

The SDK offers two primary entry points for initiating authorizations, both of which internally invoke the createConnectionRequest factory function to instantiate a request state object.

For standard OAuth-based third-party services, use the link() method to generate a connection request. This method accepts your internal user identifier and the authentication configuration ID, returning a ConnectionRequest instance containing the redirectUrl.

const connectionReq = await composio.connectedAccounts.link(
  'user_123',               // your internal user ID
  'auth_config_github'      // ID of the GitHub auth config
);
console.log('Visit this URL to authorize:', connectionReq.redirectUrl);

Programmatic Flow with ConnectedAccounts.initiate()

For scenarios requiring additional control—such as allowing multiple active connections per authentication configuration—use the initiate() method. This approach accepts an options object where you can disable the default single-connection guard.

const conn = await composio.connectedAccounts.initiate(
  'user_123',
  'auth_config_github',
  { allowMultiple: true }   // enables multiple active GitHub accounts
);
console.log('Redirect the user to:', conn.redirectUrl);
await conn.waitForConnection();

Handling the Authorization Redirect

Once instantiated, the ConnectionRequest object exposes a redirectUrl property that directs end-users to the third-party authorization page. Your application must navigate the user to this URL so the external service can authenticate and grant access permissions.

The request lifecycle is instrumented automatically via telemetry.instrument(state, 'ConnectionRequest'), enabling observability for each step of the authorization process.

Polling for Connection Completion

After the user is redirected, the SDK manages state polling through the waitForConnection(timeout?) method implemented in ts/packages/core/src/models/ConnectionRequest.ts. This helper continuously calls client.connectedAccounts.retrieve(state.id) against the Composio REST endpoint until one of three terminal states occurs:

  • ACTIVE — Connection succeeded. The method returns a transformed ConnectedAccountRetrieveResponse (normalized via ts/packages/core/src/utils/transformers/connectedAccounts.ts).
  • FAILED or EXPIRED — The SDK throws a ConnectionRequestFailedError.
  • Timeout — The SDK throws a ConnectionRequestTimeoutError if the polling duration exceeds the threshold.
// Default 60-second timeout
try {
  const connectedAccount = await connectionReq.waitForConnection();
  console.log('Connected account is now ACTIVE:', connectedAccount.id);
} catch (err) {
  if (err instanceof ConnectionRequestFailedError) {
    console.error('Connection failed:', err.message);
  } else if (err instanceof ConnectionRequestTimeoutError) {
    console.error('Timed out waiting for user to authorize.');
  }
}

Customizing Timeout Durations

By default, waitForConnection() polls for 60 seconds. Override this by passing a millisecond value as the first argument:

const timeoutMs = 2 * 60 * 1000; // 2 minutes
await connectionReq.waitForConnection(timeoutMs)
  .then(acc => console.log('Connected:', acc.id))
  .catch(err => console.error('Error:', err));

Error Handling and Edge Cases

The Composio SDK defines three specific error classes in ts/packages/core/src/errors/ConnectionRequestErrors.ts to handle distinct failure modes during the request lifecycle:

  • ComposioConnectedAccountNotFoundError — Thrown when the connection request ID does not exist or has been deleted from the system.
  • ConnectionRequestFailedError — Indicates terminal error states when the authorization flow fails or the request expires before completion.
  • ConnectionRequestTimeoutError — Raised when polling exceeds the specified timeout threshold without reaching an ACTIVE state.
try {
  await connectionReq.waitForConnection();
} catch (err) {
  if (err instanceof ConnectionRequestFailedError) {
    console.error('Connection failed:', err.message);
  } else if (err instanceof ConnectionRequestTimeoutError) {
    console.error('Timed out waiting for user to authorize.');
  } else if (err instanceof ComposioConnectedAccountNotFoundError) {
    console.error('The request ID is invalid or has been deleted.');
  } else {
    console.error('Unexpected error:', err);
  }
}

Advanced Configuration

Allowing Multiple Connected Accounts

By default, Composio restricts users to one active connection per authentication configuration. To enable users to authorize multiple accounts from the same service (e.g., two separate GitHub accounts), pass { allowMultiple: true } to the initiate() method as demonstrated in the programmatic flow example above.

Telemetry and Observability

Each ConnectionRequest is automatically instrumented with telemetry data, allowing you to monitor authorization success rates, latency, and failure states across your application.

Summary

  • Create connection requests using ConnectedAccounts.link() for OAuth flows or ConnectedAccounts.initiate() for programmatic control, both implemented in ts/packages/core/src/models/ConnectedAccounts.ts.
  • Redirect users to the redirectUrl property on the returned ConnectionRequest object to initiate third-party authorization.
  • Poll for completion using waitForConnection(timeout?), which queries the Composio API via client.connectedAccounts.retrieve() until the status becomes ACTIVE.
  • Handle three specific error types defined in ts/packages/core/src/errors/ConnectionRequestErrors.ts: ConnectionRequestFailedError, ConnectionRequestTimeoutError, and ComposioConnectedAccountNotFoundError.
  • Enable multiple connections per auth configuration by setting allowMultiple: true in the initiation options.
  • Monitor request lifecycles through built-in telemetry instrumentation that tracks state transitions automatically.

Frequently Asked Questions

What is the default timeout for waitForConnection()?

The default timeout is 60 seconds (60,000 milliseconds). You can specify a custom duration by passing a millisecond value as the first argument to waitForConnection(), such as waitForConnection(120000) for two minutes, as implemented in ts/packages/core/src/models/ConnectionRequest.ts.

How does Composio handle expired or failed connection requests?

When polling detects a FAILED or EXPIRED status, the SDK throws a ConnectionRequestFailedError from ts/packages/core/src/errors/ConnectionRequestErrors.ts. This represents a terminal state, requiring you to create a new connection request to retry the authorization flow.

Can I allow users to connect multiple accounts from the same service?

Yes. By default, Composio restricts users to one active connection per authentication configuration. To bypass this limitation, pass { allowMultiple: true } as the third argument to ConnectedAccounts.initiate(), which disables the single-connection guard and enables multiple active sessions.

Where is the connection request polling logic implemented?

The polling mechanism, state management, and error throwing are implemented in the createConnectionRequest factory function within ts/packages/core/src/models/ConnectionRequest.ts. This module handles the transition states and transforms the raw API response into a typed ConnectedAccountRetrieveResponse using utilities from ts/packages/core/src/utils/transformers/connectedAccounts.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →