ObjectStackObjectStack

Approval — Automation Protocol reference

Approval schemas of the ObjectStack Automation Protocol: ApprovalDecision and 5 more — each property with its type, default and a TypeScript example.

Source: packages/spec/src/automation/approval.zod.ts

TypeScript Usage

import { ApprovalDecision, ApprovalEscalationSchema, ApprovalNodeApproverSchema, ApprovalNodeConfigSchema, ApproverType, DecisionOutputDefSchema } from '@objectstack/spec/automation';
import type { ApprovalDecision, ApprovalEscalation, ApprovalNodeApprover, ApprovalNodeConfig, ApproverType, DecisionOutputDef } from '@objectstack/spec/automation';

// Validate data
const result = ApprovalDecision.parse(data);

ApprovalDecision

Allowed Values

  • approve
  • reject

ApprovalEscalation

Properties

PropertyTypeRequiredDescription
enabledbooleanoptional (default: true)SLA escalation switch. Defaults to true: an escalation block carrying timeoutHours is live unless this is explicitly false — the feature-level switch is whether the escalation block exists at all
timeoutHoursnumber✅Calendar (wall-clock) hours before escalation triggers — nights, weekends and holidays count. The platform ships no business-hours calendar: a request opened at 17:00 on a Friday with timeoutHours 4 escalates at 21:00 that same Friday
actionEnum<'reassign' | 'auto_approve' | 'auto_reject' | 'notify'>optional (default: "notify")Action on escalation timeout
escalateTostringoptionalUser id or position machine name to escalate to
notifySubmitterbooleanoptional (default: true)Notify the original submitter on escalation

ApprovalNodeApprover

Properties

PropertyTypeRequiredDescription
typeEnum<'manager' | 'position' | 'department' | 'team' | 'field' | 'expression' | 'org_membership_level' | 'role' | 'user' | 'queue'>✅Approval step approver type. manager is resolved from the directory rather than configured here: it reads the submitter's sys_user.manager_id at runtime, and that column is not a profile field an author edits, so until an operator populates it, a manager step resolves to nobody and the request falls to the node's onEmptyApprovers policy. os lint reports that at authoring time as approval-approvers-may-resolve-empty, and os explain approval-approvers-may-resolve-empty carries the graded population routes and the full remedy, the node-level onEmptyApprovers fallback included; the Approvals guide states the same remedy in prose.
valuestringoptionalUser id / membership tier / position / team / department / field — per type; for expression, a CEL expression over current.* / trigger.* / vars.*
resolveAsEnum<'user' | 'department' | 'position' | 'team'>optionalHow an expression result is expanded into approvers (default 'user')
groupstringoptionalGroup label for per_group sign-off (e.g. "legal", "finance")
organizationstringoptionalADR-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.

ApprovalNodeConfig

Properties

PropertyTypeRequiredDescription
approvers{ type: Enum<'manager' | 'position' | 'department' | 'team' | 'field' | 'expression' | …>; value?: string; resolveAs?: Enum<'user' | 'department' | 'position' | 'team'>; group?: string; … }[]✅Allowed approvers for this node
behaviorEnum<'first_response' | 'unanimous' | 'quorum' | 'per_group'>optional (default: "first_response")How to combine multiple approvers
minApprovalsintegeroptionalApprovals required — total (quorum) or per group (per_group). Omitted ⇒ all resolvable approvers for quorum, 1 per group for per_group
lockRecordbooleanoptional (default: true)Lock the record from editing while pending
approvalStatusFieldstringoptionalBusiness-object field to mirror request status onto
onEmptyApproversEnum<'admin_rescue' | 'fail' | 'auto_approve' | 'fallback'>optional (default: "admin_rescue")Behavior when no concrete approver resolves at node entry — 'admin_rescue' (default) opens the request on the organization's administrators; 'fallback' opens it on fallbackApprovers instead, naming who decides
fallbackApprovers{ type: Enum<'manager' | 'position' | 'department' | 'team' | 'field' | 'expression' | …>; value?: string; resolveAs?: Enum<'user' | 'department' | 'position' | 'team'>; group?: string; … }[]optionalApprovers the request opens on when onEmptyApprovers is 'fallback'
decisionOutputs(string | { key: string; label?: string; type?: Enum<'text' | 'user' | 'department' | 'position' | 'team'>; multiple?: boolean; … })[]optionalAuthor-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; … }optionalPer-node SLA escalation
maxRevisionsintegeroptional (default: 3)Max send-backs for revision before auto-reject (0 = send-back disabled)

Nested Shape: ApprovalNodeConfig.approvers[number]

