activepieces

Activepieces Workflow Automation Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "activepieces" with this command: npx skills add vamseeachanta/workspace-hub/vamseeachanta-workspace-hub-activepieces

Activepieces Workflow Automation Skill

Master Activepieces for self-hosted no-code automation with type-safe custom pieces, visual flow building, and enterprise-ready workflow orchestration. This skill covers flow design, piece development, triggers, connections management, and production deployment patterns.

When to Use This Skill

USE when:

  • Building business automations with type-safe custom components

  • Self-hosting is required for data sovereignty and compliance

  • Need modular, reusable automation pieces

  • Creating approval workflows with human-in-the-loop

  • Connecting APIs with visual flow builder

  • Teams need both no-code and code-first options

  • Require MIT-licensed open-source automation

  • Building internal tool automations

DON'T USE when:

  • Complex DAG-based data pipeline orchestration (use Airflow)

  • CI/CD pipelines tightly coupled with git (use GitHub Actions)

  • Need 400+ pre-built integrations immediately (use n8n)

  • Sub-second latency requirements (use direct API calls)

  • Simple single-trigger cron jobs (use systemd timers)

Prerequisites

Installation Options

Option 1: Docker Compose (Recommended)

docker-compose.yml

version: '3.8'

services: activepieces: image: activepieces/activepieces:latest restart: always ports: - "8080:80" environment: - AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js - AP_ENVIRONMENT=prod - AP_FRONTEND_URL=http://localhost:8080 - AP_WEBHOOK_TIMEOUT_SECONDS=30 - AP_TRIGGER_DEFAULT_POLL_INTERVAL=5 - AP_POSTGRES_DATABASE=activepieces - AP_POSTGRES_HOST=postgres - AP_POSTGRES_PORT=5432 - AP_POSTGRES_USERNAME=activepieces - AP_POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - AP_ENCRYPTION_KEY=${AP_ENCRYPTION_KEY} - AP_JWT_SECRET=${AP_JWT_SECRET} - AP_QUEUE_MODE=MEMORY - AP_REDIS_HOST=redis - AP_REDIS_PORT=6379 - AP_SANDBOX_RUN_TIME_SECONDS=600 - AP_TELEMETRY_ENABLED=false depends_on: - postgres - redis volumes: - ./data:/root/.activepieces

postgres: image: postgres:15 restart: always environment: - POSTGRES_USER=activepieces - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=activepieces volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U activepieces"] interval: 10s timeout: 5s retries: 5

redis: image: redis:7 restart: always healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5

volumes: postgres_data:

Generate required secrets

export POSTGRES_PASSWORD=$(openssl rand -hex 32) export AP_ENCRYPTION_KEY=$(openssl rand -hex 16) export AP_JWT_SECRET=$(openssl rand -hex 32)

Start services

docker compose up -d

Access UI at http://localhost:8080

Option 2: Kubernetes with Helm

Add Activepieces Helm repo

helm repo add activepieces https://activepieces.github.io/activepieces helm repo update

Create namespace

kubectl create namespace activepieces

Create secrets

kubectl create secret generic activepieces-secrets
--namespace activepieces
--from-literal=encryption-key=$(openssl rand -hex 16)
--from-literal=jwt-secret=$(openssl rand -hex 32)
--from-literal=postgres-password=$(openssl rand -hex 32)

Install Activepieces

helm install activepieces activepieces/activepieces
--namespace activepieces
--set ingress.enabled=true
--set ingress.hosts[0].host=activepieces.example.com

Get initial setup URL

kubectl get ingress -n activepieces

Option 3: Local Development

Clone repository

git clone https://github.com/activepieces/activepieces.git cd activepieces

Install dependencies

npm install

Setup environment

cp .env.example .env

Start development server

npm run dev

Access at http://localhost:4200

Development Setup for Custom Pieces

Install Activepieces CLI

npm install -g @activepieces/cli

Create new piece project

ap create-piece my-custom-piece

Navigate to piece directory

cd pieces/my-custom-piece

Install dependencies

npm install

Build piece

npm run build

Test piece locally

npm run test

Core Capabilities

  1. Basic Flow Structure

