# Connect PingRoom to OpenClaw

[OpenClaw](https://docs.openclaw.ai) runs your agent as a daemon on your own
machine. PingRoom gives that agent a way to reach *you* — a push you feel, a
card on your lock screen, a question you answer with one tap — and turns your
answer back into something the agent can act on.

This guide covers the skill: install the CLI, teach the agent to use it, and
pair without a terminal. Two things make PingRoom different from OpenClaw's
chat channels: the ping is a real push with urgency, and the answer comes back
as an exit code, not as text somebody has to parse.

**CLI:** `@pingroom/cli` ≥ 0.10.3 · **Skill:** `pingroom` ·
**Guide for MCP clients instead:** https://pingroom.io/connect-mcp.md

## Install the CLI

```bash
npm install --global @pingroom/cli
```

The skill declares this dependency, so OpenClaw can also install it for you
from the Skills UI (`kind: node`, package `@pingroom/cli`).

## Install the skill

From ClawHub:

```bash
openclaw skills install @pingroom/pingroom        # add --global to share it
```

Or from a clone of the skills repository:

```bash
git clone https://github.com/pingroom/skills.git
openclaw skills install ./skills/openclaw/skill
```

(`openclaw skills install git:owner/repo@ref` expects `SKILL.md` at the
repository root, so for a skill that lives in a subdirectory the clone form is
the one that works.)

Verify it loaded:

```bash
openclaw skills list          # expect: pingroom
openclaw skills info pingroom
```

## Create and claim the OpenClaw robot

A daemon has no terminal to scan a QR with, so use a labeled, non-interactive
pairing command:

```bash
pingroom pair --agent-label "OpenClaw"
```

```
  Created OpenClaw @agt_ab12cd34ef
  Open: https://api.pingroom.io/pair?token=…
  Waiting for claim… ✓ OpenClaw @agt_ab12cd34ef joined #Project X
```

PingRoom creates the robot profile before you sign in. Open the link on your
phone, confirm its name, `@handle`, and robot avatar, then claim it and choose
which rooms it may reach. This is a separate robot that acts for you, not your
personal PingRoom profile. Its **home room** is where direct Questions and
Handoffs land. The link is good for 15 minutes.

If PingRoom is not installed yet, claim the robot in the browser first. The
success screen links to both app stores; install or open PingRoom and sign in
with the same account. That is where OpenClaw can send urgent Pings, ask
Questions, request Approvals, and keep live work visible. Installing the app
alone does not claim the robot or grant access.

- Exit `0` — paired. The credential is written `0600` to
  `~/.pingroom/credentials.json` (or `$PINGROOM_HOME/credentials.json`).
- Exit `3` — the link expired. Run the labeled pairing command again.

Nothing prompts, nothing draws a QR, and an inherited open stdin does not hold
the command open, so it is safe to run from a supervisor.

**Re-pairing is the same command.** When a credential already exists, the new
one is saved first and the old one is revoked only afterwards, so cancelling
changes nothing.

### Letting the agent do it

Ask the agent to run `pingroom pair --agent-label "OpenClaw"` and relay the
robot identity with the link. OpenClaw backgrounds a command that runs past its
yield window and follows it with the `process` tool, so the agent can read the
link out of the process log and hand it to you in whatever chat you are already
in.

`--json` makes that easier to parse — one JSON object per line on stdout, and
the credential is never printed:

```bash
pingroom pair --agent-label "OpenClaw" --json
```

```
{"event":"pair_url","pair_url":"https://…","agent":{"profile":{"display_name":"OpenClaw","handle":"agt_ab12cd34ef"}},"expires_in":900,"poll_interval_ms":1500}
{"event":"connected","handle":"agt_ab12cd34ef","home_room":{"invite_code":"ab12cd","name":"Project X"},"room_access":"selected","api_url":"https://api.pingroom.io"}
```

Older PingRoom servers omit the nested profile and `home_room`; the legacy
label, handle, and `room` fields remain supported. Neither form prints the
credential.

### One credential per service user

The credential lives under `PINGROOM_HOME` (default `~/.pingroom`). If OpenClaw
runs as its own user, set `PINGROOM_HOME` for that user so the agent is not
sharing your personal credential.

## Or use a token

If you already have a PingRoom agent credential, skip pairing and put it in the
skill's config instead:

```json5
{
  skills: {
    entries: {
      pingroom: {
        env: { PINGROOM_TOKEN: "…", PINGROOM_ROOM: "ab12cd" },
      },
    },
  },
}
```

The skill declares `PINGROOM_TOKEN` as its `primaryEnv`, so `apiKey` works too,
including as a SecretRef:

```json5
{ skills: { entries: { pingroom: {
  apiKey: { source: "env", provider: "default", id: "PINGROOM_TOKEN" },
} } } }
```

**Sandbox caveat.** `skills.entries.*.env` is injected into the *host* agent run
and restored afterwards; it does **not** reach a sandboxed `exec`. If your agent
runs sandboxed, put the credential in `agents.defaults.sandbox.docker.env`, or
mount a `PINGROOM_HOME` containing `credentials.json` into the container.

## Send the first Ping

```bash
pingroom ping -m "OpenClaw connected ✅"
```

It arrives wearing the agent's own name and robot avatar rather than yours.

## Verify the connection

```bash
pingroom                      # prints the status line and the help
openclaw skills list          # pingroom is listed and eligible
```

A connected CLI names the claimed robot and its home room. If it prints
"not connected" instead, that is the expected non-interactive behavior — run
the labeled pairing command, which asks for exactly that.

## What the connection can do

| You want | Command | How the answer comes back |
|---|---|---|
| Redeem a gifted or promotional Pro code | `pingroom redeem <code> --json` | Plan and expiry for the linked account |
| Tell someone something happened | `pingroom ping -m "…"` | Nothing to answer |
| Make it cut through Focus | `pingroom ping -m "…" --urgent` | Nothing to answer |
| Make sure it was seen | `pingroom ping -m "…" --require-ack` | Exit 0 acked · 3 expired |
| Ask a 2–4 option question | `pingroom ask -p "…" -o a:A -o b:B --wait` | Exit 0 answered · 3 expired · 4 cancelled |
| Gate an action | `pingroom approval -p "Ship?" --wait` | Exit 0 approve · 4 deny · 3 expired |
| Ask *your* human privately | `pingroom handoff -m "…" --wait` | Exit 0 acked/answered · 3 expired |
| Show long work in progress | `pingroom live start -c <id> …` | Lock-screen card |
| Hear pings as they land | `pingroom listen` | One line (or JSON) per ping |

Exit codes are the contract: `0` success/answered/approved · `1` error ·
`2` usage · `3` expired · `4` cancelled/denied. A timeout is never an answer —
if a gate expires, say so rather than proceeding.

## Redeem a Pro code

With CLI 0.10.3 or later, run `pingroom redeem <code> --json` after pairing.
It applies the gifted or promotional code to the account linked to that CLI
connection. No room is required, and the result reports the plan and expiry.

The optional native channel plugin 0.1.4 or later exposes the same action as
`/pingroom redeem <code>` in an owner-authorized private chat, and as the
`pingroom_redeem_code` agent tool. These use the channel plugin's credential.
Update it with `openclaw plugins install npm:@pingroom/openclaw-plugin@0.1.4`.

Only submit a code the user asks to redeem. Each code is single-use; a failed
request should be reported without trying other codes. An older partial grant
may need pairing again if the server returns `insufficient_scope`.

## Re-pair and revoke

- `pingroom pair --agent-label "OpenClaw"` — creates a replacement connection
  and revokes the previous one after the new credential is stored.
- **Connected Agents** in the PingRoom app — see each claimed robot profile, its home room
  and room access, widen or narrow that access, or revoke it.
- `pingroom logout` — clears the local credential file only; the server-side
  credential stays live until you revoke it.

## Troubleshooting

- **`an agent token is required`** — not connected. Run the labeled pairing
  command, or set `PINGROOM_TOKEN` in `skills.entries.pingroom.env`.
- **`403 room_not_granted`** — the room is outside what you approved. Widen it
  under Connected Agents, or run the labeled pairing command again.
- **`403 insufficient_scope`** — the credential predates a command the skill
  uses. Pair again to claim a connection with the current server grant.
- **`409 recipient_not_ready`** — no phone of yours can answer yet. Keep the
  connection. Install or update PingRoom at <https://pingroom.io/i>, open it,
  sign in, and enable notifications, then run `/pingroom activate` in chat. It
  runs the check with the plugin's own credential. Retry the original operation
  only after you answer its test Question.
- **`402 pro_required`** — attachments and webhook management need Pro.
- **The skill does not appear** — `openclaw skills check` reports why. The most
  common cause is that the `pingroom` binary is not on `PATH` for the user
  OpenClaw runs as.
- **Sandboxed exec cannot authenticate** — see the sandbox caveat above.

## More

- Agent integration guide: https://pingroom.io/agent.md
- Auth protocol: https://pingroom.io/auth.md
- MCP connector guide (ChatGPT, Codex, Claude, Cursor): https://pingroom.io/connect-mcp.md
- CLI reference: https://www.npmjs.com/package/@pingroom/cli

## The channel plugin (optional)

The skill above teaches an agent to *run* the CLI. The plugin goes further: it
registers PingRoom as an OpenClaw **channel**, so the agent's own replies and
questions go to your phone without the agent thinking about a CLI at all.

```bash
openclaw plugins install npm:@pingroom/openclaw-plugin
openclaw plugins enable pingroom
```

Then, in any chat with your agent:

```
/pingroom connect
```

PingRoom first creates an OpenClaw robot profile. Open the claim link or scan
its QR, sign in, verify the robot identity, choose its rooms, and claim it. The
credential is then written into `channels.pingroom`, and the plugin bundles
this same skill, so installing it also teaches the agent the CLI.

| The agent does | Your phone gets |
|---|---|
| Sends its final reply | A ping |
| Calls `ask_user` | A Question card with 2–4 tappable options |
| Requests an exec approval | An approve / deny card |
| Emits a link action | A ping with a tappable button |
| Someone pings the room | A message in the agent's session |

Other subcommands: `/pingroom status`, `/pingroom rooms`,
`/pingroom disconnect`.

**It is `connect`, not `pair`.** OpenClaw already uses `/pair` for DM allowlist
pairing, which is a different thing. For the same reason
`channels.pingroom.dmPolicy: "pairing"` is refused: PingRoom cannot DM an
unknown sender a code.

**Quota.** Free PingRoom accounts get 20 agent operations per day, so by default
only the agent's final reply becomes a ping, split across at most two. Raise
`maxChunksPerReply`, or set `visibleReplies: "all"`, only on a Pro account.

Full configuration reference: https://www.npmjs.com/package/@pingroom/openclaw-plugin
