Framework guides

OpenClaw human approval and audit connector

Give your OpenClaw personal AI assistant a real, unbypassable guardrail: signed human approval for sensitive actions, a durable audit of everything it does autonomously, and one review queue across many assistants.

Updated Jul 20, 2026

OpenClaw runs autonomously in the background on the channels you already use. Contro1 gives it a signed human approval gate for the few actions that matter and a durable audit trail for everything else.

Use the integration skill

Copy this skill link into your code agent to add OpenClaw and Contro1 to your system.

Copy skill link

Key takeaways

  • OpenClaw is a personal AI assistant that acts autonomously on your own machine and answers from WhatsApp, Telegram, iMessage, Slack, and more. This connector governs what it is allowed to do.
  • Build the guardrail where it cannot be bypassed: exec approvals are enforced out-of-process on the execution host, so the assistant can only request an action, never run it around the gate.
  • Get signed, role-routed approval for the few sensitive actions, plus a durable audit trail of every autonomous action the assistant took while you were not looking.
  • One reviewer, many assistants: route approvals from every OpenClaw instance into a single queue instead of chasing separate chat threads.
  • Deny the assistant the authority to loosen its own rules: policy changes and self-written skills are privileged and reviewed, so it cannot teach itself past the guardrail.

What OpenClaw is, and where the risk lives

OpenClaw is an open-source personal AI assistant that runs on your own machine and answers you on the channels you already use: WhatsApp, Telegram, iMessage, Signal, Slack, Discord, and more. It is not a chatbot. It keeps persistent memory about you, your projects, and your people, it can browse the web, fill forms, run shell commands, manage email and calendar, and it can even write and improve its own skills. It works while you are asleep or at work.

That autonomy is the point, and it is also the risk. An assistant that can book, buy, deploy, delete, message people, and run code on your behalf will eventually reach an action you would not want it to take on its own authority. The question is not whether it is capable. It is which actions should stop for you, and how you know what it did when they did not.

What this connector does

This connector puts a governance layer around OpenClaw without slowing down the assistant. It does two jobs.

First, it turns the assistant's sensitive actions into signed, role-routed human approvals. OpenClaw already knows how to stop before a risky host command; the connector routes that decision to Contro1, waits for a signed answer, verifies it, and only then lets the action run. Second, it gives you a durable audit trail: every approved, denied, and auto-allowed action, plus - through an agent-side skill - the autonomous actions the assistant took that never needed an approval at all.

  • Sensitive exec and plugin actions pause for a signed human decision.
  • The decision routes by role, with quorum, SLA, and escalation, not just to whoever is watching the chat.
  • Approved, denied, auto-allowed, and blocked actions are all recorded as audit evidence.
  • The assistant self-logs its autonomous background work, so the trail has no blind spots.

Open the GitHub repo · Requests API · Webhooks

Why not just approve from WhatsApp?

If you already talk to your assistant in WhatsApp, routing approvals somewhere else can look like a step sideways. It is not, and the reason is the nature of the work. OpenClaw runs in the background and takes many small actions on its own. You do not want a chat ping for each one, and you should not have to babysit the thread to stay safe.

What you actually want is two different things at once: to be interrupted only for the few actions that genuinely need an accountable, signed decision, and to have a complete record of everything else it did while you were busy. A chat reply is a casual "ok". A Contro1 approval is a signed, time-stamped, role-attributed decision with evidence attached - the right instrument when the action moves money, changes access, or cannot be undone. The two are not the same tool, and high-stakes actions deserve the stronger one.

A dedicated review surface also scales in a way a chat thread cannot. If you run more than one assistant - or a team runs several - WhatsApp gives you one noisy thread per assistant. Contro1 gives you one queue for all of them, with routing so the right owner gets the right decision and coverage so nothing waits on a single person being online.

One reviewer, many assistants

OpenClaw is self-hosted, so it is normal to end up with several: one per person, one per project, one per device. Each one is autonomous, and each one occasionally needs a human.

Point every instance's bridge at the same Contro1 organization and their approvals land in one queue. A single reviewer, or a rota, can clear approvals from all of them, and role routing sends a finance action to finance and a deploy to platform no matter which assistant raised it. Evidence for every instance is searchable in one place, which is what an auditor or an incident review actually needs.

  • Every assistant's approvals arrive in one Contro1 queue.
  • Role routing and quorum apply across instances, not per chat.
  • On-shift coverage and SLA escalation mean no approval waits on one person.
  • A single, unified audit trail spans all of your assistants.

