Skip to content

Workflows

client.workflows exposes both channel automation (v2/backend route) and the flow engine (CRUD flows, runs, folders, connections, pieces, MCP servers, tables, invitations).

Initialize the client first (see Installation or Quick Start). For an end-to-end walkthrough that wires a workflow to an AI agent, see Full Flow Guide §2.


Channel Automation — client.workflows

const { data: automations } = await client.workflows.listChannelAutomation();
const { data: whatsappFlows } = await client.workflows.listChannelAutomation({ channelType: "whatsapp" });

Flows

CRUD

const { data: flows } = await client.workflows.listFlows();
const flow = await client.workflows.getFlow("flow_id");
const newFlow = await client.workflows.createFlow({
displayName: "New Lead Notification",
projectId: "project_id",
});
await client.workflows.deleteFlow("flow_id");

Flow Builder — buildFlow / addSteps

TypeScript SDK v1.4.0+. Not yet in the Python SDK — use apply_flow_operation (below).

buildFlow creates a whole flow — trigger plus (optionally nested) steps — in one call, from a declarative spec. It auto-fills the two things the raw flow-operation API requires and that are easy to miss: a propertySettings entry for every piece input, and stepLocationRelativeToParent on actions added after a loop (without it, later steps are silently orphaned). Step names auto-increment (step_1, step_2, …) unless a step sets name.

const flow = await client.workflows.buildFlow({
displayName: "Qualify new leads",
trigger: {
kind: "piece",
pieceName: "@activepieces/piece-webhook",
pieceVersion: "0.1.24",
triggerName: "catch_webhook",
displayName: "Webhook",
},
steps: [
{
kind: "code",
displayName: "Score lead",
code: "export const code = async (inputs) => inputs;",
},
{
kind: "loop",
displayName: "For each lead",
items: "{{trigger.leads}}",
steps: [
{
kind: "piece",
displayName: "Notify sales",
pieceName: "@activepieces/piece-slack",
pieceVersion: "0.5.1",
actionName: "send_channel_message",
input: { channel: "#sales" },
},
],
},
],
});

Step kinds: code (inline TypeScript), piece (a piece action), and loop (iterates items, may nest steps). The trigger is either a piece trigger or { kind: "empty" }; omit it to keep the flow’s default empty trigger.

addSteps appends steps to an existing flow with the same auto-fill. By default steps attach after the trigger; pass parentStep / location to attach elsewhere (e.g. inside a loop). startIndex continues the auto step-name counter so names stay unique:

await client.workflows.addSteps(
flow.id,
[{ kind: "code", displayName: "Log result", code: "export const code = async () => 'done';" }],
{ parentStep: "step_2", location: "INSIDE_LOOP", startIndex: 3 },
);

Apply operation

await client.workflows.applyFlowOperation("flow_id", {
type: "UPDATE_TRIGGER",
request: {
name: "trigger",
type: "PIECE_TRIGGER",
valid: true,
displayName: "Webhook",
settings: {
pieceName: "@activepieces/piece-webhook",
pieceVersion: "0.1.24",
triggerName: "catch_webhook",
input: {},
propertySettings: {},
},
},
});

Trigger a flow

// Fire and forget
await client.workflows.triggerFlow("flow_id", {
contactId: "contact_xxx",
event: "lead_qualified",
});
// Wait for result
const result = await client.workflows.triggerFlowSync("flow_id", {
contactId: "contact_xxx",
event: "lead_qualified",
});

Flow Runs

const { data: runs } = await client.workflows.listRuns({ flowId: "flow_id", limit: 20 });
const run = await client.workflows.getRun("run_id");

Folders

const { data: folders } = await client.workflows.listFolders();
const folder = await client.workflows.getFolder("folder_id");
const newFolder = await client.workflows.createFolder({ displayName: "CRM Automations", projectId: "project_id" });
const updated = await client.workflows.updateFolder("folder_id", { displayName: "Updated Name" });
await client.workflows.deleteFolder("folder_id");

App Connections

const { data: connections } = await client.workflows.listConnections();
const connection = await client.workflows.getConnection("connection_id");
await client.workflows.upsertConnection({
name: "slack-integration",
type: "OAUTH2",
value: { access_token: "xoxb-xxx" },
});
await client.workflows.deleteConnection("connection_id");

Pieces

const pieces = await client.workflows.listPieces({ limit: 20 });

Triggers

const status = await client.workflows.getTriggerRunStatus();
await client.workflows.testTrigger({ pieceName: "@activepieces/piece-webhook", flowId: "flow_id" });

Tables & Records

const { data: tables } = await client.workflows.listTables();
const table = await client.workflows.getTable("table_id");
const { data: records } = await client.workflows.listRecords({ tableId: "table_id" });

MCP Servers

const { data: servers } = await client.workflows.listMcpServers("project_id");
const server = await client.workflows.getMcpServer("mcp_server_id");
const newServer = await client.workflows.createMcpServer({ projectId: "project_id", name: "My MCP" });
await client.workflows.deleteMcpServer("mcp_server_id");
await client.workflows.rotateMcpToken("mcp_server_id");

User Invitations

const { data: invitations } = await client.workflows.listInvitations({ type: "PROJECT" });
await client.workflows.deleteInvitation("invitation_id");