// Flow definition structure interface Flow { id: string; projectId: string; folderId?: string; status: 'ENABLED' | 'DISABLED'; schedule?: { cronExpression: string; timezone: string; }; trigger: Trigger; steps: Step[]; }

// Example flow JSON const basicFlow = { "displayName": "New Customer Onboarding", "trigger": { "name": "webhook", "type": "WEBHOOK", "settings": { "inputUiInfo": {} }, "valid": true, "displayName": "Webhook Trigger" }, "steps": [ { "name": "validate_data", "type": "CODE", "settings": { "input": { "customer": "{{trigger.body.customer}}" }, "sourceCode": { "code": "export const code = async (inputs) => {\n const { customer } = inputs;\n \n if (!customer.email || !customer.name) {\n throw new Error('Missing required fields');\n }\n \n return {\n valid: true,\n customer: {\n ...customer,\n email: customer.email.toLowerCase().trim(),\n created_at: new Date().toISOString()\n }\n };\n};" } }, "displayName": "Validate Customer Data" }, { "name": "create_crm_contact", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-hubspot", "pieceVersion": "~0.5.0", "actionName": "create_contact", "input": { "email": "{{validate_data.customer.email}}", "firstName": "{{validate_data.customer.name.split(' ')[0]}}", "lastName": "{{validate_data.customer.name.split(' ').slice(1).join(' ')}}", "properties": { "source": "activepieces_onboarding", "created_via": "automation" } } }, "displayName": "Create HubSpot Contact" }, { "name": "send_welcome_email", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-sendgrid", "pieceVersion": "~0.3.0", "actionName": "send_email", "input": { "to": "{{validate_data.customer.email}}", "subject": "Welcome to Our Platform!", "html": "<h1>Welcome, {{validate_data.customer.name}}!</h1><p>We're excited to have you on board.</p>" } }, "displayName": "Send Welcome Email" }, { "name": "notify_team", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-slack", "pieceVersion": "~0.6.0", "actionName": "send_message", "input": { "channel": "#new-customers", "text": "New customer onboarded: {{validate_data.customer.name}} ({{validate_data.customer.email}})" } }, "displayName": "Notify Sales Team" } ] };

  1. Webhook Triggers

// Webhook trigger configuration const webhookFlow = { "displayName": "Payment Webhook Handler", "trigger": { "name": "webhook", "type": "WEBHOOK", "settings": { "inputUiInfo": { "customizedInputs": {} } }, "displayName": "Payment Webhook" }, "steps": [ { "name": "verify_signature", "type": "CODE", "settings": { "input": { "payload": "{{trigger.body}}", "signature": "{{trigger.headers['x-signature']}}", "secret": "{{connections.payment_webhook_secret}}" }, "sourceCode": { "code": ` import crypto from 'crypto';