A guardrail the assistant cannot bypass

A guardrail that lives in the prompt is a suggestion: a capable, self-improving agent can reason its way around an instruction. This connector does not rely on instructions. OpenClaw enforces exec approvals out-of-process, on the execution host itself - the openclaw process on the gateway machine, or the companion app on a node. The model that decides to run a command is not the thing that runs it. It can only request; the host holds the gate.

Because enforcement is external, the way to make it truly unbypassable is to deny the assistant any authority to loosen it. Three rules do that, and they are all things you configure once:

  • Policy can only tighten. OpenClaw computes the effective exec policy as the stricter of config and the host approvals file; an approval can never widen it. Keep the host approvals file owned by the host, not writable from the assistant's workspace.
  • Changing the rules is itself privileged. Config mutation and reserved namespaces need operator.admin, and the assistant's session should not hold it. Route any policy or allowlist change through Contro1 approval too, so widening the gate is itself a reviewed action.
  • It cannot rewrite its own instructions. Set skills.workshop.approvalPolicy to pending so the assistant drafts skill proposals into a review queue instead of writing to its own SKILL.md - it cannot grant itself a new capability without a human approving the skill.
  • It cannot approve its own request. The bridge holds the operator.approvals credential, not the assistant, and it runs as a separate operator client. Keep askFallback set to deny so removing the reviewer closes the gate rather than opening it.

What to prepare in Contro1

Contro1 is where the approval decision happens and where evidence is kept. You create an API key and choose who reviews risky OpenClaw actions.

  • Create a Contro1 account and organization.
  • Open Settings -> APIs & Webhooks.
  • Create an API key named something like OpenClaw approval bridge and set it as CONTRO1_API_KEY.
  • Reveal or rotate the organization webhook secret and set it as CONTRO1_WEBHOOK_SECRET.
  • Choose where approvals go: dashboard, Slack, Microsoft Teams, or your operator workflow.
  • Define reviewer routing such as required role, department, SLA, and escalation.
  • Keep the API key and webhook secret outside source control.

What to prepare in the bridge deployment

The connector is the small server in the repo. Deploy examples/typescript on any host that can receive HTTPS: Cloud Run, ECS, Kubernetes, Render, Fly.io, or a VM. It runs as an OpenClaw operator client, outside the gateway process.

Set PUBLIC_BASE_URL to the public address of that server. Contro1 posts the signed decision to PUBLIC_BASE_URL/contro1/callback, the bridge verifies the signature, and only then resolves the approval in OpenClaw.

  • Local run: cd examples/typescript, npm install, npm run dev.
  • Set CONTRO1_API_KEY and CONTRO1_WEBHOOK_SECRET from Settings -> APIs & Webhooks.
  • Set PUBLIC_BASE_URL to the public HTTPS address of the bridge.
  • Give the bridge an OpenClaw operator token with operator.approvals; full pending enumeration currently also draws on operator.admin, so scope it deliberately.
  • Point CONTRO1_POLICY_FILE at a JSON policy file to override the conservative defaults.

Configure OpenClaw so approvals actually stop

The bridge can only govern actions OpenClaw stops for. OpenClaw exec mode has five values: deny, allowlist, ask, auto, and full. Set it to ask, or to auto with a conservative allowlist, so misses pause instead of running.

Then keep the fallback closed. When a prompt is required but no approver is reachable, askFallback decides what happens: deny, allowlist, or full. Keep it deny so an unreachable bridge means the action is denied, never run. This single setting is what makes the approval a gate rather than a suggestion.

terminal
openclaw config set tools.exec.mode ask
openclaw config set skills.workshop.approvalPolicy pending

openclaw approvals set --gateway --stdin <<'EOF'
{ version: 1, defaults: { security: "allowlist", ask: "on-miss", askFallback: "deny" } }
EOF

Two skills: enforced and cooperative

The connector ships two skill files, and they do different jobs. The contro1-openclaw skill is the build guide for whoever sets up the bridge. The contro1-openclaw-agent skill installs into the assistant's own skills directory and teaches it the cooperative behavior: how to give a reviewer clear, factual context when an action needs approval, and to POST a short audit note to the bridge after each autonomous action.

