Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
Activepieces
https://github.com/activepieces/activepieces
Admin
Activepieces is an open source replacement for Zapier that enables all-in-one AI automation through
...
Tokens:
129,495
Snippets:
2,115
Trust Score:
9
Update:
1 day ago
Context
Skills
Chat
Benchmark
79.3
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Activepieces Activepieces is an open-source, all-in-one AI automation platform designed as an extensible, self-hostable alternative to Zapier. It enables users and developers to build automated workflows ("flows") by connecting triggers and actions across 280+ integrated services. Flows are constructed visually in a no-code builder, while the underlying pieces (integrations) are TypeScript npm packages that developers can create, publish, and share. The platform supports enterprise deployment with features like multi-project management, SSO, audit logs, embedding, and an MCP server for AI assistant integration. The core architecture runs on Fastify (API), React (UI), PostgreSQL, and Redis (queue via BullMQ). Flow code executes inside sandboxes (V8 isolates or Linux namespaces) for isolation. Activepieces exposes a REST API for programmatic management of flows, projects, connections, and runs, and ships an embeddable SDK for white-label SaaS integrations. The pieces framework (`@activepieces/pieces-framework`) provides the TypeScript SDK for building custom integrations with typed properties, authentication helpers, polling utilities, and durable execution (waitpoints). --- ## Installation — Docker Compose Deploy Activepieces locally or to a server using Docker Compose with auto-generated environment variables. ```bash # Clone, generate env, and start git clone https://github.com/activepieces/activepieces.git cd activepieces sh tools/deploy.sh # Generates .env with random secrets docker compose -p activepieces up # Upgrade an existing installation sh tools/update.sh # or manually: git pull && docker compose pull && docker compose up -d --remove-orphans # Reset / delete all data sh tools/reset.sh ``` --- ## Key Environment Variables Configure Activepieces behavior via environment variables in the `.env` file. ```bash # Required AP_FRONTEND_URL=https://your-domain.com # Used for webhook URLs and OAuth redirects AP_JWT_SECRET=<openssl rand -hex 32> # JWT signing key AP_ENCRYPTION_KEY=<openssl rand -hex 16> # Connection encryption key (32 hex chars) AP_POSTGRES_HOST=localhost AP_POSTGRES_PORT=5432 AP_POSTGRES_USERNAME=activepieces AP_POSTGRES_PASSWORD=<password> AP_POSTGRES_DATABASE=activepieces # Database type: POSTGRES (default) or PGLITE (embedded, single-node) AP_DB_TYPE=POSTGRES # Sandbox isolation: UNSANDBOXED | SANDBOX_CODE_ONLY | SANDBOX_PROCESS | SANDBOX_CODE_AND_PROCESS AP_EXECUTION_MODE=SANDBOX_CODE_ONLY # Recommended for multi-tenant/K8s # Network security (UNRESTRICTED default, STRICT blocks SSRF) AP_NETWORK_MODE=STRICT AP_SSRF_ALLOW_LIST=10.0.0.5,10.10.0.0/24 # IPs that bypass STRICT mode # Redis queue AP_REDIS_URL=redis://localhost:6379 # Or use AP_REDIS_HOST/PORT/PASSWORD # Worker settings AP_WORKER_CONCURRENCY=5 # Parallel jobs per worker AP_FLOW_TIMEOUT_SECONDS=600 # Max flow runtime AP_TRIGGER_DEFAULT_POLL_INTERVAL=5 # Minutes between polling trigger checks # Storage: DB (default) or S3 AP_FILE_STORAGE_LOCATION=S3 AP_S3_BUCKET=my-bucket AP_S3_REGION=us-east-1 AP_S3_ACCESS_KEY_ID=AKIA... AP_S3_SECRET_ACCESS_KEY=... # Email AP_SMTP_HOST=mail.example.com AP_SMTP_PORT=587 AP_SMTP_USERNAME=user@example.com AP_SMTP_PASSWORD=secret AP_SMTP_SENDER_EMAIL=noreply@example.com # Execution data retention AP_EXECUTION_DATA_RETENTION_DAYS=30 AP_PAUSED_FLOW_TIMEOUT_DAYS=30 # Container role: WORKER_AND_APP | APP | WORKER AP_CONTAINER_TYPE=WORKER_AND_APP ``` --- ## REST API — Authentication All REST API endpoints require a Bearer API key generated from the Platform Dashboard. ```bash # List flows using Bearer token curl -X GET "https://your-instance.com/api/v1/flows" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" # Response uses seek pagination # { # "data": [ ...flows ], # "next": "cursor_for_next_page", # "previous": "cursor_for_prev_page" # } # Paginate with limit and cursor curl -X GET "https://your-instance.com/api/v1/flows?limit=20&cursor=NEXT_CURSOR" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## REST API — Flows CRUD operations for automation flows. ```bash # Create a flow curl -X POST "https://your-instance.com/api/v1/flows" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"displayName": "My Flow", "projectId": "proj_abc123"}' # List flows in a project curl -X GET "https://your-instance.com/api/v1/flows?projectId=proj_abc123&limit=10" \ -H "Authorization: Bearer YOUR_API_KEY" # Get a specific flow curl -X GET "https://your-instance.com/api/v1/flows/flow_xyz789" \ -H "Authorization: Bearer YOUR_API_KEY" # Update a flow — publish/enable it (LOCK_AND_PUBLISH) curl -X POST "https://your-instance.com/api/v1/flows/flow_xyz789" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"type": "LOCK_AND_PUBLISH", "request": {"status": "ENABLED"}}' # Enable/disable without publishing curl -X POST "https://your-instance.com/api/v1/flows/flow_xyz789" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"type": "CHANGE_STATUS", "request": {"status": "DISABLED"}}' # Delete a flow curl -X DELETE "https://your-instance.com/api/v1/flows/flow_xyz789" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## REST API — Flow Runs Query and manage flow execution history. ```bash # List all runs for a project (with optional filters) curl -X GET "https://your-instance.com/api/v1/flow-runs?projectId=proj_abc123&status=FAILED&limit=50" \ -H "Authorization: Bearer YOUR_API_KEY" # Get a specific run by ID curl -X GET "https://your-instance.com/api/v1/flow-runs/run_def456" \ -H "Authorization: Bearer YOUR_API_KEY" # Cancel a running flow curl -X POST "https://your-instance.com/api/v1/flow-runs/cancel" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id": "run_def456"}' ``` --- ## REST API — App Connections Manage authenticated connections to third-party services. ```bash # Create or update a connection (upsert by externalId) curl -X POST "https://your-instance.com/api/v1/app-connections" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "displayName": "My Slack Connection", "pieceName": "@activepieces/piece-slack", "projectId": "proj_abc123", "type": "SECRET_TEXT", "value": { "type": "SECRET_TEXT", "secret_text": "xoxb-your-slack-token" } }' # List connections in a project curl -X GET "https://your-instance.com/api/v1/app-connections?projectId=proj_abc123" \ -H "Authorization: Bearer YOUR_API_KEY" # Delete a connection curl -X DELETE "https://your-instance.com/api/v1/app-connections/conn_ghi789" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## REST API — Projects Create and manage projects (isolated workspaces within a platform). ```bash # List all projects curl -X GET "https://your-instance.com/api/v1/projects" \ -H "Authorization: Bearer YOUR_API_KEY" # Create a project with an external ID for embedding curl -X POST "https://your-instance.com/api/v1/projects" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "displayName": "Customer Project", "externalId": "org_1234", "metadata": {} }' # Update a project curl -X POST "https://your-instance.com/api/v1/projects/proj_abc123" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"displayName": "Updated Name"}' # Delete a project curl -X DELETE "https://your-instance.com/api/v1/projects/proj_abc123" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## REST API — Folders Organize flows into named folders within a project. ```bash # Create a folder curl -X POST "https://your-instance.com/api/v1/folders" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"displayName": "Marketing Automations", "projectId": "proj_abc123"}' # List folders in a project curl -X GET "https://your-instance.com/api/v1/folders?projectId=proj_abc123" \ -H "Authorization: Bearer YOUR_API_KEY" # Update folder name curl -X POST "https://your-instance.com/api/v1/folders/folder_jkl012" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"displayName": "Email Campaigns"}' # Delete a folder curl -X DELETE "https://your-instance.com/api/v1/folders/folder_jkl012" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## REST API — Global Connections (Enterprise) Manage platform-level connections shared across projects (used for predefined/white-label connections). ```bash # Create a global connection (scoped to specific projects) curl -X POST "https://your-instance.com/api/v1/global-connections" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "displayName": "Shared Gelato Connection", "pieceName": "@activepieces/piece-gelato", "type": "CUSTOM_AUTH", "value": { "type": "CUSTOM_AUTH", "props": { "base_url": "https://api.gelato.io", "access_token": "secret-token" } }, "scope": "PLATFORM", "projectIds": ["proj_abc123"], "externalId": "gelato_org_1234" }' # List global connections curl -X GET "https://your-instance.com/api/v1/global-connections" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## REST API — Tables Full CRUD for structured data tables (key-value/relational store within projects). ```bash # List tables in a project curl -X GET "https://your-instance.com/api/v1/tables?projectId=proj_abc123" \ -H "Authorization: Bearer YOUR_API_KEY" # Create records in a table curl -X POST "https://your-instance.com/api/v1/records" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "tbl_mno345", "records": [ {"name": "Alice", "email": "alice@example.com"}, {"name": "Bob", "email": "bob@example.com"} ] }' # Delete a record curl -X DELETE "https://your-instance.com/api/v1/records/rec_pqr678" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## Piece SDK — `createPiece` Define a new integration piece with its display name, logo, authentication, actions, and triggers. ```typescript import { PieceAuth, createPiece } from '@activepieces/pieces-framework'; import { getIcecreamFlavor } from './lib/actions/get-icecream-flavor'; import { newFlavorCreated } from './lib/triggers/new-flavor-created'; // Define authentication (API key in this case) export const gelatoAuth = PieceAuth.SecretText({ displayName: 'API Key', required: true, description: 'Enter your Gelato API key', validate: async ({ auth }) => { if (auth.startsWith('sk_')) { return { valid: true }; } return { valid: false, error: 'Invalid API Key — must start with sk_' }; }, }); // Register the piece export const gelato = createPiece({ displayName: 'Gelato', logoUrl: 'https://cdn.activepieces.com/pieces/gelato.png', auth: gelatoAuth, authors: ['your-github-handle'], actions: [getIcecreamFlavor], triggers: [newFlavorCreated], minimumSupportedRelease: '0.20.0', }); ``` --- ## Piece SDK — Authentication Types `PieceAuth` provides multiple authentication strategies for collecting and validating user credentials. ```typescript import { PieceAuth, OAuth2GrantType } from '@activepieces/pieces-framework'; // Secret text (API key, token) const secretAuth = PieceAuth.SecretText({ displayName: 'API Key', required: true, validate: async ({ auth }) => ({ valid: auth.length > 0, error: 'Empty key' }), }); // Username + password const basicAuth = PieceAuth.BasicAuth({ displayName: 'Credentials', required: true, username: { displayName: 'Username', description: 'Your username' }, password: { displayName: 'Password', description: 'Your password' }, }); // Custom auth with multiple fields (e.g., base URL + token) const customAuth = PieceAuth.CustomAuth({ displayName: 'Custom Auth', required: true, props: { base_url: Property.ShortText({ displayName: 'Base URL', required: true }), access_token: PieceAuth.SecretText({ displayName: 'Access Token', required: true }), }, validate: async ({ auth }) => ({ valid: !!auth.props.access_token }), }); // OAuth2 authorization code flow const oauth2Auth = PieceAuth.OAuth2({ displayName: 'OAuth2', required: true, grantType: OAuth2GrantType.AUTHORIZATION_CODE, authUrl: 'https://example.com/oauth/authorize', tokenUrl: 'https://example.com/oauth/token', scope: ['read', 'write'], }); // OAuth2 client credentials (service accounts) const clientCredentialsAuth = PieceAuth.OAuth2({ displayName: 'Service Account', required: true, grantType: OAuth2GrantType.CLIENT_CREDENTIALS, tokenUrl: 'https://example.com/oauth/token', scope: ['api'], }); ``` --- ## Piece SDK — `createAction` Define an action — a task that runs when a flow executes a step. ```typescript import { createAction, Property } from '@activepieces/pieces-framework'; import { httpClient, HttpMethod } from '@activepieces/pieces-common'; import { gelatoAuth } from '../..'; export const createOrder = createAction({ name: 'create_order', // Unique identifier within the piece (never change) auth: gelatoAuth, displayName: 'Create Order', description: 'Creates a new Gelato order', props: { productId: Property.Dropdown({ displayName: 'Product', required: true, refreshers: ['auth'], options: async ({ auth }) => { if (!auth) return { disabled: true }; const res = await httpClient.sendRequest<{ id: string; name: string }[]>({ method: HttpMethod.GET, url: 'https://api.gelato.io/products', headers: { Authorization: `Bearer ${auth}` }, }); return { options: res.body.map(p => ({ label: p.name, value: p.id })) }; }, }), quantity: Property.Number({ displayName: 'Quantity', required: true }), note: Property.LongText({ displayName: 'Note', required: false }), }, async run(context) { const { productId, quantity, note } = context.propsValue; const res = await httpClient.sendRequest({ method: HttpMethod.POST, url: 'https://api.gelato.io/orders', headers: { Authorization: `Bearer ${context.auth}` }, body: { productId, quantity, note }, }); return res.body; // Returned value is available as output in subsequent steps }, }); ``` --- ## Piece SDK — Property Types `Property` provides typed input fields rendered in the flow builder UI. ```typescript import { Property, MarkdownVariant } from '@activepieces/pieces-framework'; const exampleProps = { // Basic text name: Property.ShortText({ displayName: 'Name', required: true, defaultValue: 'John' }), description: Property.LongText({ displayName: 'Description', required: false }), count: Property.Number({ displayName: 'Count', required: true }), active: Property.Checkbox({ displayName: 'Active', required: false, defaultValue: true }), scheduledAt: Property.DateTime({ displayName: 'Run At', required: true }), file: Property.File({ displayName: 'Attachment', required: false }), tags: Property.Array({ displayName: 'Tags', required: false, defaultValue: ['tag1'] }), metadata: Property.Json({ displayName: 'Metadata', required: false, defaultValue: {} }), options: Property.Object({ displayName: 'Key-Value Pairs', required: false }), info: Property.MarkDown({ value: '> ⚠️ This will send an email.', variant: MarkdownVariant.WARNING }), // Static dropdown (predefined options) status: Property.StaticDropdown({ displayName: 'Status', required: true, options: { options: [{ label: 'Active', value: 'active' }, { label: 'Paused', value: 'paused' }] }, }), // Multi-select static roles: Property.StaticMultiSelectDropdown({ displayName: 'Roles', required: false, options: { options: [{ label: 'Admin', value: 'admin' }, { label: 'Viewer', value: 'viewer' }] }, }), // Dynamic dropdown (loads from API, refreshes when auth changes) projectId: Property.Dropdown({ displayName: 'Project', required: true, refreshers: ['auth'], refreshOnSearch: true, options: async ({ auth }, { searchValue }) => { if (!auth) return { disabled: true }; const res = await httpClient.sendRequest({ method: HttpMethod.GET, url: '/projects', headers: { Authorization: auth } }); return { options: res.body.map((p: any) => ({ label: p.name, value: p.id })) }; }, }), // Array of structured objects fields: Property.Array({ displayName: 'Fields', required: false, properties: { key: Property.ShortText({ displayName: 'Key', required: true }), value: Property.ShortText({ displayName: 'Value', required: true }), }, }), // Dynamic form — shape is determined at runtime by an API dynamicForm: Property.DynamicProperties({ displayName: 'Form', required: true, refreshers: ['auth'], props: async ({ auth }) => ({ field1: Property.ShortText({ displayName: 'Field 1', required: true }), field2: Property.Number({ displayName: 'Field 2', required: false }), }), }), }; ``` --- ## Piece SDK — Polling Trigger Create a trigger that polls an API every 5 minutes to detect new items. ```typescript import { DedupeStrategy, HttpMethod, Polling, httpClient, pollingHelper, } from '@activepieces/pieces-common'; import { TriggerStrategy, createTrigger, AppConnectionValueForAuthProperty, } from '@activepieces/pieces-framework'; import { gelatoAuth } from '../..'; import dayjs from 'dayjs'; // Time-based deduplication strategy const polling: Polling<AppConnectionValueForAuthProperty<typeof gelatoAuth>, Record<string, never>> = { strategy: DedupeStrategy.TIMEBASED, items: async ({ auth, lastFetchEpochMS }) => { const res = await httpClient.sendRequest<{ id: string; created_at: string; name: string }[]>({ method: HttpMethod.GET, url: `https://api.gelato.io/orders?since=${lastFetchEpochMS}`, headers: { Authorization: auth }, }); return res.body.map(order => ({ epochMilliSeconds: dayjs(order.created_at).valueOf(), data: order, })); }, }; // Last-ID deduplication strategy (alternative) const pollingById: Polling<AppConnectionValueForAuthProperty<typeof gelatoAuth>, Record<string, never>> = { strategy: DedupeStrategy.LAST_ITEM, items: async ({ auth }) => { const res = await httpClient.sendRequest<{ id: number; name: string }[]>({ method: HttpMethod.GET, url: 'https://api.gelato.io/orders', headers: { Authorization: auth }, }); return res.body.map(item => ({ id: item.id, data: item })); }, }; export const newOrderCreated = createTrigger({ auth: gelatoAuth, name: 'new_order_created', displayName: 'New Order Created', description: 'Triggers when a new Gelato order is placed', type: TriggerStrategy.POLLING, props: {}, sampleData: { id: 'order_sample', name: 'Chocolate Gelato', created_at: '2024-01-15T10:00:00Z' }, async test(context) { return pollingHelper.test(polling, context); }, async onEnable(context) { await pollingHelper.onEnable(polling, context); }, async onDisable(context) { await pollingHelper.onDisable(polling, context); }, async run(context) { return pollingHelper.poll(polling, context); }, }); ``` --- ## Piece SDK — Webhook Trigger Create a trigger that listens for real-time events via a webhook URL. ```typescript import { createTrigger, TriggerStrategy, WebhookHandshakeType } from '@activepieces/pieces-framework'; import { httpClient, HttpMethod } from '@activepieces/pieces-common'; import { gelatoAuth } from '../..'; export const newOrderWebhook = createTrigger({ auth: gelatoAuth, name: 'new_order_webhook', displayName: 'New Order (Webhook)', description: 'Triggers instantly when a new order event is received', type: TriggerStrategy.WEBHOOK, props: {}, sampleData: { event: 'order.created', orderId: 'ord_123', flavor: 'vanilla' }, async onEnable(context) { // Register webhook URL with third-party service const res = await httpClient.sendRequest<{ id: string }>({ method: HttpMethod.POST, url: 'https://api.gelato.io/webhooks', headers: { Authorization: context.auth }, body: { url: context.webhookUrl, events: ['order.created'] }, }); // Store webhook ID to use during disable await context.store.put('webhookId', res.body.id); }, async onDisable(context) { // Remove webhook registration from third-party service const webhookId = await context.store.get<string>('webhookId'); if (webhookId) { await httpClient.sendRequest({ method: HttpMethod.DELETE, url: `https://api.gelato.io/webhooks/${webhookId}`, headers: { Authorization: context.auth }, }); } }, async run(context) { // Return the webhook payload as a single-item array to trigger one flow run return [context.payload.body]; }, }); ``` --- ## Piece SDK — Persistent Storage Store and retrieve key-value data that persists across flow runs, scoped per flow or project. ```typescript import { StoreScope } from '@activepieces/pieces-framework'; // Inside an action or trigger run() / onEnable() / onDisable() async run(ctx) { // Write: store the last processed cursor at project scope await ctx.store.put('lastCursor', 'cursor_abc123', StoreScope.PROJECT); // Write at flow scope (default if scope omitted) await ctx.store.put('runCount', 42); // Read: retrieve a typed value const lastCursor = await ctx.store.get<string>('lastCursor', StoreScope.PROJECT); const runCount = await ctx.store.get<number>('runCount'); // flow scope // Delete await ctx.store.delete('lastCursor', StoreScope.PROJECT); return { lastCursor, runCount }; } ``` --- ## Piece SDK — Flow Control (Stop & Waitpoints) Control flow execution: stop early, pause durably, or delay until a future timestamp. ```typescript import { ExecutionType } from '@activepieces/shared'; import { StatusCodes } from 'http-status-codes'; // --- Stop the flow and return an HTTP response --- async run(context) { if (!context.propsValue.approved) { context.run.stop({ response: { status: StatusCodes.FORBIDDEN, body: { error: 'Not approved' }, headers: {} }, }); return {}; } return { message: 'Approved!' }; } // --- Pause until an external callback (webhook waitpoint) --- async run(ctx) { if (ctx.executionType === ExecutionType.BEGIN) { const waitpoint = await ctx.run.createWaitpoint({ type: 'WEBHOOK' }); const callbackUrl = waitpoint.buildResumeUrl({ queryParams: { runId: ctx.run.id } }); // Email the callbackUrl to an approver, post it to Slack, etc. await sendApprovalEmail({ url: callbackUrl }); ctx.run.waitForWaitpoint(waitpoint.id); return {}; } // Second invocation after callback is received const approved = ctx.resumePayload.queryParams['action'] === 'approve'; return { approved, metadata: ctx.resumePayload.body }; } // --- Respond immediately AND pause (fire-and-wait) --- async run(ctx) { if (ctx.executionType === ExecutionType.BEGIN) { const waitpoint = await ctx.run.createWaitpoint({ type: 'WEBHOOK', responseToSend: { status: 200, headers: {}, body: { received: true } }, }); const nextUrl = waitpoint.buildResumeUrl({ sync: true }); ctx.run.waitForWaitpoint(waitpoint.id); return { callbackUrl: nextUrl }; } return { resumeBody: ctx.resumePayload.body }; } // --- Delay until a future timestamp --- async run(ctx) { if (ctx.executionType === ExecutionType.RESUME) { return { delayCompleted: true, resumedAt: new Date().toISOString() }; } const resumeAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours const waitpoint = await ctx.run.createWaitpoint({ type: 'DELAY', resumeDateTime: resumeAt.toUTCString(), }); ctx.run.waitForWaitpoint(waitpoint.id); return {}; } ``` --- ## Piece SDK — File Handling Read and write files within actions and triggers using the `ctx.files` API. ```typescript import { createAction, Property } from '@activepieces/pieces-framework'; export const generateReport = createAction({ name: 'generate_report', displayName: 'Generate Report', description: 'Generates a CSV report and returns a file reference', props: { attachment: Property.File({ displayName: 'Existing File', required: false }), }, async run(ctx) { // Write a file — returns a reference usable in subsequent steps const csvData = 'id,name\n1,Alice\n2,Bob\n'; const fileRef = await ctx.files.write({ fileName: 'report.csv', data: Buffer.from(csvData, 'utf-8'), }); // ctx.propsValue.attachment is auto-parsed from Property.File // and available as a Buffer or base64 string for downstream use const inputFile = ctx.propsValue.attachment; return { reportFile: fileRef, // Pass this to email/storage actions inputReceived: !!inputFile, }; }, }); ``` --- ## Piece SDK — Scaffold a New Piece Generate the complete scaffolding for a new community or custom piece using the CLI. ```bash # Generate a new piece interactively npm run cli pieces create # ? Enter the piece name: my-service # ? Enter the package name: @activepieces/piece-my-service # ? Select the piece type: community # Generate an action for the piece npm run cli actions create # ? Enter the piece folder name: my-service # ? Enter the action display name: Send Message # ? Enter the action description: Sends a message to the service # Generate a trigger for the piece npm run cli triggers create # ? Enter the piece folder name: my-service # ? Enter the trigger display name: New Event # ? Enter the trigger description: Triggers on new event # ? Select the trigger technique: polling (or: webhook) # Enable the piece for local development in .env.dev AP_DEV_PIECES=my-service # Build a specific piece npx turbo run build --filter=@activepieces/piece-my-service # Run hot-reload dev mode npm run dev # Install an npm dependency for a piece (use bun from piece directory) cd packages/pieces/community/my-service bun install --save some-library ``` --- ## Embedding SDK — Embed Builder Embed the Activepieces flow builder iframe into a SaaS application (Enterprise feature). ```html <!-- Include SDK (no async/defer) --> <script src="https://cdn.activepieces.com/sdk/embed/0.9.0.js"></script> <div id="automation-container" style="width:100%;height:800px;"></div> <script> // jwtToken must be generated server-side — see Provision Users docs const jwtToken = 'GENERATED_JWT_TOKEN'; activepieces.configure({ instanceUrl: 'https://your-activepieces-instance.com', jwtToken: jwtToken, prefix: '/automations', // Optional URL prefix embedding: { containerId: 'automation-container', builder: { disableNavigation: false, // Hide back/home button hideFlowName: false, // Hide flow name header homeButtonClickedHandler: () => { // Override home button window.history.back(); }, }, dashboard: { hideSidebar: false, hideFlowsPageNavbar: false, hidePageHeader: false, }, hideFolders: false, hideTables: false, hideExportAndImportFlow: false, hideDuplicateFlow: false, styling: { fontUrl: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&display=swap', fontFamily: 'Inter', mode: 'light', // 'light' | 'dark' }, locale: 'en', // ISO 639-1 locale navigation: { handler: ({ route }) => { // Sync iframe route to host app URL window.history.replaceState(null, '', `/automations${route}`); }, }, }, }).then(() => console.log('Activepieces authenticated')); </script> ``` --- ## Embedding SDK — Create Connections Allow users to authenticate third-party services from within your SaaS embedding. ```html <button onclick="connectSlack()">Connect Slack</button> <script src="https://cdn.activepieces.com/sdk/embed/0.9.0.js"></script> <script> // Initialize SDK first (see Embed Builder section) activepieces.configure({ instanceUrl: '...', jwtToken: '...' }); async function connectSlack() { const result = await activepieces.connect({ pieceName: '@activepieces/piece-slack', connectionName: 'user_slack_connection', // Optional: externalId for the connection // newWindow: { width: 600, height: 700 } // Open in popup instead of full page }); if (result.connection) { console.log('Connection created:', result.connection.id); console.log('External ID (name):', result.connection.name); // Store result.connection.name in your DB to reference this connection later } else { console.log('User cancelled the connection dialog'); } } </script> ``` --- ## Embedding SDK — Predefined (Global) Connections Programmatically create shared connections for embedded projects so users don't need to authenticate manually. ```javascript const apiKey = 'YOUR_PLATFORM_API_KEY'; const instanceUrl = 'https://your-instance.com'; // Step 1: Get or create a project for this customer async function getOrCreateProject(externalProjectId) { const list = await fetch(`${instanceUrl}/api/v1/projects?externalId=${externalProjectId}`, { headers: { Authorization: `Bearer ${apiKey}` }, }).then(r => r.json()); if (list.data.length > 0) return list.data[0]; return fetch(`${instanceUrl}/api/v1/projects`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ displayName: externalProjectId, externalId: externalProjectId }), }).then(r => r.json()); } // Step 2: Create a global connection scoped to that project async function createGlobalConnection({ projectId, externalProjectId }) { return fetch(`${instanceUrl}/api/v1/global-connections`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ displayName: 'My Service Connection', pieceName: '@activepieces/piece-my-service', type: 'CUSTOM_AUTH', value: { type: 'CUSTOM_AUTH', props: { base_url: 'https://api.my-service.com', access_token: 'tok_secret' }, }, scope: 'PLATFORM', projectIds: [projectId], externalId: `my_service_${externalProjectId}`, // Naming convention for lookup }), }); } // Usage: call when generating a JWT for a customer const project = await getOrCreateProject('org_1234'); await createGlobalConnection({ projectId: project.id, externalProjectId: 'org_1234' }); ``` --- ## MCP Server — Connect AI Assistants Configure an MCP (Model Context Protocol) server to let AI assistants build and manage flows via natural language. ```json // ~/.cursor/mcp.json (Cursor) // ~/Library/Application Support/Claude/claude_desktop_config.json (Claude Desktop) { "mcpServers": { "activepieces": { "url": "https://your-instance.com/mcp" } } } ``` ``` # Example AI prompts once connected: "Create a flow that sends a Slack message when a new row is added to Google Sheets" "List all disabled flows in my project" "Validate my flow for issues before publishing" "Create a Contacts table with name, email, and phone fields, then add 3 sample records" "Show me the last 5 failed runs and what went wrong" "What triggers are available for the GitHub piece?" ``` --- ## MCP Server — Available Tool Categories Reference of MCP tool categories and key tools exposed to AI clients. ``` Discovery (always available, read-only): ap_list_flows — List flows with status, trigger type, published state ap_flow_structure — Full step tree, input values, insert locations for a flow ap_validate_flow — Check a flow for structural issues without publishing ap_list_pieces — Browse available pieces with actions/triggers ap_get_piece_props — Get input schema for a specific action/trigger ap_validate_step_config — Validate a step's config before applying it ap_list_connections — List available OAuth/app connections Flow Management (enable/disable per project): ap_create_flow — Create a new blank flow ap_duplicate_flow — Clone an existing flow ap_rename_flow — Rename a flow ap_publish_flow — Publish and optionally enable a flow Flow Building: ap_add_action — Add a new step to a flow ap_update_step — Update an existing step's configuration ap_delete_step — Remove a step from a flow ap_update_trigger — Change the flow's trigger Tables: ap_create_table — Create a new data table ap_list_tables — List tables in the project ap_create_records — Add records to a table ap_update_record — Update an existing record ap_delete_records — Delete records Testing & Runs: ap_test_flow — Test a flow with sample data ap_list_runs — List recent flow runs ap_get_run_details — Inspect a run's step-by-step output ap_retry_run — Retry a failed run ``` --- ## Summary Activepieces serves two primary audiences: **end-users** who automate business workflows visually (CRM sync, notifications, data pipelines), and **developers** who build and embed automation capabilities into their own SaaS products. End-users benefit from a no-code flow builder with 280+ integrations spanning Google Workspace, Slack, GitHub, OpenAI, databases, and more, while developers use the TypeScript pieces framework to create new integrations with hot-reload development, typed properties, built-in HTTP client, persistent storage, and durable execution (waitpoints for long-running human-in-the-loop flows). The REST API provides full programmatic control over every resource — flows, runs, connections, projects, and tables — all secured with Bearer API keys and cursor-based pagination. For platform integrators, Activepieces uniquely supports white-label embedding via the JavaScript SDK: the flow builder renders inside an iframe in any SaaS, users authenticate their connections via an SDK dialog, and global connections allow platform admins to pre-provision credentials so customers never see raw API keys. Enterprise features include SSO (SAML/SCIM), audit logs, role-based project access, canary deployments, secret manager integrations (AWS Secrets Manager, HashiCorp Vault, 1Password), and the MCP server — which exposes the entire Activepieces control plane as a catalog of tools consumable by AI assistants like Claude Desktop, Cursor, and Windsurf, enabling natural-language flow authoring and management at scale.