# OpenClaw setup

Source: https://pushary.com/docs/agents/guides/openclaw
Last updated: 2026-11-14

> Approve OpenClaw tool calls from your phone. The Pushary plugin for OpenClaw holds every exec call, and the tools you add, until you say yes, and blocks it on anything else.

Pushary sends OpenClaw's tool calls to your phone for approval. The Pushary CLI ships a plugin for OpenClaw that registers a `before_tool_call` hook. Before OpenClaw runs a gated tool call, Pushary asks you on your phone. Only a yes runs the call. Anything else blocks it.

It is built for an OpenClaw that runs for you alone, often always on, on a small VPS. It works on the Agent and Agent Pro plans. It needs OpenClaw 2026.11.3 or later and Node 24.16 or later.

## Set it up [#set-it-up]

Run this on the machine that runs OpenClaw, as the user the gateway runs as:

```bash
npx pushary@latest setup --agents openclaw
```

Or run `npx pushary@latest setup` and pick OpenClaw when setup asks &#x2A;*Which agents do you use?** Setup is the only supported way to install the plugin.

<Steps>
  <Step>
    ### Scan the QR code with your phone [#scan-the-qr-code-with-your-phone]

    Setup prints a QR code, a phone link and a fingerprint in the terminal. Scan the code with your phone's camera or the Pushary app, sign in or create an account, check that the code on your phone matches the one in the terminal, then tap **Approve** on &#x2A;*Connect this computer?** and allow notifications.

    To skip this step, pass a key from [your agent settings](https://pushary.com/dashboard/agent/settings) with `--key pk_xxx.xxx`. On a shared server, use `--key-stdin` instead, so the key does not show in the process list. If this machine already has a Pushary key, setup reuses it.
  </Step>

  <Step>
    ### Setup installs the plugin [#setup-installs-the-plugin]

    Setup saves your key in `~/.pushary/config.json`, where the plugin reads it. It writes the plugin that ships inside the CLI to `~/.pushary/openclaw-plugin`, a folder Pushary owns, then installs and enables it with OpenClaw's own commands:

    ```bash
    openclaw plugins install ~/.pushary/openclaw-plugin --force --accept-capabilities
    openclaw plugins enable pushary --accept-capabilities
    ```

    A running gateway picks the plugin up. A stopped one loads it at its next start.
  </Step>

  <Step>
    ### Check that it loaded [#check-that-it-loaded]

    ```bash
    openclaw plugins inspect pushary --runtime --json
    ```

    `typedHooks` should list `before_tool_call`. Then run `npx pushary@latest doctor`, which checks your key, your plan and the plugin. See [Check that it works](#check-that-it-works).
  </Step>
</Steps>

To update the plugin, run `npx pushary@latest upgrade`, or run setup again. Both reinstall it from the copy inside the newer CLI.

## What setup changes on your machine [#what-setup-changes-on-your-machine]

| Where                        | What changes                                                                                          |
| ---------------------------- | ----------------------------------------------------------------------------------------------------- |
| `~/.pushary/config.json`     | Your API key. The plugin reads it from here.                                                          |
| `~/.pushary/openclaw-plugin` | The plugin files, copied from the CLI.                                                                |
| OpenClaw's plugins           | `pushary` is installed and enabled, through `openclaw plugins install` and `openclaw plugins enable`. |

## Run it on a VPS [#run-it-on-a-vps]

OpenClaw often runs on a server that is always on, where nobody watches the terminal. Pushary is how a gated call reaches you there.

1. Install OpenClaw on the server as [its docs](https://openclaw.ai) say, and check that `openclaw` runs in a new shell.
2. Connect over SSH as the same user the gateway runs as.
3. Run `npx pushary@latest setup --agents openclaw`. The QR code prints in your SSH session, so you sign in on your phone. The server needs no browser.
4. Check it with `openclaw plugins inspect pushary --runtime --json` and `npx pushary@latest doctor`.

If the gateway runs as a different user, or as a service without that user's home folder, it cannot read `~/.pushary/config.json`. Put the key in that user's `~/.openclaw/.env` instead, which OpenClaw loads into the gateway's environment:

```bash
PUSHARY_API_KEY=pk_xxx.xxx
```

Restart the gateway after you add the key, so it reads the file.

<Callout type="warn" title="No key means every gated call is blocked">
  A gated call that finds no key is blocked, and the reason OpenClaw shows says how to fix it. If you remove your key, remove the plugin too, with `npx pushary@latest clean` or `openclaw plugins uninstall pushary`.
</Callout>

## What it gates [#what-it-gates]

By default the plugin gates:

* Every `exec` call.

* The `process` actions that send input to a running session: `write`, `send-keys`, `submit` and `paste`. They are gated because `process send-keys` can type a command into a session that `exec` started in the background.

* The actions that could switch the gate off or change OpenClaw itself:

  * `plugins`: `install`, `enable`, `disable`, `uninstall`, `reload` and `reloadAndResume`.
  * `gateway`: `update.run`, which updates OpenClaw and restarts the gateway.
  * `openclaw`: every action that sets or unsets config, installs or removes a plugin, creates an agent or team, sets the default model, configures a channel, search, skills or the gateway, or starts, stops or restarts the gateway.

  These are the tools OpenClaw gives the agent to turn the plugin off. Without them, an agent could run `plugins` with `{"action": "disable", "pluginId": "pushary"}` and every later call would run without asking. The limits below list what the gate does not cover.

* A `sessions_spawn` with a `cwd` outside the agent's workspace or in its `hooks` folder, or with a `projectId`, `projectGitUrl` or `worktree`, so a child session cannot start in OpenClaw's state folder. The question names the folder.

* Every `automations` action (the tool is also called `cron`) except `status`, `list`, `get`, `runs` and `next_check`. A scheduled job can start a command that never passes through `before_tool_call`, so adding, changing, running or waking a job asks.

* Every `terminal` action except `read` and `list`. `terminal input` types into a shared operator terminal.

The other `process` actions, `list`, `poll`, `log`, `kill`, `clear` and `remove`, run without asking. So do the read actions of the other three: `plugins` `list`, `inspect` and `search`; `gateway` `config.get` and `config.schema.lookup`; and the `openclaw` status, read and navigation actions such as `status`, `config_get`, `plugin_list` and `open_setup`. An action the plugin does not know is gated.

Changes to OpenClaw's own config and plugins are gated by default. Every `write`, `edit` or `apply_patch` into OpenClaw's own files asks: anything inside an OpenClaw state directory, `~/.config/openclaw` (where Linux keeps `gateway.env`), `~/.pushary`, a config file set by `OPENCLAW_CONFIG_PATH`, the `.env` in the gateway's working folder, and the launchd and systemd files OpenClaw installs for its gateway. That covers hooks, `$include` files, the state database and the plugins themselves. The one exception is the agent's own workspace when it lives inside the state directory, which is where OpenClaw puts it by default (`~/.openclaw/workspace`), so ordinary coding there does not ask. The workspace `hooks` folder always asks, wherever the workspace is, because OpenClaw can load hooks from it as code. The plugin resolves `~`, relative paths against the agent's workspace, and symlinks before it decides, and asks when it cannot resolve a path. An `apply_patch` asks if any one of its files is protected. Writes anywhere else run without asking.

To gate more tools, add their OpenClaw tool ids to `gatedTools` in your OpenClaw config. The list adds to the defaults, so everything above stays gated while the plugin is on:

```json5
{
  plugins: {
    entries: {
      pushary: {
        enabled: true,
        config: { gatedTools: ["message"] },
      },
    },
  },
}
```

Ids are matched the way OpenClaw names its tools: `bash` means `exec`, `cron` means `automations` and `tool_call` means `dispatch_action`. An id that is not a valid tool id is ignored, and the plugin logs it as an error when the gateway starts.

OpenClaw does not mark a tool call as a message send, a payment or a public post, so gate those by adding the tool that does it, such as `message`. Gate the tools that can do harm. Every gated call asks you, and a phone that buzzes for every call teaches you to approve without reading.

Your phone shows the command, for example &#x2A;*Allow OpenClaw to run `rm -rf ./dist`?**

## What happens to a gated call [#what-happens-to-a-gated-call]

| Pushary says                                                                                       | The call                                                                                                                                                                                                                     |
| -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| You answer yes                                                                                     | Runs                                                                                                                                                                                                                         |
| You deny                                                                                           | Blocked, and the agent is told not to retry                                                                                                                                                                                  |
| No answer before the wait ends                                                                     | Blocked, and the question is withdrawn                                                                                                                                                                                       |
| OpenClaw cancels the run                                                                           | Blocked, and the question is withdrawn                                                                                                                                                                                       |
| The agent was stopped from Pushary                                                                 | Blocked                                                                                                                                                                                                                      |
| No phone, browser or Slack is connected                                                            | Blocked, with a link to connect one                                                                                                                                                                                          |
| Pushary cannot be reached, rejects the key or sends a reply the plugin cannot read                 | Blocked                                                                                                                                                                                                                      |
| Your delivery mode keeps approvals off the phone, or a When away push window passes with no answer | Withdrawn in Pushary and handed to OpenClaw's own approval prompt, which offers allow once or deny and blocks the call when nobody can answer there. On an OpenClaw older than 2026.11.3, the plugin blocks the call instead |

Only a yes runs the call. A missing answer never becomes an approval. A plugin error or timeout blocks the call too: OpenClaw fails closed when a `before_tool_call` hook throws or runs out of time.

## Where approvals reach you [#where-approvals-reach-you]

A gated call reaches you as a push notification on your phone, and on the other places you connected to Pushary, such as a browser or Slack. Answer on any of them. Phone calls are not available for OpenClaw yet.

## How long it waits [#how-long-it-waits]

The plugin asks OpenClaw for a five minute `before_tool_call` budget and stops waiting ten seconds before it ends, so it has time to withdraw the question. Your phone has about 4 minutes 50 seconds.

If you set `plugins.entries.pushary.hooks.timeouts.before_tool_call` or `plugins.entries.pushary.hooks.timeoutMs`, in milliseconds, the plugin reads that value and ends its wait before it instead:

```json5
{
  plugins: {
    entries: {
      pushary: {
        hooks: { timeouts: { before_tool_call: 120000 } },
      },
    },
  },
}
```

The wait is held inside one gateway process. If the gateway restarts during a wait, OpenClaw drops the call, and the question stays in Pushary until it expires after 10 minutes.

## Check that it works [#check-that-it-works]

```bash
openclaw plugins inspect pushary --runtime --json
npx pushary@latest doctor
```

`typedHooks` in the first command's output should list `before_tool_call`. Doctor says whether the plugin is installed, enabled and registers its gate, and whether it can read your key. It sends a real test push. Add `--roundtrip` to send a test question too.

Then ask OpenClaw to run a harmless command, such as `ls`. Your phone asks before it runs.

## Where the plugin reads your key [#where-the-plugin-reads-your-key]

1. `PUSHARY_API_KEY` in the gateway's environment, which OpenClaw also loads from `~/.openclaw/.env`.
2. `apiKey` in `~/.pushary/config.json`, or in the file named by `PUSHARY_CONFIG_FILE`.

## Limits [#limits]

* The gate covers the tools this page names and any you add. It does not see other tools: for example the file-transfer plugin's `file_write` is not gated unless you add it to `gatedTools`.
* ACP sessions set to approve everything, and CLI backends, run tools inside the external harness. Those calls never reach `before_tool_call`, so the plugin cannot gate them.
* This is an approval layer, not a sandbox. Pair it with OpenClaw's own sandboxing and `tools.fs.workspaceOnly`.
* The plugin talks only to `https://pushary.com` and `https://www.pushary.com`, the hosts the CLI uses. Any other `PUSHARY_BASE_URL` blocks every gated call, so a changed setting cannot point it at a server that always says yes.
* The plugin is not told which folder a tool runs in. It resolves a relative path against the agent's workspace, any folder inside it, and the gateway's working folder, and a session spawned into any other folder asks first. A run started some other way in a folder inside OpenClaw's state is not covered.
* Config `$include` files outside the state directory, including roots named by `OPENCLAW_INCLUDE_ROOTS`, are not protected.
* Windows has not been tested.
* Pushary's tool rules from the dashboard are not evaluated on the machine. Every gated call asks.
* Another plugin that rewrites tool parameters can change what runs, including a plugin that runs before this one, because OpenClaw gives each plugin its own copy of the call and keeps the earlier plugin's rewrite. Pushary shows and approves the parameters it was given.
* The wait is held inside one gateway process. A gateway restart during a wait drops the call, and the question stays in Pushary until it expires.

## Remove it [#remove-it]

```bash
npx pushary@latest clean
```

`clean` removes Pushary from this machine, for every agent. For OpenClaw it uninstalls the plugin when Pushary installed it, and leaves a different plugin that uses the id `pushary` alone. Add `--dry-run` to see what it would change first.

To remove only the OpenClaw plugin, run `openclaw plugins uninstall pushary`. Without the plugin, OpenClaw's own exec approvals still apply, and nothing reaches your phone.

## Troubleshooting [#troubleshooting]

### Setup says the OpenClaw CLI was not found [#setup-says-the-openclaw-cli-was-not-found]

Setup looks for `openclaw` on your PATH, then in `~/.openclaw/bin` and `~/.local/bin`. Install OpenClaw, check that `openclaw` runs in a new shell as the same user, then run setup again.

### Setup says another plugin already uses the id pushary [#setup-says-another-plugin-already-uses-the-id-pushary]

Setup does not replace a `pushary` plugin that Pushary did not install. Remove it with `openclaw plugins uninstall pushary`, then run `npx pushary@latest setup --agents openclaw`.

### Setup says OpenClaw could not install the plugin [#setup-says-openclaw-could-not-install-the-plugin]

The plugin needs OpenClaw 2026.11.3 or later. Run `openclaw update`, then run setup again.

### Setup says OpenClaw would not enable the plugin [#setup-says-openclaw-would-not-enable-the-plugin]

Run `openclaw plugins enable pushary --accept-capabilities`.

### typedHooks does not list before\_tool\_call [#typedhooks-does-not-list-before_tool_call]

The plugin is installed but did not register its gate. Check that the gateway runs on Node 24.16 or later, then run `npx pushary@latest setup --agents openclaw` again.

### Doctor says only this shell has PUSHARY\_API\_KEY [#doctor-says-only-this-shell-has-pushary_api_key]

The gateway does not run in your shell, so it cannot see the variable. Put the key in `~/.openclaw/.env`, or run setup so it saves the key in `~/.pushary/config.json`.

### A tool I added to gatedTools does not ask [#a-tool-i-added-to-gatedtools-does-not-ask]

Check the gateway's log from its last start. An id that is not a valid tool id is ignored and logged as an error. Use the tool id as OpenClaw names it, then restart the gateway.

### Every exec call is blocked [#every-exec-call-is-blocked]

Read the reason OpenClaw shows for the block. It says which of these happened:

* No key: run setup, or add `PUSHARY_API_KEY` to `~/.openclaw/.env`, then restart the gateway.
* No phone, browser or Slack connected: connect one in [your agent settings](https://pushary.com/dashboard/agent/settings).
* Pushary rejected the key: run setup again to get a new key, then restart the gateway.
* Pushary could not be reached: check the server's network. [Troubleshooting](/docs/agents/reference/troubleshooting#doctor-says-mcp-server-reachable-failed) shows how to test the connection.
* Pushary refused the request: answer or cancel your open questions, then try again.

## Ask your own users (Partner plan) [#ask-your-own-users-partner-plan]

The plugin is for an OpenClaw you run for yourself. If you build a product on OpenClaw and your users approve what it does, call `decisions.ask()` from `@pushary/server` in your own `before_tool_call` hook and pass each user's own id as `externalId`. That needs the [Partner plan](/docs/pricing-and-limits). [Framework adapters](/docs/agents/adapters#openclaw) has the hook.

## Next steps [#next-steps]

<Cards>
  <Card title="OpenClaw human in the loop" description="The plugin and the Partner path side by side." href="/human-in-the-loop-openclaw" />

  <Card title="Receiving notifications" description="Where approvals reach you, and how to connect a phone, a browser or Slack." href="/docs/agents/receiving-notifications" />

  <Card title="Human in the loop" description="How questions, answers and timeouts work across agents." href="/docs/agents/human-in-the-loop" />
</Cards>