Be clear about the split. The agent-side skill improves quality and completeness - good context, honest logging. It is not the enforcement. A well-behaved assistant follows it; an adversarial one cannot escape the approval path either way, because that path does not trust the assistant to call anything. Enforcement is the host guardrail; the skill is the good manners on top.

Audit records and cases

Local vs production mode

Use OPENCLAW_TRANSPORT=mock while evaluating the repo. This needs no OpenClaw install: you POST an approval to /mock/approvals as if OpenClaw had raised one, and watch it flow to Contro1.

Without a CONTRO1_API_KEY, the bridge runs in simulated mode and logs the request it would create, so you can read the exact payload before connecting a real reviewer.

Switch to OPENCLAW_TRANSPORT=cli against a real gateway once the operator token, exec mode, and public callback URL are ready.

Fail-closed action binding

An approval is permission for one exact action, not a standing licence. OpenClaw enforces this on its own side: it stores the canonical systemRunPlan at approval time and rejects a forwarded run whose command, cwd, agentId, or sessionKey changed after approval. It even re-binds a script to one concrete file and denies the run if that file changed after approval.

The bridge mirrors that rule on the Contro1 side. It hashes the machine-observed facts the reviewer saw, and before resolving an approved decision it re-reads the approval and re-checks the hash. Any drift, or an approval that is no longer pending, is a hard denial.

  • Invalid signature or a timestamp older than 5 minutes: nothing runs.
  • Unknown request id: nothing runs.
  • Approval no longer pending in OpenClaw (expired or resolved elsewhere): nothing runs.
  • Machine-observed facts no longer match what the reviewer approved: nothing runs.
  • Each request id is resolved exactly once; a duplicate callback is a no-op.

Policy defaults

Policy classifies each approval from machine-observed facts only: the command OpenClaw captured, the agent id, and the session key. Text the assistant authored about its own intent is never consulted for a security decision.

DecisionDefault behavior
Auto-allowOnly when an operator-configured auto-allow pattern matches and the session is not production. Nothing is auto-allowed out of the box.
Require approvalEvery plugin/system-agent approval, every production-session command, and any sensitive shape: pipe-to-shell, sudo, ssh, cloud CLIs, kubectl, terraform apply/destroy, docker run/exec, git push, npm publish, chmod 777, secret/.env/.ssh access.
BlockBefore a reviewer sees them: rm -rf /, fork bombs, mkfs, dd to a device, and unknown agents when an allowed-agent list is set.

TypeScript quick start

terminal
git clone https://github.com/contro1-hq/contro1-openclaw-connector
cd contro1-openclaw-connector/examples/typescript
npm install
cp ../../.env.example .env
OPENCLAW_TRANSPORT=mock npm run dev

# in another terminal, inject an approval as if OpenClaw raised one
curl -sX POST http://localhost:8092/mock/approvals \
  -H 'content-type: application/json' \
  -d '{"rawCommand":"sudo systemctl restart api","agentId":"main","sessionKey":"whatsapp:+15550001111"}'

Event taxonomy

The connector emits audit-first event names so security, operations, and compliance can search the full timeline - both the approvals and the assistant's self-logged autonomous work.

events.txt
openclaw.approval.requested
openclaw.approval.auto_allowed
openclaw.approval.blocked
openclaw.approval.approved
openclaw.approval.denied
openclaw.approval.expired
openclaw.approval.binding_mismatch
openclaw.approval.resolve_failed
openclaw.autonomous.*        # self-logged background actions

Beat the 30-minute expiry

OpenClaw expires a pending exec approval after 30 minutes by default, then denies the waiting command. The bridge sets the Contro1 request expires_at to the approval's own expiry, so a reviewer is never asked to approve something OpenClaw has already given up on. For high-value actions, use Contro1 role routing, quorum, and SLA escalation to make sure a decision lands inside that window.

Direct gateway transport is a preview

The bridge reaches OpenClaw through the documented openclaw approvals CLI by default. A direct Gateway WebSocket transport would subscribe to exec.approval.requested and resolve via approval.resolve, learning about approvals the instant they are raised.

