| title | Approval |
|---|---|
| description | Approval protocol schemas |
{/*
Approval Step Approver Type
Declaration order is author-facing: designers derive their picker from this
enum, and it leads with the portable indirect bindings (manager,
position, department, team) — a literal user id comes last.
import { ApprovalDecision, ApprovalEscalation, ApprovalNodeApprover, ApprovalNodeConfig, ApproverType, DecisionOutputDef } from '@objectstack/spec/automation';
import type { ApprovalDecision, ApprovalEscalation, ApprovalNodeApprover, ApprovalNodeConfig, ApproverType, DecisionOutputDef } from '@objectstack/spec/automation';
// Validate data
const result = ApprovalDecision.parse(data);approvereject
| Property | Type | Required | Description |
|---|---|---|---|
| enabled | boolean |
✅ | Enable SLA-based escalation for this node |
| timeoutHours | number |
✅ | Hours before escalation triggers |
| action | Enum<'reassign' | 'auto_approve' | 'auto_reject' | 'notify'> |
✅ | Action on escalation timeout |
| escalateTo | string |
optional | User id or position machine name to escalate to |
| notifySubmitter | boolean |
✅ | Notify the original submitter on escalation |
| Property | Type | Required | Description |
|---|---|---|---|
| type | Enum<'manager' | 'position' | 'department' | 'team' | 'field' | 'expression' | 'org_membership_level' | 'role' | 'user' | 'queue'> |
✅ | |
| value | string |
optional | User id / membership tier / position / team / department / field — per type; for expression, a CEL expression over current.* / trigger.* / vars.* |
| resolveAs | Enum<'user' | 'department' | 'position' | 'team'> |
optional | How an expression result is expanded into approvers (default 'user') |
| group | string |
optional | Group label for per_group sign-off (e.g. "legal", "finance") |
| organization | string |
optional | ADR-0105 D9 — organization whose directory resolves this approver: $root (group org), $parent (one level up), or an organization slug. Omitted = the request's own organization. |
| Property | Type | Required | Description |
|---|---|---|---|
| approvers | { type: Enum<'manager' | 'position' | 'department' | 'team' | 'field' | 'expression' | 'org_membership_level' | 'role' | 'user' | 'queue'>; value?: string; resolveAs?: Enum<'user' | 'department' | 'position' | 'team'>; group?: string; … }[] |
✅ | Allowed approvers for this node |
| behavior | Enum<'first_response' | 'unanimous' | 'quorum' | 'per_group'> |
✅ | How to combine multiple approvers |
| minApprovals | integer |
optional | Approvals required — total (quorum) or per group (per_group). Default 1 |
| lockRecord | boolean |
✅ | Lock the record from editing while pending |
| approvalStatusField | string |
optional | Business-object field to mirror request status onto |
| onEmptyApprovers | Enum<'admin_rescue' | 'fail' | 'auto_approve'> |
✅ | Behavior when no concrete approver resolves at node entry |
| decisionOutputs | string | { key: string; label?: string; type?: Enum<'text' | 'user' | 'department' | 'position' | 'team'>; multiple?: boolean }[] |
optional | Author-declared decision outputs — bare keys or typed { key, type, multiple } declarations |
| escalation | { enabled: boolean; timeoutHours: number; action: Enum<'reassign' | 'auto_approve' | 'auto_reject' | 'notify'>; escalateTo?: string; … } |
optional | Per-node SLA escalation |
| maxRevisions | integer |
✅ | Max send-backs for revision before auto-reject (0 = send-back disabled) |
managerpositiondepartmentteamfieldexpressionorg_membership_levelroleuserqueue
| Property | Type | Required | Description |
|---|---|---|---|
| key | string |
✅ | Output key (the flow variable name under the node id) |
| label | string |
optional | Field label in the decision dialog |
| type | Enum<'text' | 'user' | 'department' | 'position' | 'team'> |
optional | Decision-dialog input widget (default 'text') |
| multiple | boolean |
optional | Collect multiple values (id array) |