# OpenClaw Hooks Reference — before_tool_call, Fail-Open, and --local That Actually Installs

How OpenClaw's before_tool_call hook works: block or requireApproval resolutions, how it differs from operator shell hooks, and its fail-open behavior.

<div style="background:var(--acp-surface,#f8f9fb);border:1px solid var(--acp-border,#e2e5ea);border-radius:12px;padding:20px 24px;margin:24px 0;">
<p style="margin:0 0 10px;font-weight:600;">Just want the answer?</p>
<p style="margin:0 0 12px;">OpenClaw's control point is a plugin hook, <code>before_tool_call</code>, not a hooks.json file. Install the ACP plugin:</p>
<pre style="margin:0 0 12px;overflow-x:auto;"><code>curl -sf https://agenticcontrolplane.com/install.sh | bash
# or by hand:
openclaw plugins install @agenticcontrolplane/openclaw</code></pre>
<p style="margin:0;">Full walkthrough: <a href="/integrations/openclaw">OpenClaw install guide</a>. Native control model: <a href="/controls/openclaw">OpenClaw permissions, explained</a>.</p>
</div>

OpenClaw is a different shape from the other harnesses in this reference series — a personal-assistant gateway that fronts messaging channels and typically runs unattended, not a terminal coding CLI you're watching. Its hook story is correspondingly different: no `hooks.json` to hand-edit, a plugin system with a typed interception point that can drive OpenClaw's own approval flow directly, and — per the maintained plugin's own README — a fail-open default on network errors, which this reference states plainly rather than rounding up.

## Two hook systems — only one sees tool calls

OpenClaw ships two structurally different hook mechanisms:

- **Plugin hooks** — in-process TypeScript, registered from a plugin's entry point. This is where `before_tool_call` and `after_tool_call` live, alongside agent- and message-lifecycle events.
- **Internal hooks** — operator-authored shell scripts, triggered by command and gateway lifecycle events only. No tool-call visibility at all; a `tool:before`/`tool:after` pair for this system has been requested but isn't shipped.

If someone tells you they "added a hook" to control what OpenClaw's tools do, the first question is which of these two systems they mean — only the plugin system can see a tool call before it runs.

## before_tool_call: block, or a typed approval

`before_tool_call` can return `{ block: true }` — terminal, stops any lower-priority handler from running — or a `requireApproval` result whose resolution is a typed union: `allow-once`, `allow-always`, `deny`, `timeout`, or `cancelled`. That typed shape is what lets a plugin drive OpenClaw's native approval flow directly, instead of bolting a second approval UI on top of it.

Two things worth verifying before relying on it:

- Only the **typed hook runner** dispatches `before_tool_call`/`after_tool_call`. The legacy `registerHook` path — still present for backward compatibility — does not fire them. A plugin registered the old way won't see tool calls even if its code expects to.
- `before_tool_call` existed in the codebase for a period before it was actually wired into the execution pipeline. Verify a fresh install with a real test call rather than trusting the presence of the hook name in documentation.

## Installing the ACP plugin

```bash
curl -sf https://agenticcontrolplane.com/install.sh | bash
```

or by hand:

```bash
openclaw plugins install @agenticcontrolplane/openclaw
```

The plugin registers into OpenClaw's plugin system and routes every tool call through a policy check before it runs (`POST /govern/tool-use`) and forwards the result after (`POST /govern/tool-output`) for audit and content scanning. Every call, decision, and identity lands in the activity log — per-sender attribution where OpenClaw provides it.

## Fails open on network errors

State this plainly, as the plugin's own README does: **the hook fails open on network errors — an outage doesn't break the agent.** That's simpler than the story documented for the other harnesses in this series. The Grok Build, Muse Code, and fx integrations each spell out an attended-fails-open/unattended-fails-closed split, with a loud warning either way. OpenClaw's README doesn't draw that same line for this plugin — it states a single behavior: network errors fail open, full stop.

If you're pointing OpenClaw at an unattended deployment — the common case for a messaging-gateway agent — that single-behavior statement is worth confirming directly against the version you install, rather than assuming it quietly matches the tiered posture used elsewhere in this series. Read the documented behavior at face value: a gateway outage lets calls through rather than blocking them.

## The one integration in this series the installer doesn't skip under `--local`

Across `install.sh`, the plugin-install step for Grok Build, Muse Code, Antigravity, dsh, pi, Prime Agent, and opencode is explicitly gated: under `--local`, each prints a variant of "needs a workspace; local decisions aren't wired there yet" and the step is skipped. OpenClaw's step — `openclaw plugins install @agenticcontrolplane/openclaw` — carries no such gate. It runs the same way whether or not `--local` is passed.

State the caveat carefully rather than oversell it: `--local` mode itself skips the browser sign-in step entirely and seeds `~/.acp/policy.json` for the on-device engine that Claude Code's and Cursor's hooks call directly for a decision. Nothing in the OpenClaw plugin's own source reads that file or resolves anything on-device the way those two do. So what actually happens if you pair `--local` with OpenClaw isn't something the install script alone can characterize — read this as "not explicitly excluded from the installer," not as "verified to work fully offline."

## What this hook doesn't reach

`before_tool_call` is the tool-call control point; OpenClaw's broader native hardening — approval binding, per-sender policy — lives one level up and is out of scope for a hooks-focused reference. See [OpenClaw permissions, explained](/controls/openclaw) for the full model.

## Known limitations

- Hooks see tool calls, not tokens: model spend isn't metered for OpenClaw today — no proxy or transcript path ships for it, so tool-call rows (allow/deny, with per-sender identity where available) are what's recorded, not per-call token cost.
- Confirm your OpenClaw version dispatches through the typed hook runner — the legacy `registerHook` path doesn't fire `before_tool_call` at all.
- Internal (operator shell) hooks still can't see tool calls — that gap belongs to OpenClaw's own hook architecture, not to any plugin built on top of it.

## Comparing to Claude Code hooks

Claude Code's hooks are file-registered (`settings.json`) and process-spawned per call; OpenClaw's are plugin-registered and in-process, with a typed approval resolution Claude Code's flatter allow/deny/ask contract doesn't have. Both share the same practical lesson: a control hook needs its own explicit failure posture, because the harness's own default — fail-open here, fail-open-by-timeout on Claude Code — is a design choice for the harness, not a guarantee for whatever's built on top of it. See the [Claude Code hooks reference](/blog/claude-code-hooks-reference) for that side of the comparison.

## Frequently asked questions



## Where to read more

- [OpenClaw permissions, explained](/controls/openclaw)
- [OpenClaw install guide](/integrations/openclaw)
- [Claude Code hooks reference](/blog/claude-code-hooks-reference)
- [Codex hooks reference](/blog/codex-cli-hooks-reference)
- [openclaw-acp-plugin source](https://github.com/agentic-control-plane/openclaw-acp-plugin)
- <!-- source: https://github.com/openclaw/openclaw --> [OpenClaw](https://github.com/openclaw/openclaw)