export const code = async (inputs) => { const { payload, signature, secret } = inputs;

const expectedSignature = crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex');

if (signature !== expectedSignature) { throw new Error('Invalid webhook signature'); }

return { verified: true, event: payload.event_type, data: payload.data }; };` } }, "displayName": "Verify Webhook Signature" }, { "name": "route_by_event", "type": "BRANCH", "settings": { "conditions": [ { "name": "payment_completed", "expression": { "type": "EXPRESSION", "value": "{{verify_signature.event}} === 'payment.completed'" } }, { "name": "payment_failed", "expression": { "type": "EXPRESSION", "value": "{{verify_signature.event}} === 'payment.failed'" } }, { "name": "refund_initiated", "expression": { "type": "EXPRESSION", "value": "{{verify_signature.event}} === 'refund.initiated'" } } ] }, "displayName": "Route by Event Type" } ] };

  1. Scheduled Flows

// Scheduled flow with cron expression const scheduledFlow = { "displayName": "Daily Sales Report", "schedule": { "cronExpression": "0 9 * * 1-5", // 9 AM Mon-Fri "timezone": "America/New_York" }, "trigger": { "name": "schedule", "type": "SCHEDULE", "settings": {}, "displayName": "Daily Schedule" }, "steps": [ { "name": "get_date_range", "type": "CODE", "settings": { "sourceCode": { "code": ` export const code = async () => { const now = new Date(); const yesterday = new Date(now); yesterday.setDate(yesterday.getDate() - 1);

return { start_date: yesterday.toISOString().split('T')[0], end_date: now.toISOString().split('T')[0], report_date: now.toISOString() }; }; } }, "displayName": "Calculate Date Range" }, { "name": "fetch_sales_data", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-http", "pieceVersion": "~0.4.0", "actionName": "send_request", "input": { "method": "GET", "url": "{{connections.sales_api.base_url}}/api/v1/sales", "headers": { "Authorization": "Bearer {{connections.sales_api.api_key}}" }, "queryParams": { "start_date": "{{get_date_range.start_date}}", "end_date": "{{get_date_range.end_date}}" } } }, "displayName": "Fetch Sales Data" }, { "name": "generate_report", "type": "CODE", "settings": { "input": { "sales": "{{fetch_sales_data.body.data}}", "report_date": "{{get_date_range.report_date}}" }, "sourceCode": { "code": export const code = async (inputs) => { const { sales, report_date } = inputs;

const totalRevenue = sales.reduce((sum, sale) => sum + sale.amount, 0); const totalOrders = sales.length; const avgOrderValue = totalOrders > 0 ? totalRevenue / totalOrders : 0;

const byCategory = sales.reduce((acc, sale) => { const cat = sale.category || 'Other'; acc[cat] = (acc[cat] || 0) + sale.amount; return acc; }, {});

return { report_date, summary: { total_revenue: totalRevenue.toFixed(2), total_orders: totalOrders, avg_order_value: avgOrderValue.toFixed(2) }, by_category: Object.entries(byCategory) .map(([category, amount]) => ({ category, amount })) .sort((a, b) => b.amount - a.amount), top_products: sales .sort((a, b) => b.amount - a.amount) .slice(0, 5) }; };` } }, "displayName": "Generate Report Summary" }, { "name": "send_report", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-gmail", "pieceVersion": "~0.5.0", "actionName": "send_email", "input": { "to": ["sales-team@example.com", "management@example.com"], "subject": "Daily Sales Report - {{get_date_range.start_date}}", "body": "<h1>Daily Sales Report</h1><h2>Summary</h2><ul><li>Total Revenue: ${{generate_report.summary.total_revenue}}</li><li>Total Orders: {{generate_report.summary.total_orders}}</li><li>Average Order Value: ${{generate_report.summary.avg_order_value}}</li></ul>" } }, "displayName": "Send Report Email" } ] };

  1. Branching and Conditional Logic

// Branch step with multiple conditions const branchingFlow = { "displayName": "Lead Qualification Flow", "trigger": { "name": "webhook", "type": "WEBHOOK", "settings": {}, "displayName": "New Lead" }, "steps": [ { "name": "enrich_lead", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-clearbit", "pieceVersion": "~0.2.0", "actionName": "enrich_company", "input": { "domain": "{{trigger.body.company_domain}}" } }, "displayName": "Enrich Lead Data" }, { "name": "calculate_score", "type": "CODE", "settings": { "input": { "lead": "{{trigger.body}}", "enriched": "{{enrich_lead}}" }, "sourceCode": { "code": ` export const code = async (inputs) => { const { lead, enriched } = inputs; let score = 0;

// Company size scoring if (enriched.metrics?.employees > 1000) score += 30; else if (enriched.metrics?.employees > 100) score += 20; else if (enriched.metrics?.employees > 10) score += 10;

// Industry scoring const highValueIndustries = ['technology', 'finance', 'healthcare']; if (highValueIndustries.includes(enriched.category?.industry?.toLowerCase())) { score += 25; }

// Title scoring const seniorTitles = ['ceo', 'cto', 'vp', 'director', 'head']; if (seniorTitles.some(t => lead.title?.toLowerCase().includes(t))) { score += 20; }

// Budget scoring if (lead.budget > 100000) score += 25; else if (lead.budget > 50000) score += 15; else if (lead.budget > 10000) score += 10;

return { score, tier: score >= 70 ? 'hot' : score >= 40 ? 'warm' : 'cold', lead: { ...lead, enriched } }; };` } }, "displayName": "Calculate Lead Score" }, { "name": "route_by_tier", "type": "BRANCH", "settings": { "conditions": [ { "name": "hot_lead", "expression": { "type": "EXPRESSION", "value": "{{calculate_score.tier}} === 'hot'" }, "steps": [ { "name": "notify_sales_urgent", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-slack", "actionName": "send_message", "input": { "channel": "#hot-leads", "text": "HOT LEAD: {{calculate_score.lead.name}} from {{calculate_score.lead.company}} (Score: {{calculate_score.score}})" } } }, { "name": "create_salesforce_lead", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-salesforce", "actionName": "create_record", "input": { "objectName": "Lead", "fields": { "FirstName": "{{calculate_score.lead.first_name}}", "LastName": "{{calculate_score.lead.last_name}}", "Company": "{{calculate_score.lead.company}}", "Email": "{{calculate_score.lead.email}}", "LeadSource": "Website", "Rating": "Hot" } } } }, { "name": "schedule_demo", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-calendly", "actionName": "create_scheduling_link", "input": { "event_type": "sales-demo", "invitee_email": "{{calculate_score.lead.email}}" } } } ] }, { "name": "warm_lead", "expression": { "type": "EXPRESSION", "value": "{{calculate_score.tier}} === 'warm'" }, "steps": [ { "name": "add_to_nurture", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-mailchimp", "actionName": "add_subscriber", "input": { "list_id": "{{connections.mailchimp.nurture_list_id}}", "email": "{{calculate_score.lead.email}}", "tags": ["warm-lead", "nurture-sequence"] } } } ] }, { "name": "cold_lead", "expression": { "type": "EXPRESSION", "value": "{{calculate_score.tier}} === 'cold'" }, "steps": [ { "name": "add_to_drip", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-mailchimp", "actionName": "add_subscriber", "input": { "list_id": "{{connections.mailchimp.general_list_id}}", "email": "{{calculate_score.lead.email}}", "tags": ["cold-lead"] } } } ] } ] }, "displayName": "Route by Lead Tier" } ] };

  1. Loop and Iteration

// Loop over items const loopFlow = { "displayName": "Batch Order Processing", "trigger": { "name": "webhook", "type": "WEBHOOK", "settings": {}, "displayName": "Batch Orders" }, "steps": [ { "name": "process_orders", "type": "LOOP_ON_ITEMS", "settings": { "items": "{{trigger.body.orders}}", "steps": [ { "name": "validate_order", "type": "CODE", "settings": { "input": { "order": "{{loop.item}}" }, "sourceCode": { "code": ` export const code = async (inputs) => { const { order } = inputs;

const errors = [];

if (!order.customer_id) errors.push('Missing customer_id'); if (!order.items || order.items.length === 0) errors.push('No items in order'); if (!order.total || order.total <= 0) errors.push('Invalid total');

return { valid: errors.length === 0, errors, order: errors.length === 0 ? order : null }; }; } }, "displayName": "Validate Order" }, { "name": "check_inventory", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-http", "actionName": "send_request", "input": { "method": "POST", "url": "{{connections.inventory_api.base_url}}/api/check-availability", "headers": { "Authorization": "Bearer {{connections.inventory_api.api_key}}" }, "body": { "items": "{{validate_order.order.items}}" } } }, "displayName": "Check Inventory" }, { "name": "process_payment", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-stripe", "actionName": "create_charge", "input": { "amount": "{{validate_order.order.total}}", "currency": "usd", "customer": "{{validate_order.order.stripe_customer_id}}", "description": "Order {{validate_order.order.id}}" } }, "displayName": "Process Payment" }, { "name": "create_shipment", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-shippo", "actionName": "create_shipment", "input": { "address_from": "{{connections.shippo.warehouse_address}}", "address_to": "{{validate_order.order.shipping_address}}", "parcels": "{{validate_order.order.items}}" } }, "displayName": "Create Shipment" } ] }, "displayName": "Process Each Order" }, { "name": "summarize_results", "type": "CODE", "settings": { "input": { "results": "{{process_orders}}" }, "sourceCode": { "code": export const code = async (inputs) => { const { results } = inputs;

const successful = results.filter(r => r.success); const failed = results.filter(r => !r.success);

return { total_processed: results.length, successful: successful.length, failed: failed.length, failed_orders: failed.map(f => ({ order_id: f.order_id, error: f.error })), total_revenue: successful.reduce((sum, r) => sum + r.amount, 0) }; };` } }, "displayName": "Summarize Results" } ] };

  1. Custom Piece Development

// pieces/my-custom-api/src/index.ts import { createPiece, PieceAuth } from '@activepieces/pieces-framework'; import { createCustomResource } from './lib/actions/create-resource'; import { getCustomResource } from './lib/actions/get-resource'; import { listCustomResources } from './lib/actions/list-resources'; import { resourceCreatedTrigger } from './lib/triggers/resource-created';

const authDescription = ` To get your API key:

  1. Go to Settings > API Keys in your dashboard
  2. Click "Create New Key"
  3. Copy the generated key `;

export const customApiAuth = PieceAuth.SecretText({ displayName: 'API Key', required: true, description: authDescription, validate: async ({ auth }) => { // Validate the API key const response = await fetch('https://api.example.com/validate', { headers: { Authorization: Bearer ${auth} } });

if (!response.ok) {
  return {
    valid: false,
    error: 'Invalid API key'
  };
}

return { valid: true };

} });

export const myCustomApi = createPiece({ displayName: 'My Custom API', auth: customApiAuth, minimumSupportedRelease: '0.20.0', logoUrl: 'https://example.com/logo.png', authors: ['your-name'], description: 'Connect to My Custom API for resource management', actions: [createCustomResource, getCustomResource, listCustomResources], triggers: [resourceCreatedTrigger] });

// pieces/my-custom-api/src/lib/actions/create-resource.ts import { createAction, Property } from '@activepieces/pieces-framework'; import { customApiAuth } from '../../index';

export const createCustomResource = createAction({ name: 'create_resource', displayName: 'Create Resource', description: 'Create a new resource in My Custom API', auth: customApiAuth, props: { name: Property.ShortText({ displayName: 'Resource Name', required: true, description: 'The name of the resource' }), type: Property.Dropdown({ displayName: 'Resource Type', required: true, refreshers: [], options: async () => ({ options: [ { label: 'Document', value: 'document' }, { label: 'Image', value: 'image' }, { label: 'Video', value: 'video' } ] }) }), tags: Property.Array({ displayName: 'Tags', required: false, description: 'Tags to categorize the resource' }), metadata: Property.Object({ displayName: 'Metadata', required: false, description: 'Additional metadata for the resource' }), content: Property.LongText({ displayName: 'Content', required: true, description: 'The content of the resource' }) }, async run(context) { const { auth, propsValue } = context;

const response = await fetch('https://api.example.com/resources', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${auth}`
  },
  body: JSON.stringify({
    name: propsValue.name,
    type: propsValue.type,
    tags: propsValue.tags || [],
    metadata: propsValue.metadata || {},
    content: propsValue.content
  })
});

