When Approval Is Required
A run enters pending_approval status when:
- An approval rule matches the run (based on predicates like
destroyed_gt,added_gt, etc.) - An OPA policy returns a
require_approvalrule - An external check provider is configured for the workspace
Approving a Run
POST /workspaces/{workspaceId}/runs/{runId}/approve
{ "comment": "Looks good, infrastructure changes are expected" }
Requirements:
- Run must be in
pending_approvalstatus (409 otherwise) - Caller must have the
approveverb on the workspace - User auth required — API keys cannot approve runs
Rejecting a Run
POST /workspaces/{workspaceId}/runs/{runId}/reject
{ "comment": "Too many resources being destroyed" }
The comment is used as the rejection reason. The run transitions to rejected.
Multi-Stage Approvals
Approval requirements are organized into stages. All requirements in a stage must be satisfied before the next stage activates:
Stage 1: Platform team approval (min: 1)
↓ (all stage 1 requirements satisfied)
Stage 2: Security team approval (min: 2)
↓ (all stage 2 requirements satisfied)
Run completes → auto-apply if enabled
View requirements for a run:
GET /runs/{id}/approval-requirements
Viewing Approval Decisions
GET /workspaces/{workspaceId}/runs/{runId}/approvals
Returns all approval/rejection decisions:
[{
"id": "uuid",
"run_id": "uuid",
"user_id": "uuid",
"user_email": "user@example.com",
"decision": "approved",
"comment": "Looks good",
"created_at": "2026-01-15T10:00:00Z"
}]
Re-notifying Approvers
POST /workspaces/{workspaceId}/runs/{runId}/renotify
Re-sends approval notifications for a pending_approval run. Rate-limited to once per 5 minutes (429 otherwise).
Slack Approvals
If Slack is connected, approvers can approve or reject directly from Slack messages using interactive buttons. The Slack user’s email must match a Forgecroft account in the same org.
Auto-Apply After Approval
If the workspace has auto_apply: true and the changeset is not discarded, a successful approval automatically enqueues an apply run.
Related API Endpoints
POST /workspaces/{workspaceId}/runs/{runId}/approve— Approve a runPOST /workspaces/{workspaceId}/runs/{runId}/reject— Reject a runGET /workspaces/{workspaceId}/runs/{runId}/approvals— List decisionsGET /runs/{id}/approval-requirements— List requirementsPOST /workspaces/{workspaceId}/runs/{runId}/renotify— Re-notify approvers