Approval FormID: 11035
Suspend a workflow and route execution based on a structured human approval decision. Supports three voting strategies, configurable deadlines with auto-escalation, and six output ports for precise downstream routing.
When to Use
- Purchase order approval: Any PO above $10,000 must be approved by the CFO before the ERP is updated. The Approval node suspends the workflow, notifies the CFO in their inbox with the full PO context, and resumes on the approved or rejected port based on their decision.
- Employee leave request approval: An employee's leave request must be approved by their direct manager before calendar blocking and payroll adjustments are made. The manager sees the dates, remaining balance, and reason in their inbox.
- Contract sign-off by multiple stakeholders: A new vendor contract must be approved by both Legal and Finance before external signature. Using the
Anonymousstrategy, both parties must approve before the downstream e-signature step fires. - Expense claim over policy threshold: Claims above the employee's self-approval limit require finance team review. The Approval node routes to the finance role — any finance team member can approve or reject.
- IT change advisory board (CAB) vote: An infrastructure change requires N of M CAB members to approve before automated deployment proceeds, using the
NofMstrategy with a configurable required count.
CRITICAL — Workflow suspension: The Approval node suspends workflow execution at the
waiting port. The workflow remains suspended until a decision is made or the deadline expires. Always configure deadlineSeconds and autoOnTimeout to prevent workflows from hanging indefinitely.
Two-Phase Execution
Phase 1 — Suspend
Request Creation
- Creates an EngageSession for this execution
- Creates an InboxItem for each actor in the
actorsarray - Sends notification to each actor (in-app, email, push)
- Workflow execution suspends and yields CPU
- waiting port fires immediately
- Deadline countdown begins if configured
Phase 2 — Resume
Decision Processing
- Actor opens inbox and reviews approval request
- Actor submits decision: approve, reject, escalate, or abstain
- BizFirst engine resumes the suspended workflow
- Decision data (actor, time, comments) injected into output
- Execution continues on the matching output port
Configuration
| Setting | Required | Description |
|---|---|---|
title | Optional | The heading shown to approvers in their inbox. Default: "Approval Required". Supports BizFirst expressions. |
description | Optional | Supporting text explaining what is being approved and any context needed for the decision. Supports BizFirst expressions. Rendered as formatted text in the inbox. |
requireReason | Optional | Boolean, default false. When true, approvers must enter a text reason/comment before submitting any decision. Enforced for compliance workflows (SOX, ISO 27001). |
allowAbstain | Optional | Boolean, default false. When true, approvers may choose "Abstain" as a valid decision. Useful for committee-style approvals where abstention is a distinct recorded outcome. |
strategy | Optional | Voting strategy. One of: "Anonymous" (default — all actors must approve), "AnyOne" (first actor to decide determines the outcome), "NofM" (N of M actors must approve, where N is set by requiredCount). |
requiredCount | Optional | Integer. Required when strategy is "NofM". Minimum value: 1. Must be less than or equal to the total number of actors. Example: 2 for a 2-of-3 approval requirement. |
deadlineSeconds | Optional | Integer. Default 0 (no deadline). When positive, the workflow automatically takes the autoOnTimeout action if no decision is reached within this many seconds. Example: 86400 (24 hours). |
autoOnTimeout | Optional | Action when deadline expires. One of: "rejected" (routes to rejected port), "approved" (routes to approved port), "escalated" (routes to escalated port and notifies escalateToUserId). |
escalateToUserId | Optional | String user ID. Required when autoOnTimeout is "escalated". This user receives an escalation notification when the deadline expires. |
actors | Required | JSON array of actor objects. At least one actor is required. Each actor: { "id": "user_or_role_id", "actorType": "User"|"Role", "label": "display name", "correlationField": "optional" }. Role actors expand to all current members of that role. |
watchers | Optional | JSON array of watcher objects (same format as actors). Watchers receive a read-only notification of the approval request and all decisions but cannot vote. Useful for audit trail participants like compliance officers. |
Approval Strategy Comparison
| Strategy | How It Works | When to Use | Example |
|---|---|---|---|
Anonymous |
All actors in the list must approve. A single rejection from any actor immediately rejects the request. Abstentions do not count as approvals. | High-value or regulated decisions requiring unanimous consent. Compliance sign-offs, contract execution. | Both Legal AND Finance must approve before a contract is sent for signature. |
AnyOne |
The first actor to submit any decision determines the outcome. Other actors' pending requests are automatically cancelled once a decision is made. | Routing to a role where any team member can handle the request. Support team approvals, first-available manager confirmation. | Any member of the Finance team can approve an expense claim — assigned to role:finance. |
NofM |
N specific actors out of M total actors must approve. The workflow waits until N approvals or any rejection that makes N impossible to reach. | Committee-style voting. Change Advisory Boards, multi-signer documents, quorum-required decisions. | 3 of 5 board members must approve a strategic investment decision. |
Output Ports
| Port | When It Fires |
|---|---|
| waiting | Phase 1 — fires immediately when the approval request is created and the workflow suspends. Use this to notify stakeholders that a decision is pending. |
| approved | The voting strategy's approval threshold is met. Route to the "proceed" branch: update systems, notify requesters, continue the process. |
| rejected | An actor rejects the request, or the strategy determines rejection is inevitable (e.g., in NofM, enough rejections make approval impossible). Route to rejection handling: notifications, alternative paths, requester feedback. |
| escalated | An actor explicitly escalates the request, or autoOnTimeout is "escalated" and the deadline expires. Route to a higher-authority approval node or escalation notification. |
| abstained | An actor abstains (only available when allowAbstain is true). In committee approvals, abstention is a distinct recorded outcome — route accordingly. |
| error | Internal error: an actor ID cannot be resolved, a required configuration is invalid, or a critical system failure occurs. Route to error notification and alerting. |
Actor Types
| Actor Type | Behaviour | When to Use |
|---|---|---|
User |
Creates an InboxItem for a single, specific user identified by their user ID. That user alone can make the decision. | Named individual approvers: the CFO, a specific manager, a named compliance officer. |
Role |
Expands to all current members of the specified role. Each member receives an InboxItem. With AnyOne strategy, the first member to respond decides; with Anonymous, all must respond. |
Team-based approvals where any qualified team member can decide. Finance team, Legal team, IT support. |
Never use System actor type for real approvals: The System actor type is reserved for automated testing and synthetic workflow simulations. Using it in a production approval workflow bypasses the human-in-the-loop requirement entirely and invalidates the approval audit trail.
Output Fields (on approved, rejected, escalated, abstained ports)
| Field | Type | Description |
|---|---|---|
decision | string | The decision outcome: "approved", "rejected", "escalated", or "abstained". |
decided_by | string | User ID of the actor who made the final decision. |
decided_at | string | ISO 8601 timestamp of when the decision was submitted. |
comments | string | Optional free-text comments provided by the approver. Empty string if no comment was provided or if requireReason was false. |
all_decisions | array | For multi-actor approvals, an array of all individual decisions recorded, each with actorId, decision, comments, and timestamp. |
Sample Configuration
{
"nodeType": "approval",
"formId": 11035,
"settings": {
"title": "Purchase Order Approval — {{ $output.poNode.poNumber }}",
"description": "PO for {{ $output.poNode.vendorName }} totalling {{ $output.poNode.totalAmount | currency }}.\nRaised by: {{ $output.poNode.requestedBy }}\nCost centre: {{ $output.poNode.costCentre }}\nBusiness justification: {{ $output.poNode.justification }}",
"requireReason": true,
"allowAbstain": false,
"strategy": "AnyOne",
"deadlineSeconds": 86400,
"autoOnTimeout": "escalated",
"escalateToUserId": "user:cfo-deputy",
"actors": [
{ "id": "role:finance-director", "actorType": "Role", "label": "Finance Director" }
],
"watchers": [
{ "id": "user:procurement-manager", "actorType": "User", "label": "Procurement Manager" }
]
}
}
Sample Output
Success Port — Approved
{
"decision": "approved",
"approvedBy": [
{
"actorId": "usr_abc123",
"actorName": "Jane Smith",
"decidedAt": "2025-03-15T14:32:00Z",
"reason": "Budget within quarterly limit. Approved.",
"vote": "approved"
}
],
"rejectedBy": [],
"abstainedBy": [],
"totalActors": 1,
"requiredCount": 1,
"outcome": "approved",
"completedAt": "2025-03-15T14:32:00Z",
"timeoutAt": null,
"sessionId": "approval_session_xyz789"
}
Rejected Port
{
"decision": "rejected",
"rejectedBy": [
{
"actorId": "usr_def456",
"actorName": "Bob Johnson",
"decidedAt": "2025-03-15T15:10:00Z",
"reason": "Exceeds authority limit. Please escalate to VP Finance.",
"vote": "rejected"
}
],
"approvedBy": [],
"outcome": "rejected",
"completedAt": "2025-03-15T15:10:00Z"
}
Expression Reference
| Expression | Value |
|---|---|
{{ $output.approvalNode.decision }} | The outcome string: "approved" or "rejected". |
{{ $output.approvalNode.decided_by }} | User ID of the decision-maker. |
{{ $output.approvalNode.decided_at }} | ISO timestamp of the decision. |
{{ $output.approvalNode.comments }} | The approver's comment text. |
Node Policies & GuardRails
| Policy | Rationale |
|---|---|
Always configure autoOnTimeout | Without a timeout action, the workflow suspends indefinitely if an approver is on leave, ignores their inbox, or leaves the organization. All production approval nodes must have a deadline and a defined timeout action. |
Use Role actor type for scalable team approvals | Specifying individual user IDs creates a brittle configuration that breaks when people change roles. Using a Role actor means the approval group automatically reflects the current team members without workflow changes. |
Enable requireReason for compliance workflows | For SOX, GDPR, ISO 27001, and internal audit trails, approvers must document why they approved or rejected. This creates an irrefutable audit record that the decision was deliberate and informed. |
Use Anonymous strategy for contractual and regulatory approvals | When unanimous consent is legally or policy-required, Anonymous ensures every named approver's decision is on record. AnyOne is faster but may not satisfy regulatory requirements for specific approvers. |
| Connect all six output ports | Leaving the error or escalated ports unconnected means those workflow paths silently stall. Every output port must connect to at least a notification or logging node. |
Log all_decisions for multi-actor NofM approvals | In committee-style approvals, persist the complete decision array to an audit table immediately after the success/rejection port fires. Individual vote records are essential for governance reviews. |
Pattern Examples
Pattern 1 — Two-Person Purchase Order Sign-Off (Anonymous)
Both the Finance Director AND the CFO must approve before a high-value PO is submitted to the ERP. A single rejection from either immediately rejects the request.
{
"strategy": "Anonymous",
"deadlineSeconds": 172800,
"autoOnTimeout": "rejected",
"requireReason": true,
"title": "PO Approval Required: {{ $output.poNode.poNumber }}",
"actors": [
{ "id": "user:finance-director-001", "actorType": "User", "label": "Finance Director" },
{ "id": "user:cfo-001", "actorType": "User", "label": "CFO" }
]
}
Pattern 2 — IT Change Advisory Board 3-of-5 Vote (NofM)
An infrastructure change requires at least 3 of 5 CAB members to approve before the deployment pipeline is triggered. The node waits up to 48 hours for the quorum.
{
"strategy": "NofM",
"requiredCount": 3,
"deadlineSeconds": 172800,
"autoOnTimeout": "rejected",
"allowAbstain": true,
"requireReason": true,
"title": "CAB Vote: {{ $output.changeNode.changeTitle }}",
"actors": [
{ "id": "user:cab-member-1", "actorType": "User", "label": "CAB Member 1" },
{ "id": "user:cab-member-2", "actorType": "User", "label": "CAB Member 2" },
{ "id": "user:cab-member-3", "actorType": "User", "label": "CAB Member 3" },
{ "id": "user:cab-member-4", "actorType": "User", "label": "CAB Member 4" },
{ "id": "user:cab-member-5", "actorType": "User", "label": "CAB Member 5" }
]
}
Pattern 3 — Leave Request with 24-Hour Manager SLA (AnyOne)
Any member of the management role can approve or reject a leave request. After 24 hours with no decision, the request escalates to the HR director.
{
"strategy": "AnyOne",
"deadlineSeconds": 86400,
"autoOnTimeout": "escalated",
"escalateToUserId": "user:hr-director",
"title": "Leave Request: {{ $output.formNode.form_data.employeeName }}",
"description": "{{ $output.formNode.form_data.employeeName }} has requested {{ $output.formNode.form_data.leaveType }} leave from {{ $output.formNode.form_data.startDate }} to {{ $output.formNode.form_data.endDate }}. Remaining balance: {{ $var.leaveBalance }} days.",
"actors": [
{ "id": "role:line-managers", "actorType": "Role", "label": "Line Managers" }
]
}