# Gateway protocol device, node, and approval methods

> Gateway RPC families for device pairing, node invoke, approvals, Control UI commands, and automation

- 网址：https://funcoding.ai/agents/openclaw/gateway/protocol/rpc-devices-nodes-and-approvals/
- 来源：OpenClaw 官方文档原文（英文），MIT 许可，同步于 2026-10-11
- 官方原文：https://docs.openclaw.ai/zh-CN/gateway/protocol/rpc-devices-nodes-and-approvals

---
RPC method families for device pairing and device tokens, node pairing and invoke, approvals, Control UI commands, and automation, skills, and tools.

## Device pairing and device tokens

- `device.pair.list` returns pending and approved paired devices.
- `device.pair.setupCode` creates a mobile setup code and, by default, a PNG QR data URL. It requires `operator.admin` and is intentionally omitted from advertised discovery. Current gateways include an opaque non-secret `setupId`, authoritative `expiresAtMs`, `setupCode`, optional `qrDataUrl`, `gatewayUrl`, the non-secret `auth` label, `urlSource`, and the issued `access` level (`full`, `limited`, or `node`). Older protocol-v4 gateways omit `setupId` and `expiresAtMs`, so separately shipped clients must treat those lifecycle fields as optional. The `setupId` is independent from the bootstrap credential and is not embedded in the setup code.
- `device.pair.setupStatus` reconciles one setup credential the caller already issued (`{ setupId }`). It requires `operator.admin`, is omitted from advertised discovery, and returns either `{ completion }` after the credential-bearing response finishes or `{ deliveryUncertain }` when the bearer was retired but response delivery could not be confirmed. Both use the same non-secret payload as their corresponding events. When both fields are absent, the gateway holds no retained outcome for that `setupId`.
- `device.pair.approve`, `device.pair.reject`, and `device.pair.remove` manage device-pairing records.
- `device.pair.rename` assigns an operator label (`{ deviceId, label }`) that is preferred over the client-reported display name and survives device repair or re-approval.
- `device.token.rotate` rotates a paired device token within its approved role and caller scope bounds.
- `device.token.revoke` revokes a paired device token within its approved role and caller scope bounds.

The setup code embeds a short-lived bootstrap credential. Clients must not
log or persist it beyond the pairing flow.

Pairing-scoped clients receive `device.pair.setup.completed` only after the
exact setup handoff has delivered its credentials. Its payload is
`{ setupId, deviceId, deviceName?, access, ts }`; it never includes the
bootstrap credential or token-derived identifiers.

If the response closes before delivery can be confirmed, the gateway keeps
the bearer retired and emits `device.pair.setup.deliveryUncertain` instead
of success. The presenting client should offer the operator a path to inspect
or remove the paired device and generate a new setup code.

The gateway records an uncertain outcome when it consumes the bearer, then
promotes it to completion only after response delivery finishes. Operator
event frames are best effort and drop for slow subscribers rather than
closing their socket. A client that displayed a setup code must therefore
call `device.pair.setupStatus` before presenting the code as expired.
Outcomes are retained past the credential's own expiry.

## Node pairing, invoke, and pending work

- `node.pair.list`, `node.pair.approve`, `node.pair.reject`, and `node.pair.remove` cover node capability approvals. `node.pair.request` and `node.pair.verify` were removed in 2026.7 together with the standalone node pairing store; pending requests are created by the Gateway during node connects.
- `node.list` and `node.describe` return known/connected node state.
- `node.rename` updates a paired node label.
- `node.invoke` forwards a command to a connected node.
- `node.invoke.result` returns the result for an invoke request.
  A node may return `NODE_NOT_READY` only when lifecycle cleanup prevented
  execution, before calling a command handler or emitting progress. The
  Gateway retries this rejection up to four times within the original invoke
  deadline, rechecking the connection, pairing, and command authorization at
  each dispatch. General `UNAVAILABLE` errors, disconnects, timeouts, and
  failures after progress are not retried.
- `mcp.tools.call.v1` is the headless node-host command for calling a configured node-local MCP tool. It is carried through `node.invoke`, requires the node to declare the command, and remains subject to pairing approval and `gateway.nodes.commands.deny`.
- `node.event` carries node-originated events back into the gateway.
- `node.pluginTools.update` is the only publication path for replacing the connected node's agent-visible plugin/MCP tool descriptors; `connect` params do not carry them.
- `node.pending.pull` and `node.pending.ack` are the connected-node queue APIs.
- `node.pending.enqueue` and `node.pending.drain` manage durable pending work for offline/disconnected nodes.

## Approval families

