Digitorn
Digitorn
← All patterns
safety pattern

Human in the loop

Pause for explicit approval before any irreversible action.

The problem

The agent is about to commit a refund, send an email, drop a database table, or push code. You want the workflow speed of an agent and the certainty that a human said yes before the irreversible part.

Symptoms
  • Action affects users, money, infra, or external parties
  • Cost of a wrong call is much higher than the cost of the agent waiting
  • Compliance requires an audit trail of human approval
Use when

Refunds, account suspensions, infra changes, money movement, customer-visible communication, schema changes, force pushes, deletes.

Skip when

Internal read-only operations, search, summarisation, draft generation. Approval gates kill UX when the action is reversible.

The YAML

Drop this into an app.yaml. Adjust the credential refs and module names to fit your existing setup.

app.yaml
1modules:2  http: {}3  context_builder: {}45execution:6  mode: conversation7  entry_agent: ops89capabilities:10  grant:11    - { module: context_builder, actions: [ask_user] }1213agents:14  - id: ops15    modules:16      - {http: [post]}17      - {context_builder: [ask_user]}18    brain: { model: claude-sonnet-4-6, credential: anthropic_main }19    system_prompt: |20      Before any irreversible POST (refunds, deletes, sends), call:21        ask_user(message="Confirm: <one-line summary>", choices=["yes", "no"])22      Only proceed when the user picks "yes". Log every decision.2324execution:25  hooks:26    - id: gate_destructive27      "on": tool_start28      condition:29        type: all_of30        conditions:31          - { type: tool_name, match: "http.post|http.delete" }32          - { type: expression, expr: "tool.params.url contains '/refund' or tool.params.url contains '/delete'" }33      action:34        type: gate35        require_approval: true36        message: "Confirm {{tool.name}} to {{tool.params.url}}"

How it works

Walking through the YAML one block at a time so the design is clear, not memorised.

01

Two-tier safety: the prompt and the runtime

The system prompt teaches the agent to ask first. The hook enforces it: even if the agent forgets, the runtime gates destructive calls.

02

ask_user is a real tool, not a hack

context_builder.ask_user pauses the agent, surfaces the question on the SSE stream, and resumes when the user answers. The choice becomes a normal tool result.

03

Hook gate is the safety net

The gate action with require_approval routes through ApprovalQueue. The user sees a system-level prompt that survives even a misbehaving agent.

04

Audit trail by default

Every approval and rejection is logged with timestamps and user id. Compliance and replay both work without extra wiring.

Other ways to solve it

The pattern above is not the only answer. Here is when something else is the right call.

Alternative

Plan-then-execute

Have the agent emit a plan first, the user approves the plan, then the agent executes the whole plan without per-step prompts. Less interruption, larger blast radius if approval is given to an underspecified plan.

Prefer when: When users prefer one big approval to many small ones and the plan is concrete enough to read.
Alternative

Out-of-band review

Agent does the work, posts a draft for human review (Slack message, PR, ticket). Humans approve later. Higher throughput, weaker safety because the action is already taken.

Prefer when: Reversible operations where a delayed correction is acceptable. Not for money or destructive ops.
Read the deep dive

How credentials work on Digitorn: an encrypted vault driven from YAML

Read article
Newsletter

Get the next post in your inbox.

Engineering notes from the Digitorn team. No marketing, no launch announcements, no "10 prompts that will change your life". Just the things we write that we'd want to read.

One-click unsubscribe. We never share your address. Powered by our own infrastructure, not a tracker.

Related patterns

dataAudit everythingEvery tool call, every credential read, hash-chained for replay.