if (!response.ok) {
  const error = await response.text();
  throw new Error(`Failed to create resource: ${error}`);
}

const result = await response.json();

return {
  id: result.id,
  name: result.name,
  type: result.type,
  created_at: result.created_at,
  url: result.url
};

} });

// pieces/my-custom-api/src/lib/triggers/resource-created.ts import { createTrigger, TriggerStrategy, Property } from '@activepieces/pieces-framework'; import { customApiAuth } from '../../index';

export const resourceCreatedTrigger = createTrigger({ name: 'resource_created', displayName: 'Resource Created', description: 'Triggers when a new resource is created', auth: customApiAuth, props: { resourceType: Property.Dropdown({ displayName: 'Resource Type', required: false, refreshers: [], options: async () => ({ options: [ { label: 'All Types', value: 'all' }, { label: 'Document', value: 'document' }, { label: 'Image', value: 'image' }, { label: 'Video', value: 'video' } ] }) }) }, type: TriggerStrategy.WEBHOOK, async onEnable(context) { const { auth, propsValue, webhookUrl, store } = context;

// Register webhook with the external API
const response = await fetch('https://api.example.com/webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${auth}`
  },
  body: JSON.stringify({
    url: webhookUrl,
    events: ['resource.created'],
    filter: propsValue.resourceType !== 'all'
      ? { type: propsValue.resourceType }
      : undefined
  })
});

const webhook = await response.json();

// Store webhook ID for cleanup
await store.put('webhookId', webhook.id);

}, async onDisable(context) { const { auth, store } = context;

const webhookId = await store.get('webhookId');
if (webhookId) {
  await fetch(`https://api.example.com/webhooks/${webhookId}`, {
    method: 'DELETE',
    headers: {
      Authorization: `Bearer ${auth}`
    }
  });
}

}, async run(context) { const { payload } = context;

return [
  {
    id: payload.body.data.id,
    name: payload.body.data.name,
    type: payload.body.data.type,
    created_at: payload.body.data.created_at,
    created_by: payload.body.data.created_by
  }
];

}, sampleData: { id: 'res_123456', name: 'Sample Resource', type: 'document', created_at: '2026-01-17T10:00:00Z', created_by: 'user_789' } });

  1. Approval Flows