PropertyTypeRequiredDescription
typeEnum<'manager' | 'position' | 'department' | 'team' | 'field' | 'expression' | …>✅Approval step approver type. manager is resolved from the directory rather than configured here: it reads the submitter's sys_user.manager_id at runtime, and that column is not a profile field an author edits, so until an operator populates it, a manager step resolves to nobody and the request falls to the node's onEmptyApprovers policy. os lint reports that at authoring time as approval-approvers-may-resolve-empty, and os explain approval-approvers-may-resolve-empty carries the graded population routes and the full remedy, the node-level onEmptyApprovers fallback included; the Approvals guide states the same remedy in prose.
valuestringoptionalUser id / membership tier / position / team / department / field — per type; for expression, a CEL expression over current.* / trigger.* / vars.*
resolveAsEnum<'user' | 'department' | 'position' | 'team'>optionalHow an expression result is expanded into approvers (default 'user')
groupstringoptionalGroup label for per_group sign-off (e.g. "legal", "finance")
organizationstringoptionalADR-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.

Nested Shape: ApprovalNodeConfig.fallbackApprovers[number]

PropertyTypeRequiredDescription
typeEnum<'manager' | 'position' | 'department' | 'team' | 'field' | 'expression' | …>✅Approval step approver type. manager is resolved from the directory rather than configured here: it reads the submitter's sys_user.manager_id at runtime, and that column is not a profile field an author edits, so until an operator populates it, a manager step resolves to nobody and the request falls to the node's onEmptyApprovers policy. os lint reports that at authoring time as approval-approvers-may-resolve-empty, and os explain approval-approvers-may-resolve-empty carries the graded population routes and the full remedy, the node-level onEmptyApprovers fallback included; the Approvals guide states the same remedy in prose.
valuestringoptionalUser id / membership tier / position / team / department / field — per type; for expression, a CEL expression over current.* / trigger.* / vars.*
resolveAsEnum<'user' | 'department' | 'position' | 'team'>optionalHow an expression result is expanded into approvers (default 'user')
groupstringoptionalGroup label for per_group sign-off (e.g. "legal", "finance")
organizationstringoptionalADR-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.

Nested Shape: ApprovalNodeConfig.decisionOutputs[number]

PropertyTypeRequiredDescription
keystring✅Output key (the flow variable name under the node id)
labelstringoptionalField label in the decision dialog
typeEnum<'text' | 'user' | 'department' | 'position' | 'team'>optionalDecision-dialog input widget (default 'text'). The user, department and team pickers commit record ids; the position picker commits position machine names
multiplebooleanoptionalCollect multiple values (an array of what the picker commits)
requiredbooleanoptionalApprover must supply this output to approve

Nested Shape: ApprovalNodeConfig.escalation

PropertyTypeRequiredDescription
enabledbooleanoptional (default: true)SLA escalation switch. Defaults to true: an escalation block carrying timeoutHours is live unless this is explicitly false — the feature-level switch is whether the escalation block exists at all
timeoutHoursnumber✅Calendar (wall-clock) hours before escalation triggers — nights, weekends and holidays count. The platform ships no business-hours calendar: a request opened at 17:00 on a Friday with timeoutHours 4 escalates at 21:00 that same Friday
actionEnum<'reassign' | 'auto_approve' | 'auto_reject' | 'notify'>optional (default: "notify")Action on escalation timeout
escalateTostringoptionalUser id or position machine name to escalate to
notifySubmitterbooleanoptional (default: true)Notify the original submitter on escalation

ApproverType

Approval step approver type. manager is resolved from the directory rather than configured here: it reads the submitter's sys_user.manager_id at runtime, and that column is not a profile field an author edits, so until an operator populates it, a manager step resolves to nobody and the request falls to the node's onEmptyApprovers policy. os lint reports that at authoring time as approval-approvers-may-resolve-empty, and os explain approval-approvers-may-resolve-empty carries the graded population routes and the full remedy, the node-level onEmptyApprovers fallback included; the Approvals guide states the same remedy in prose.

Allowed Values

  • manager
  • position
  • department
  • team
  • field
  • expression
  • org_membership_level
  • role
  • user
  • queue

DecisionOutputDef

Properties

PropertyTypeRequiredDescription
keystring✅Output key (the flow variable name under the node id)
labelstringoptionalField label in the decision dialog
typeEnum<'text' | 'user' | 'department' | 'position' | 'team'>optionalDecision-dialog input widget (default 'text'). The user, department and team pickers commit record ids; the position picker commits position machine names
multiplebooleanoptionalCollect multiple values (an array of what the picker commits)
requiredbooleanoptionalApprover must supply this output to approve

On this page