hold disposition — the third possible outcome of the governance pipeline, alongside allow and block.
Approvals bridge the gap between full autonomy and full manual control. They let agents execute freely for routine work while ensuring that high-stakes, high-risk, or policy-sensitive actions get human review before proceeding.
⚡ How Approvals Are Triggered
Approvals enter the system through two paths:Policy-Triggered
Dispatch policy with action: require_approval matches, or approval policy trigger condition matches. Returns hold.
Workflow-Triggered
Step configured with hitl flag. Requires human approval regardless of policy evaluation.
pendingApprovals counter is incremented and its operatorStatus is recalculated. The run remains in a paused state until all pending approvals are resolved.
🔄 Approval States
Every approval follows a simple state machine:APPROVAL STATE MACHINE
Pending
Awaiting human decision. Execution is paused.
human approves→
human rejects→
7 days elapsed→
Approved
Execution resumes.
Rejected
Step fails. Workflow handles.
Auto-Rejected
7 days stale. System rejects.
RESOLUTION CHANNELS
BurgundySlackAPI
ESCALATION TIMELINE
Created
1h: Normal
First reminder
24h: Elevated
Urgency up
72h: Critical
Urgent reminder
7d: Auto-Reject
System rejects stale
| State | Description | Terminal |
|---|---|---|
pending | Waiting for human decision. Execution is paused. | No |
approved | Human approved. The workflow interpreter is notified to resume. | Yes |
rejected | Human rejected. The step is marked as failed. | Yes |
📡 Resolution Channels
Approvals can be resolved through three channels:| Channel | How It Works | Permission Required |
|---|---|---|
| Operator Interface | Operators resolve approvals from the approvals queue in a connected operator interface (e.g., the Burgundy dashboard). This is the primary resolution channel. | governance:approve |
| Slack | When a Slack channel is configured, approval notifications are sent to Slack. Operators can resolve directly from Slack. | governance:approve |
| API | Approvals can be resolved programmatically through the platform API. Used for automated approval workflows or external integrations. | governance:approve |
resolvedVia field on each approval records which channel was used, providing a complete audit trail of how governance decisions were made.
🔄 Approval Lifecycle
Creation
When the governance pipeline returns a
hold disposition, an approval record is created with:- runId and stepId identifying the blocked execution
- summary describing what requires approval
- source indicating whether the trigger was
workflow(explicit HITL flag) orpolicy(policy evaluation) - workflowName and vendorName for context
Waiting
While pending, the approval is queryable via the Platform API and visible in operator interfaces. The approval record surfaces how long the approval has been waiting, the blocked workflow run and its current status, the governance source, and a downstream effect label explaining the impact on the run.
Escalating Reminders
A cron job runs every 15 minutes to check for pending approvals and send escalating reminders:
Reminders are debounced: a new reminder is not sent within 1 hour of the previous one, and a reminder at a given tier is not re-sent until the approval ages into a higher tier.
| Age | Urgency | Behavior |
|---|---|---|
| 1 hour | normal | First reminder notification |
| 24 hours | elevated | Elevated urgency reminder |
| 72 hours | critical | Critical urgency reminder |
Resolution
An operator resolves the approval with a decision (
approved or rejected), their identity, and an optional reason. Resolution triggers:- The approval record is updated with status, resolver, timestamp, and resolution channel
- The workflow run’s
pendingApprovalscounter is decremented - The run’s
operatorStatusis recalculated - A workflow resume event is fired via reliable delivery (outbox pattern with retry) to unblock the interpreter
🏷️ Special Approval Types
Beyond standard workflow step approvals, Forge uses the approval system for several governance use cases:Agent Lifecycle Transitions
Agent Lifecycle Transitions
When an agent’s lifecycle stage transition requires approval (e.g., activating a new agent), a lifecycle approval is created with a step ID in the format
lifecycle:{agentId}:{targetStage}.When approved, the agent’s lifecycle stage is transitioned automatically. When rejected, the transition is recorded in the audit trail without changing the agent’s state.Deployment Approvals
Deployment Approvals
When a deployment policy requires approval, a deployment approval is created with a step ID in the format
deployment:{deploymentId}.Resolution transitions the deployment to active (approved) or inactive (rejected).Position Creation Approvals
Position Creation Approvals
When organizational hierarchy changes require approval (creating new agent positions), a position creation approval is created. Pending position data is stored in a separate
pendingPositionCreations table.Approval creates the position. Rejection cleans up the pending record. Auto-rejection after 7 days also cleans up.
Approvals Queue
The platform provides a dedicated approvals queue that surfaces pending approvals with rich context. This data is accessible through the Platform API and operator interfaces. See the Burgundy Dashboard Guide for the operator experience.
- Blocked Run Summary — The workflow run being blocked, its status, and operator status
- Governance Source — Which policy, gate, or trigger caused the hold, with the policy name and trigger details
- Explanation — A headline and rationale snippet derived from the latest governance decision
- Downstream Effect — A label explaining the impact (e.g., “Run progress is blocked until this approval is resolved”)
- Age — How long the approval has been pending
- Action Links — Direct links to the workflow run detail page and audit packet
📋 Approval Data Model
| Field | Type | Description |
|---|---|---|
runId | ID (optional) | The workflow run requiring approval |
stepId | string | The step or lifecycle identifier |
summary | string | Human-readable description of what needs approval |
status | pending / approved / rejected | Current state |
source | workflow / policy | What triggered the approval |
requestedAt | timestamp | When the approval was created |
resolvedAt | timestamp (optional) | When the approval was resolved |
resolvedBy | string (optional) | Who resolved the approval |
decision | string (optional) | The resolution decision |
reason | string (optional) | Reason provided by the resolver |
resolvedVia | burgundy / slack / api | Resolution channel |
slackChannel | string (optional) | Slack channel for notifications |
reminderSentAt | timestamp (optional) | Last reminder timestamp |
In Burgundy: Resolve approvals in the approval queue.