// Human approval workflow const approvalFlow = { "displayName": "Expense Approval Workflow", "trigger": { "name": "webhook", "type": "WEBHOOK", "settings": {}, "displayName": "New Expense Request" }, "steps": [ { "name": "validate_expense", "type": "CODE", "settings": { "input": { "expense": "{{trigger.body}}" }, "sourceCode": { "code": ` export const code = async (inputs) => { const { expense } = inputs;

return { ...expense, requires_approval: expense.amount > 500, approval_level: expense.amount > 5000 ? 'executive' : expense.amount > 500 ? 'manager' : 'auto', formatted_amount: new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(expense.amount) }; };` } }, "displayName": "Validate Expense" }, { "name": "check_approval_required", "type": "BRANCH", "settings": { "conditions": [ { "name": "needs_approval", "expression": { "type": "EXPRESSION", "value": "{{validate_expense.requires_approval}} === true" }, "steps": [ { "name": "send_approval_request", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-slack", "actionName": "send_message", "input": { "channel": "#expense-approvals", "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "Expense Approval Required" } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "Submitted by:\n{{validate_expense.employee_name}}" }, { "type": "mrkdwn", "text": "Amount:\n{{validate_expense.formatted_amount}}" }, { "type": "mrkdwn", "text": "Category:\n{{validate_expense.category}}" }, { "type": "mrkdwn", "text": "Description:\n{{validate_expense.description}}" } ] }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "Approve" }, "style": "primary", "action_id": "approve_expense", "value": "{{validate_expense.id}}" }, { "type": "button", "text": { "type": "plain_text", "text": "Reject" }, "style": "danger", "action_id": "reject_expense", "value": "{{validate_expense.id}}" } ] } ] } } }, { "name": "wait_for_approval", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-approval", "actionName": "wait_for_approval", "input": { "timeout_hours": 48, "approval_link_text": "Click to review expense" } } }, { "name": "process_decision", "type": "BRANCH", "settings": { "conditions": [ { "name": "approved", "expression": { "type": "EXPRESSION", "value": "{{wait_for_approval.status}} === 'APPROVED'" }, "steps": [ { "name": "process_reimbursement", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-http", "actionName": "send_request", "input": { "method": "POST", "url": "{{connections.finance_api.base_url}}/reimbursements", "body": { "expense_id": "{{validate_expense.id}}", "amount": "{{validate_expense.amount}}", "employee_id": "{{validate_expense.employee_id}}", "approved_by": "{{wait_for_approval.approved_by}}" } } } } ] }, { "name": "rejected", "expression": { "type": "EXPRESSION", "value": "{{wait_for_approval.status}} === 'REJECTED'" }, "steps": [ { "name": "notify_rejection", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-gmail", "actionName": "send_email", "input": { "to": "{{validate_expense.employee_email}}", "subject": "Expense Request Rejected", "body": "Your expense request for {{validate_expense.formatted_amount}} has been rejected." } } } ] } ] } } ] }, { "name": "auto_approve", "expression": { "type": "EXPRESSION", "value": "{{validate_expense.requires_approval}} === false" }, "steps": [ { "name": "auto_process", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-http", "actionName": "send_request", "input": { "method": "POST", "url": "{{connections.finance_api.base_url}}/reimbursements", "body": { "expense_id": "{{validate_expense.id}}", "amount": "{{validate_expense.amount}}", "auto_approved": true } } } } ] } ] }, "displayName": "Check Approval Required" } ] };

  1. Error Handling and Retry Logic

// Error handling patterns const errorHandlingFlow = { "displayName": "Resilient API Integration", "trigger": { "name": "schedule", "type": "SCHEDULE", "settings": { "cronExpression": "*/15 * * * *" }, "displayName": "Every 15 Minutes" }, "steps": [ { "name": "fetch_with_retry", "type": "CODE", "settings": { "input": { "api_url": "{{connections.external_api.base_url}}/data", "api_key": "{{connections.external_api.api_key}}" }, "sourceCode": { "code": ` export const code = async (inputs) => { const { api_url, api_key } = inputs; const maxRetries = 3; const baseDelay = 1000;

for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const response = await fetch(api_url, { headers: { Authorization: `Bearer ${api_key}` }, signal: AbortSignal.timeout(30000) // 30 second timeout });

  if (response.status === 429) {
    // Rate limited - wait and retry
    const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
    if (attempt &#x3C; maxRetries) {
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      continue;
    }
  }

  if (!response.ok) {
    throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
  }

  const data = await response.json();
  return {
    success: true,
    data,
    attempts: attempt
  };

} catch (error) {
  if (attempt === maxRetries) {
    return {
      success: false,
      error: error.message,
      attempts: attempt
    };
  }

  // Exponential backoff
  const delay = baseDelay * Math.pow(2, attempt - 1);
  await new Promise(r => setTimeout(r, delay));
}

} }; } }, "displayName": "Fetch with Retry" }, { "name": "handle_result", "type": "BRANCH", "settings": { "conditions": [ { "name": "success", "expression": { "type": "EXPRESSION", "value": "{{fetch_with_retry.success}} === true" }, "steps": [ { "name": "process_data", "type": "CODE", "settings": { "input": { "data": "{{fetch_with_retry.data}}" }, "sourceCode": { "code": export const code = async (inputs) => { const { data } = inputs; // Process successful data return { processed: true, count: data.length, timestamp: new Date().toISOString() }; };` } } } ] }, { "name": "failure", "expression": { "type": "EXPRESSION", "value": "{{fetch_with_retry.success}} === false" }, "steps": [ { "name": "alert_failure", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-slack", "actionName": "send_message", "input": { "channel": "#alerts", "text": "API Integration Failed after {{fetch_with_retry.attempts}} attempts. Error: {{fetch_with_retry.error}}" } } }, { "name": "log_failure", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-http", "actionName": "send_request", "input": { "method": "POST", "url": "{{connections.logging_api.base_url}}/errors", "body": { "flow": "Resilient API Integration", "error": "{{fetch_with_retry.error}}", "attempts": "{{fetch_with_retry.attempts}}", "timestamp": "{{now}}" } } } } ] } ] }, "displayName": "Handle Result" } ] };

Integration Examples

Integration with Notion and Slack

const notionSlackSync = { "displayName": "Notion to Slack Sync", "trigger": { "name": "notion_database_updated", "type": "PIECE_TRIGGER", "settings": { "pieceName": "@activepieces/piece-notion", "triggerName": "database_item_updated", "input": { "database_id": "{{connections.notion.task_database_id}}" } }, "displayName": "Task Updated in Notion" }, "steps": [ { "name": "check_status_change", "type": "CODE", "settings": { "input": { "item": "{{trigger}}" }, "sourceCode": { "code": ` export const code = async (inputs) => { const { item } = inputs;

const statusProperty = item.properties?.Status; const currentStatus = statusProperty?.select?.name;

return { task_name: item.properties?.Name?.title?.[0]?.plain_text || 'Unnamed Task', status: currentStatus, assignee: item.properties?.Assignee?.people?.[0]?.name || 'Unassigned', due_date: item.properties?.['Due Date']?.date?.start, url: item.url, is_completed: currentStatus === 'Done', is_blocked: currentStatus === 'Blocked' }; };` } }, "displayName": "Extract Task Details" }, { "name": "route_notification", "type": "BRANCH", "settings": { "conditions": [ { "name": "task_completed", "expression": { "type": "EXPRESSION", "value": "{{check_status_change.is_completed}} === true" }, "steps": [ { "name": "celebrate_completion", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-slack", "actionName": "send_message", "input": { "channel": "#team-wins", "text": "Task completed! {{check_status_change.task_name}} by {{check_status_change.assignee}}" } } } ] }, { "name": "task_blocked", "expression": { "type": "EXPRESSION", "value": "{{check_status_change.is_blocked}} === true" }, "steps": [ { "name": "alert_blockers", "type": "PIECE", "settings": { "pieceName": "@activepieces/piece-slack", "actionName": "send_message", "input": { "channel": "#blockers", "text": "Task blocked: {{check_status_change.task_name}} - Assigned to {{check_status_change.assignee}}" } } } ] } ] }, "displayName": "Route Notification" } ] };

Best Practices

  1. Flow Organization

flows/ ├── onboarding/ │ ├── customer-onboarding.json │ └── employee-onboarding.json ├── integrations/ │ ├── crm-sync.json │ ├── inventory-sync.json │ └── payment-webhooks.json ├── notifications/ │ ├── alert-routing.json │ └── daily-reports.json └── utilities/ ├── data-validation.json └── error-handler.json

  1. Error Handling Best Practices

// Always wrap external calls in try-catch try { const result = await externalApiCall(); return { success: true, data: result }; } catch (error) { // Return structured error for downstream handling return { success: false, error: error.message, retryable: !error.message.includes('PERMANENT'), timestamp: new Date().toISOString() }; }

  1. Security Best Practices

Environment variables

AP_ENCRYPTION_KEY: "32-character-secure-key" AP_JWT_SECRET: "secure-jwt-secret" AP_WEBHOOK_TIMEOUT_SECONDS: 30

Use connections for all credentials

Never hardcode API keys in flow definitions

  1. Performance Tips

// Batch operations when possible const BATCH_SIZE = 50; const items = inputs.items;

for (let i = 0; i < items.length; i += BATCH_SIZE) { const batch = items.slice(i, i + BATCH_SIZE); await processBatch(batch); }

// Use appropriate timeouts const controller = new AbortController(); setTimeout(() => controller.abort(), 30000);

Troubleshooting

Common Issues

Issue: Flow not triggering

Check webhook URL is accessible

curl -X POST https://activepieces.example.com/api/v1/webhooks/your-flow-id
-H "Content-Type: application/json"
-d '{"test": true}'

Verify flow is enabled

Check logs in Activepieces UI

Issue: Connection authentication failing

Test credentials manually

curl -X GET "https://api.service.com/test"
-H "Authorization: Bearer YOUR_TOKEN"

Re-authenticate connection in UI

Issue: Code step timeout

Increase sandbox timeout

AP_SANDBOX_RUN_TIME_SECONDS: 600

Debugging Tips

// Add logging in code steps console.log('Input:', JSON.stringify(inputs, null, 2)); console.log('Processing step...');

// Return debug info return { result: processedData, debug: { input_count: inputs.items.length, processing_time_ms: Date.now() - startTime } };

Version History

Version Date Changes

1.0.0 2026-01-17 Initial release with comprehensive flow patterns

Resources

  • Activepieces Documentation

  • Pieces Directory

  • GitHub Repository

  • Community Discord

  • Custom Piece Development

This skill provides production-ready patterns for Activepieces workflow automation, tested across enterprise integration scenarios.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

git-worktree-workflow

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

agent-os-framework

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

yaml-workflow-executor

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

agenta

No summary provided by upstream source.

Repository SourceNeeds Review