Composio Integration¶
Composio is an MIT-licensed, self-hostable integration framework that FlowForge uses to connect with 250+ external services. Instead of maintaining individual API clients for each service, FlowForge maps its integration actions to Composio tool slugs and delegates execution.
How ComposioAdaptor Works¶
The ComposioAdaptor class implements the IntegrationAdaptor interface. It translates FlowForge (service, action) pairs to Composio tool slugs using an internal TOOL_MAP:
ctx.integrate('slack', 'sendMessage', { channel, text })
|
v
ComposioAdaptor.execute('sendMessage', params, connectionId)
|
v
TOOL_MAP['slack']['sendMessage'] => 'SLACK_SEND_A_MESSAGE'
|
v
composioClient.tools.execute('SLACK_SEND_A_MESSAGE', {
userId: connectionId,
arguments: { channel, text }
})
Key details:
- The
connectionIdfrom FlowForge maps to Composio'suserIdparameter, which routes to the correct OAuth connection. - The adaptor strips
connectionIdfrom the arguments before passing them to Composio. - Errors from Composio are wrapped in
IntegrationErrorwith the integration name for clear debugging.
TOOL_MAP Reference¶
The TOOL_MAP is a Record<string, Record<string, string>> that maps each integration and action to a Composio tool slug. The convention is <TOOLKIT>_<ACTION> in UPPER_SNAKE_CASE.
Example entries:
const TOOL_MAP = {
slack: {
sendMessage: 'SLACK_SEND_A_MESSAGE',
updateMessage: 'SLACK_UPDATE_A_MESSAGE',
addReaction: 'SLACK_ADD_A_REACTION',
createChannel: 'SLACK_CREATE_A_CHANNEL',
listUsers: 'SLACK_LIST_USERS',
uploadFile: 'SLACK_UPLOAD_A_FILE',
},
github: {
createIssue: 'GITHUB_CREATE_AN_ISSUE',
createPR: 'GITHUB_CREATE_A_PULL_REQUEST',
addComment: 'GITHUB_CREATE_AN_ISSUE_COMMENT',
createRelease: 'GITHUB_CREATE_A_RELEASE',
listRepos: 'GITHUB_LIST_REPOSITORIES_FOR_THE_AUTHENTICATED_USER',
},
stripe: {
createCharge: 'STRIPE_CREATE_A_CHARGE',
createCustomer: 'STRIPE_CREATE_A_CUSTOMER',
createSubscription: 'STRIPE_CREATE_A_SUBSCRIPTION',
listPayments: 'STRIPE_LIST_ALL_CHARGES',
createRefund: 'STRIPE_CREATE_A_REFUND',
},
// ... 35+ more integrations
};
The full map covers all 38+ integrations. See the source at packages/integration-service/src/adaptors/composio.ts for the complete reference.
createComposioAdaptors()¶
The factory function creates a ComposioAdaptor instance for every entry in TOOL_MAP:
import { Composio } from 'composio-core';
import { createComposioAdaptors } from '@flowforgejs/integration-service';
const composioClient = new Composio({ apiKey: 'your-key' });
const adaptors = createComposioAdaptors(composioClient);
// Register all adaptors with the engine
for (const adaptor of adaptors) {
engine.registerIntegration(adaptor);
}
This registers one adaptor per integration (slack, github, stripe, etc.), each with the correct action list derived from its TOOL_MAP entry.
Adding New Integrations¶
To add a new integration, extend the TOOL_MAP in packages/integration-service/src/adaptors/composio.ts:
// 1. Add the mapping
const TOOL_MAP = {
// existing entries...
myService: {
doThing: 'MY_SERVICE_DO_THING',
doOtherThing: 'MY_SERVICE_DO_OTHER_THING',
},
};
Then create the corresponding node in packages/nodes/src/communication/:
// 2. Create the node (packages/nodes/src/communication/my-service.ts)
import { z } from 'zod';
import { defineNode } from '@flowforgejs/sdk';
const inputSchema = z.object({
action: z.enum(['doThing', 'doOtherThing']),
// action-specific fields...
});
const outputSchema = z.object({
result: z.unknown(),
});
const configSchema = z.object({
connectionId: z.string(),
});
export const myServiceNode = defineNode({
name: 'communication/my-service',
version: '0.1.0',
description: 'Interact with My Service',
category: 'communication',
inputSchema,
outputSchema,
configSchema,
tags: ['my-service'],
handler: async (ctx) => {
const { action, ...params } = ctx.input as z.infer<typeof inputSchema>;
const { connectionId } = ctx.config as z.infer<typeof configSchema>;
const result = await ctx.integrate('myService', action, {
connectionId,
...params,
});
return { result };
},
});
Finding tool slugs
Browse the Composio tool catalog to find the correct tool slugs for new integrations.
Authentication Flow¶
- A user authenticates with a service through Composio's connection management (OAuth, API key, etc.).
- Composio stores the connection and returns a
connectionId. - The
connectionIdis stored in FlowForge and passed as theuserIdto Composio when executing tools. - Composio uses the stored credentials to make authenticated API calls on behalf of the user.
User -> Composio OAuth -> connectionId stored
Workflow -> ctx.integrate('slack', 'sendMessage', { connectionId }) -> ComposioAdaptor -> Composio SDK -> Slack API
ComposioClient Interface¶
The adaptor depends on a minimal interface rather than the full Composio SDK, making it easy to mock in tests:
interface ComposioClient {
tools: {
execute(
toolSlug: string,
options: { userId: string; arguments: Record<string, unknown> },
): Promise<unknown>;
};
}
Self-Hosting¶
Composio is MIT-licensed and can be self-hosted. When running your own instance:
- Deploy Composio according to its documentation.
- Set
composioBaseUrlin your FlowForge integration config to point at your instance. - No external API key is needed -- authentication is handled by your own instance.
Direct adaptors
For services where you need full control over the API client (e.g., custom SMTP configuration, webhook signing), FlowForge also supports direct adaptors that bypass Composio entirely. These implement the IntegrationAdaptor interface directly.