- `approval.history` returns newest-first terminal approvals retained for 30 days for exec, plugin, and system-agent requests (scope `operator.approvals`). It supports cursor pagination plus an optional kind filter; pending approvals are not history rows. Treat each cursor as an opaque server token and return the exact value without padding, rewriting, or adding fields.
- `approval.get` and `approval.resolve` are the kind-agnostic durable approval methods (scope `operator.approvals`). `approval.get` returns a sanitized pending or retained terminal projection with a stable `urlPath`; `approval.resolve` accepts the canonical approval id, an explicit `kind`, and a decision, applies first-answer-wins resolution, and always returns the recorded canonical result.
- `exec.approval.request`, `exec.approval.get`, `exec.approval.list`, and `exec.approval.resolve` cover one-shot exec approval requests plus pending approval lookup/replay. They are protocol-boundary adapters over the same durable approval registry.
- `exec.approval.waitDecision` waits on one pending exec approval and returns the final decision (or `null` on timeout).
- `exec.approvals.get` and `exec.approvals.set` manage gateway exec approval policy snapshots.
- `exec.approvals.node.get` and `exec.approvals.node.set` manage node-local exec approval policy via node relay commands.
- `plugin.approval.request`, `plugin.approval.list`, `plugin.approval.waitDecision`, and `plugin.approval.resolve` cover plugin-defined approval flows.

Approval lookup, history, waits, and resolution retain their original device and
account authority while storage work is pending. Disconnecting the socket alone does not cancel an admitted request.
Revoking that authority before commit admission prevents the verdict and withholds
approval details; the pending approval remains available to another authorized reviewer.
A verdict that already committed remains recorded and settles its waiting action.
Resolution replies do not wait for best-effort channel or push notifications after
the decision is recorded. A slow or failed notification cannot reopen the approval
or delay acknowledgement of its verdict.

`exec.approvals.get` accepts optional `expectedOwnerId`; `exec.approvals.set`
accepts `file`, optional `baseHash`, and optional `expectedOwnerId`. Existing
snapshots require the hash returned by `get`; a stale or missing hash refuses the
save. For an absent snapshot, an omitted hash is accepted, but a supplied hash
must match. The default local CLI always carries its observed hash. Both methods
return the existing redacted snapshot (`path`, `exists`, `hash`, `file`) plus
`resolvedDefaults`; omitted socket defaults retain their existing merge behavior.

Default local CLI reads and writes negotiate their separate
[owner capabilities](https://funcoding.ai/agents/openclaw/gateway/protocol/versioning/#local-state-owner-routing)
and send `expectedOwnerId`. Reading participates because it can initialize missing
state. Existing RPC clients may omit the owner field; explicit Gateway and node
targets retain their current transport and response contracts. This changes no
exec policy, standing-grant, or execution-authorization semantics.

## Control UI commands

- `ui.command` lets an `operator.write` caller send typed layout and navigation commands to the requesting Control UI connection, which must advertise the `ui-commands` capability.
- Commands cover pane split/close/focus, sidebar visibility, terminal/browser panel visibility and dock, and session navigation.
- The Gateway derives the recipient from the authenticated request or the agent turn's captured browser target, never from the destination session. Other connections keep their current view. A missing or disconnected requester fails with `UNAVAILABLE`; there is no broadcast fallback.
- Standalone callers that relied on legacy broadcast delivery must initiate these actions from a Control UI connection or a turn started there. A standalone call without a browser target no longer controls connected dashboards.

## Automation, skills, and tools

- Automation: `wake` schedules an immediate or next-heartbeat wake text injection; `cron.get`, `cron.list`, `cron.status`, `cron.add`, `cron.update`, `cron.remove`, `cron.run`, `cron.runs` manage scheduled work.
- `cron.run` enqueues a manual run and acknowledges with `{ ok: true, enqueued: true, runId }`. Pass `waitTimeoutMs` to hold the response until that run records its outcome: the acknowledgement then also carries `run`, the same entry `cron.runs` returns for that `runId`, or `finished: true` when the run ended but its history is not visible to the caller. If the wait ends first, neither is set and the run continues. Agent-runtime callers get the plain acknowledgement immediately for main-session jobs and jobs that run in their own session, because those runs start only after the calling turn.
- `cron.runs` accepts an optional non-empty `runId` filter so clients can follow one queued manual run without racing against other history entries for the same job.
- Skills and tools: `commands.list`, `skills.*`, `tools.catalog`, `tools.effective`, `tools.invoke`. See [Operator helper methods](https://funcoding.ai/agents/openclaw/gateway/protocol/operator-methods/#operator-helper-methods).
