Skip to content

Approvals

You can pause a workflow and wait for a human decision. The gate is durable — it survives crashes and restarts.

Waiting for Approval

go
func DeployWorkflow(ctx turbine.Context, input DeployInput) (string, error) {
    // ... build steps ...

    result, err := turbine.WaitForApproval(ctx)
    if err != nil {
        return "", err
    }

    if !result.Approved {
        return "rejected: " + result.Comment, nil
    }

    // ... deploy steps ...
}

The workflow's app status is automatically set to "waiting for approval" (yellow) while blocked.

ApprovalResult

go
type ApprovalResult struct {
    Approved bool   `json:"approved"`
    Comment  string `json:"comment,omitempty"`
}

Setting a Timeout

By default, WaitForApproval blocks indefinitely. Use WithApprovalTimeout to set a deadline:

go
result, err := turbine.WaitForApproval(ctx,
    turbine.WithApprovalTimeout(1 * time.Hour),
)
if errors.Is(err, turbine.ErrApprovalTimeout) {
    // no decision within 1 hour
}

Delivering a Decision

From an HTTP Handler

Send an ApprovalResult via the approval topic:

go
err := turbine.Send(
    rt.NewContext(re.Request.Context()),
    workflowID,
    &turbine.ApprovalResult{Approved: true, Comment: "LGTM"},
    "pt.approval",
)

From Another Workflow

go
turbine.Send(ctx, targetWorkflowID,
    &turbine.ApprovalResult{Approved: false, Comment: "insufficient budget"},
    "pt.approval",
)

From the Dashboard

The Turbine dashboard shows an approve/reject UI for workflows that are waiting for approval.

See the dashboard example for a deploy workflow with an approval gate (DeployWorkflow).