The OpenClaw Human Approval Gates Playbook
The worst email I ever sent started life as a draft. It was a reply to one person about a scheduling question, a skill with a loose recipient filter sent it to a thread of eleven, and it went out at 6:40 in the morning while every human who could have stopped it was asleep. The incident response playbook ends that story with a rule: anything leaving the building needs a required human approval, enforced by machinery and not by a sentence in a prompt.
This page is the machinery.
It assumes one always-on host running the OpenClaw gateway, some scheduled jobs, and a person who reads Telegram on their phone. Nothing here needs a special product. It needs a folder and a short script.
What the Usual Guides Get Right
The articles that rank for human-in-the-loop agents mostly agree with each other, and mostly they are correct. Gate the actions that are irreversible or expensive and let the agent run everything else. Watch for approval fatigue, because a person asked to approve dozens of things a day stops reading them. Give each pending request a timeout, and when it expires, treat silence as a no.
I follow all of that. Where they stop is the moment the human taps approve, as if the approval and the action happen at the same instant. On an agent host they almost never do. A request written at 6:10 gets approved at 7:45 from a phone in a car, and the executor sends what was true at 6:10. Most of this page is about that gap.
Decide What Waits by Who It Reaches
Sort actions by their consequences, because sorting by tool goes wrong fast. “Email needs approval” sounds tidy until you notice the agent also sends email to itself as a log, and that a Slack post into a client channel is much worse than most emails. My list is the same one I use to define an incident: anything that reaches a human who did not ask for it, and anything that spends real money outside a budget. Deleting data you cannot restore belongs there too, though it comes up far less often than either.
Drafting is free. So is writing to the agent's own state files, as long as the idempotent jobs rules hold. Keep the gate narrow enough that every request through it deserves a real look, because the moment it fills with noise, people start approving without reading.
Make the Queue a Folder the Agent Cannot Empty
The pattern I trust has three parts. The agent writes a proposal into a pending folder and tells the human it is there. The human moves it to an approved folder. A separate executor script, running on its own schedule, sends only what sits in the approved folder and moves each item to done once the send is confirmed.
The important property is that the agent never holds the credential for the gated channel. If the outbound email app password lives only in the executor's environment, no skill edit can route around the queue, since the agent physically cannot send. A gate the agent can open is a suggestion.
A proposal is one JSON file. The fields that matter are the action, the exact recipients, the exact body, and a hash of the evidence the agent was looking at when it wrote the draft:
{
"id": "reply-2026-10-07-0610-a41f",
"action": "email.send",
"to": ["one.person@example.com"],
"subject": "Re: Thursday",
"body": "Thursday at 2 works. I've added it to the calendar.",
"evidence_file": "~/state/inbox/thread-8812.json",
"evidence_sha": "3be0c1f29a7d5e44",
"created": "2026-10-07T06:10:00-07:00",
"expires": "2026-10-07T10:10:00-07:00"
}Write it through a temp file and mv, the same way you write any shared state, so the executor never reads half a proposal. Then send yourself a Telegram message with the id, the recipient count and the first lines of the body. The Telegram setup guide covers getting a bot that can reach your phone.
How the file gets from pending to approved is up to you, with one rule. The approve step should not be a tool the agent can call. An SSH session and a mv is fine. A tiny handler outside the gateway that only accepts messages from your own Telegram user id is fine. An agent skill called approve_proposal defeats the point, because the agent that wants the email sent now has a way to send it.
Building with OpenClaw?
Get the Starter Kit with annotated config, 5 production skills, and deployment checklist.
Grab the Starter Kit →Approvals Go Stale
The ranking guides skip this gap. Between the draft and the approval, the world moved. Maybe the person you were replying to answered their own question at 7:02, or somebody added nine people to the thread and the reply is now going to a crowd. The human approving from their phone sees a sensible draft, because the draft was sensible when it was written, and approves it.
The executor has to check again right before it acts. That is why the proposal carries an evidence hash. Before sending, the executor recomputes the hash of the evidence file, and if it changed, the proposal goes back to pending with a note instead of going out:
now=$(shasum -a 256 "$EVIDENCE" | cut -c1-16)
if [ "$now" != "$EVIDENCE_SHA" ]; then
mv "$P" ~/state/approvals/pending/
notify "$ID: evidence changed since draft, re-review"
continue
fi
n=$(jq '.to | length' "$P")
[ "$n" -gt "$MAX_RECIPIENTS" ] && { mv "$P" ~/state/approvals/rejected/; continue; }The recipient cap sits in the executor too, after the approval, on purpose. A tired human will approve a draft addressed to eleven people when it reads well. A one-line check in a script will not.
Expiry is the blunt version of the same idea. An approval for a reply about Thursday means nothing on Friday, so the executor refuses anything past its expires time, whatever folder it is sitting in. Four hours is a sane default for messages to people. Anything older should be redrafted from fresh evidence, which costs the agent a few seconds and costs nobody an apology.
When Nobody Answers
Silence means no, and the gate should say so out loud. Expired proposals move to an expired folder, and the job that made them logs one line saying what it did not do. Otherwise you have built a new silent failure: work that was drafted, never approved, never sent, and never mentioned, which from the outside looks exactly like the agent doing nothing. The observability guide has a heartbeat pattern that fits here. Alert when the oldest file in pending is more than a few hours old, and alert separately when the pending folder has been empty for a week on a host that normally fills it, because that usually means the notification route broke.
An approval channel that has never delivered a request in a test is decorative.
Approval Fatigue Is a Signal, Too
Keep a count of how many proposals in each category get approved without a single edit. When a category runs clean for a month, the honest conclusion is that review there has turned into pure latency. Graduate it out of the gate and replace the approval with a mechanical limit, such as a recipient cap or an allowlist of addresses, so the work stops waiting while the risky shape of it stays blocked. Business deployments that begin with supervised autonomy and loosen it as confidence grows are doing this already, just usually without the numbers.
Keep anything that touches money gated permanently. I have seen nothing that argues otherwise, and I would rather wait.
Rehearse It Before It Matters
Trigger the drafting job by hand with openclaw cron run and confirm a proposal lands in pending and the Telegram message arrives. Leave it alone past expiry. Check that nothing went out and that the expiry line appeared in openclaw cron log. Then do it again, approve it, and before the executor runs, edit the evidence file. The executor should refuse and say why. If it sends anyway, the gate is a formality, and you found out on a test address at a reasonable hour instead of at 6:40 with eleven real people on the thread.
Related Reading
- The OpenClaw Agent Incident Response Playbook
- The OpenClaw Idempotent Jobs Playbook
- OpenClaw Agent Observability: Detecting Silent Failures
- OpenClaw Telegram Bot Setup Guide
Frequently Asked Questions
Which OpenClaw agent actions should require human approval?
Anything that reaches a person who did not ask for it or spends money outside a budget, plus the rare delete of data you cannot restore. Drafts and writes to the agent's own state files can run without a gate.
Why shouldn't the agent hold the credential for a gated channel?
Because an agent that can send can find a way to send without the queue, through a skill edit or by improvising when a script fails. If only the executor script has the token, the queue is the only path out.
What is a stale approval?
An approval given to a draft whose underlying facts changed after it was written, such as a thread that gained recipients or a question that was already answered. Store a hash of the evidence in the proposal and have the executor recompute it before acting.
What should happen when nobody approves a request?
Treat it as a no. Expire the proposal after a fixed window, log that the action was not taken, and alert if the pending queue grows old, so an unanswered gate never looks like a healthy, idle agent.
Get the free OpenClaw quickstart checklist
Zero to running agent in under an hour. No fluff.