That transport ships as a preview and is intentionally not implemented: the connect handshake requires signing the gateway challenge with the device-auth v3 payload from @openclaw/gateway-client, and that package is not yet published on npm as of OpenClaw v2026.7.1. The connector does not guess at an authentication payload, so the CLI transport is the supported path until the package publishes.

Send full agent traceability

Beyond the approval call, attach identity, a run trace, the tools you invoked, and the context you retrieved. Each field is optional — add what you have. The verified identity always comes from your API key; a caller-supplied actor.agent_id is recorded as a claimed sub-agent until an admin verifies it.

  • trace_id / parent_trace_id — link one run (and sub-agent runs) into a single trace.
  • tool_calls[] — what the agent tried to do, so reviewers see the actions.
  • retrieved_context[] — the data the decision was based on (RAG provenance).
  • Then export a signed evidence packet from GET /requests/:id/evidence.
Send full traceability
// Same approval call you already make — now with full traceability.
await client.requests.create({
  request_type: "approval",
  title: "Refund $4,200 to customer 8831",
  source: { integration: "openclaw" },
  actor: { agent_id: "billing-agent", agent_name: "Billing Agent" }, // claimed sub-agent
  trace_id: `trc_${runId}`,          // link every step of this run
  tool_calls: [{ name: "lookup_order", outcome: "success" }],
  retrieved_context: [{ source: "policy:refunds", uri: "kb://policy/refunds" }],
  continuation: { mode: "decision" },
});

Agent identity, traceability & signed evidence

Make the approval the gate, not a suggestion

The signed webhook is cryptographic proof of a human decision. Verify it inside the system that executes the action - not inside the agent. Any tool that must never run without human sign-off (payments, deploys, data deletion) should refuse to act without a verified approval; that way no agent, including shadow agents nobody registered, can trigger it by skipping Contro1.

  • Verify the signature and reject timestamps older than 5 minutes (replay protection; the timestamp marks callback delivery, not request creation, so long SLAs are unaffected).
  • Bind the approval to the exact action parameters via metadata / correlation_id - never treat "an approval arrived" as permission for a different action.
  • Execute each request_id exactly once (idempotency on your side).
  • When in doubt, confirm state directly with GET /v1/requests/:id using a read-only API key.

Full guardrail pattern with code: Webhooks

Put this integration in the enterprise control model

The connector creates the runtime gate. The operating model around it defines which actions pause, which owner receives the decision, how a timeout escalates, and where the outcome is recorded across agent frameworks.

runtime human approval for AI agents · enterprise AI agent control plane

Frequently asked questions

Why route approvals to Contro1 instead of just replying in WhatsApp?

Because the assistant works in the background and takes many actions on its own. You want to be interrupted only for the few that need an accountable, signed decision, and to have a durable audit of everything else. A chat reply is a casual ok; a Contro1 approval is signed, role-routed, and evidence-bearing. It also lets one reviewer handle approvals from many assistants in a single queue. A native WhatsApp approval surface is on the roadmap; the value of signed approvals and audit is the same either way.

How do I make a guardrail OpenClaw cannot teach itself to bypass?

Enforce it outside the model. OpenClaw runs exec approvals on the execution host, so the assistant can only request a command, not run it around the gate. Then remove its authority to loosen the rules: keep the host approvals file out of its workspace, require operator.admin for config changes (which its session should not hold), set skills.workshop.approvalPolicy to pending so it cannot rewrite its own skills, keep askFallback at deny, and let the bridge - not the assistant - hold the approvals credential.

Does it log what the assistant does when I am not looking?

Yes. Approved, denied, auto-allowed, and blocked actions are all recorded. The contro1-openclaw-agent skill additionally teaches the assistant to POST a short audit note after each autonomous action, so background work that never needed an approval is captured too - in a durable, exportable trail beyond OpenClaw's own 30-day metadata ledger.

Does this run as an OpenClaw plugin?

No. It runs as an external operator client outside the gateway. OpenClaw native plugins run in-process and are not sandboxed, so an approval control inside a plugin would have gateway-wide blast radius. The bridge only needs an operator token scoped to approvals.

Which OpenClaw version was this built against?

OpenClaw stable v2026.7.1. The default CLI transport uses only documented openclaw approvals commands, so it is resilient to internal changes; the gateway WebSocket transport stays preview pending the @openclaw/gateway-client npm release.

Related resources