Approval Workflows

Set up multi-party approval workflows with human-in-the-loop decision making

Approval Workflows

Need a human to approve something before your workflow continues? The Approval Node pauses your workflow, asks for approval, and then continues based on the decision.

Status: Production-Ready ✅ Version: 1.0 Last Updated: November 2025

How It Works

Approval workflows have two paths:

Your Workflow
    ↓
┌─────────────────────┐
│  Approval Node      │
│  (pause execution)  │
└─────────────────────┘
    ↙              ↘
✅ Approved      ❌ Rejected
    ↓                 ↓
Continue Path    Reject Path

When the Approval Node runs:

  1. It pauses the workflow
  2. It sends a notification (email, Slack, UI)
  3. An approver responds (approve or reject)
  4. The workflow resumes on the appropriate path

Tip: Perfect for expense approvals, content review, deployment gates, or any process that needs human sign-off.

Quick Example: Expense Approval

Let's say you want to approve expenses over $1000:

Workflow:

  1. User submits expense form (trigger)
  2. Check if amount > $1000
  3. If yes → Approval Node (pause and wait)
  4. Manager approves → Send confirmation email
  5. Manager rejects → Send rejection notice

Approval Node Configuration:

prompt: "Approve expense of ${{ $trigger.amount }} for {{ $trigger.description }}?"
approverId: "{{ $trigger.managerId }}"
requireComment: true
expiresIn: 86400  # 24 hours

Approval Strategies

When you have multiple approvers, how do you make the final decision?

1. ANY — First One Wins

Behavior: First person to approve OR reject decides.

Example:

approvers: ["alice@company.com", "bob@company.com", "charlie@company.com"]
approvalStrategy: "any"
  • Alice approves → Approved (Bob and Charlie don't need to respond)
  • OR Alice rejects → Rejected (can't override)

Use when: You need quick decisions and don't need consensus.

2. ALL — Everyone Must Agree

Behavior: All approvers must approve. One rejection = rejected.

Example:

approvers: ["alice@company.com", "bob@company.com"]
approvalStrategy: "all"
  • Alice approves, Bob approves → Approved
  • Alice approves, Bob rejects → Rejected

Use when: Security-critical or all perspectives matter (e.g., security + compliance)

3. MAJORITY — More Than Half

Behavior: More than 50% must approve.

Example:

approvers: ["alice@company.com", "bob@company.com", "charlie@company.com"]
approvalStrategy: "majority"
  • 2 approve, 1 rejects → Approved (2/3 > 50%)
  • 1 approves, 2 reject → Rejected

Use when: You want input from multiple people but don't need unanimity.

4. WEIGHTED — Custom Weights

Behavior: Each approver has a weight. If total weight meets threshold, approved.

Example:

approvers: ["alice@company.com", "bob@company.com", "charlie@company.com"]
approverWeights:
  alice@company.com: 3.0    # CEO
  bob@company.com: 2.0      # CFO
  charlie@company.com: 1.0  # Manager
approvalStrategy: "weighted"
requiredWeightThreshold: 3.0
  • Alice (CEO, weight=3.0) approves → Approved (3.0 ≥ 3.0)
  • Bob (CFO, weight=2.0) + Charlie (Manager, weight=1.0) approve → Approved (3.0 ≥ 3.0)
  • Only Charlie approves → Rejected (1.0 < 3.0)

Use when: Approvers have different authority levels.

Configuration

Minimal Setup

prompt: "Approve this request?"
approverId: "user@company.com"

That's enough to get started!

Full Configuration

# Basic
prompt: "Approve expense of ${{ $trigger.amount }}?"
approverId: "{{ $workflow.settings.managerId }}"

# Notifications
approvalType: "email"  # email, slack, teams, webhook, or ui
requireComment: true

# Timeout
timeoutMinutes: 1440  # 24 hours

# Multi-party approvals
approvers: ["alice@company.com", "bob@company.com"]
approvalStrategy: "majority"  # any, all, majority, weighted

# Data to show approver
data:
  amount: "{{ $trigger.amount }}"
  description: "{{ $trigger.description }}"
  requester: "{{ $node['Get User'].json.name }}"

Real-World Examples

Example 1: Expense Approval

Approval Node:
  prompt: "Approve ${{ $trigger.amount }} expense?"
  approverId: "{{ $trigger.managerId }}"
  requireComment: true
  timeoutMinutes: 1440

Connections:
  approved  Send Approval Email
  rejected  Send Rejection Email

Example 2: Content Review

Approval Node:
  prompt: "Review and approve blog post: {{ $node['Draft'].json.title }}"
  approvers:
    - "editor@company.com"
    - "manager@company.com"
  approvalStrategy: "all"  # Both must approve
  data:
    content: "{{ $node['Draft'].json.content }}"

Example 3: Deployment Gate

Approval Node:
  prompt: "Approve deployment to {{ $trigger.environment }}?"
  approvers:
    - "devops-lead@company.com"
    - "security-lead@company.com"
  approvalStrategy: "weighted"
  approverWeights:
    devops-lead@company.com: 2
    security-lead@company.com: 2
  requiredWeightThreshold: 3  # Both must approve

Output Ports

The Approval Node has two output ports:

  • approved — Triggers when the request is approved
  • rejected — Triggers when the request is rejected

Connect different workflows to each port:

Approval Node
  ├─→ approved port  Send Success Email  Update Database
  └─→ rejected port  Send Rejection Email  Log Request

Common Mistakes & How to Avoid Them

Mistake 1: Hardcoding approver IDs

❌ Wrong:

approverId: "alice@company.com"  # Always asks Alice

✅ Correct:

approverId: "{{ $trigger.managerId }}"  # Dynamic based on trigger

Mistake 2: Forgetting expiration

❌ Wrong:

# No timeout - approval request never expires!

✅ Correct:

timeoutMinutes: 1440  # 24 hours

Mistake 3: Wrong strategy for the use case

❌ Wrong:

# Security approval needs both security AND compliance
approvalStrategy: "any"  # Oops, only one person needs to approve

✅ Correct:

approvalStrategy: "all"  # Both must approve

Best Practices

  • ✅ Use expressions for dynamic approvers — Assign approval to the right person based on data
  • ✅ Always set timeout — Prevent approvals from hanging forever
  • ✅ Include context in data — Show approvers what they're reviewing
  • ✅ Use weighted for hierarchies — CEO's approval might be more important
  • ✅ Test approval flow — Verify the approved and rejected paths work
  • ❌ Don't hardcode approvers — Use expressions for flexibility
  • ❌ Don't skip requireComment — Comments help with audit trails
  • ❌ Don't use "any" for critical approvals — Use "all" or "weighted" instead

Next Steps

  • Build your first workflow — Add an approval node to a simple workflow
  • Test approval paths — Verify both approved and rejected routes
  • Set up notifications — Configure email or Slack to notify approvers
  • Use multi-party approval — Try "majority" or "weighted" strategies
  • Integrate with your data — Use expressions to pass context to approvers

Related: