iOS App: Connect to Gateway, Pairing, and Troubleshooting
Learn how the iOS app connects to a Gateway, pairs devices, and exposes node capabilities. Includes offline caching, queued messages, and troubleshooting tips.
Read this when
- Pairing or reconnecting the iOS node
- Using voice input and spoken replies on Apple Watch
- Enabling or troubleshooting the direct Apple Watch node
- Running the iOS app from source
- Debugging gateway discovery or iOS node commands
- Choosing colors for native chat sessions
iPhone app builds reach users through Apple's distribution channels once a release is enabled. Running directly from source is also possible for local development builds.
What it does
- Establishes a WebSocket connection to a Gateway over LAN or tailnet.
- Makes node capabilities available: screen snapshots, camera capture, location, talk mode, voice wake, and optional health summaries.
- Accepts
node.invokecommands and reports node status events. - From the Agents surface (Files), the selected agent's workspace can be browsed read-only: navigate directories, preview text with syntax highlighting, view images, and export via the share sheet. Writes are not permitted, and the gateway caps preview sizes.
- A small read-only offline cache holds recent chat sessions and transcripts for each paired gateway: cold starts render the last known transcript immediately and update once the gateway responds, recent chats stay viewable while offline, and reset/forget wipes the protected local cache.
- Text messages sent while offline queue in a durable per-gateway outbox (up to 50): queued bubbles appear in the transcript, flush in order on reconnect with idempotent retries, stay durable until canonical history confirms the send, retry with backoff before showing a retry/delete action, and expire after 48 hours offline instead of sending; reset/forget clears the queue along with the cache.
- Chat serves as the single text-and-voice surface. From Chat, actions can open the full Sessions screen without leaving the current view, and can toggle visibility of assistant reasoning and tool activity. Tap the microphone for draft dictation, open its menu to record a voice note, or use the inline Talk control for realtime voice; the Talk control animates from live microphone or playback level while listening or speaking.
- Chat accepts images from the photo picker, camera, Files, paste, and the iOS share sheet. Assistant-generated images render inline from short-lived Gateway artifact URLs, open in a full-screen preview, and remain available after reconnect or history reload without storing image bytes in the transcript cache.
- Settings -> OpenClaw opens a dedicated Gateway settings assistant when the operator connection has
operator.adminand the Gateway supportsopenclaw.chat. Its setup conversation stays separate from ordinary Chat, redacts secret replies locally, and moves to Chat only after you tap Open Chat. - Assistant messages can be spoken on demand: long-press a message in Chat and choose Listen. The app plays supported gateway
tts.speakclips with the configured TTS provider and falls back to on-device speech when gateway audio is unavailable or unplayable. Playback stops on session switch or backgrounding.
Session colors
Long-press a session in the sidebar or Sessions screen to open its session actions, then choose Color. Select red, blue, green, yellow, purple, orange, pink, or cyan. Default clears the color.
A colored session has a narrow leading stripe in session lists and a small dot beside its title in Chat. Unset colors show neither marker. The Gateway stores color names, not hex values; the app adjusts their hues for light and dark appearances.
Requirements
- Gateway running on another device (macOS, Linux, or Windows via WSL2).
- Network path:
- Same LAN via Bonjour, or
- Tailnet via unicast DNS-SD (example domain:
openclaw.internal.), or - Manual host/port (fallback).
Quick start (pair + connect)
On first launch the app walks through a short pairing explainer, then Gateway setup. It does not present an aggregate permissions page. Optional access is requested when you use the related feature, or after you tap Continue for that permission under Settings -> Permissions -> Privacy & Access. Continue immediately presents the native iOS authorization prompt. You can change granted access later in the iOS Settings app.
- Start an authenticated Gateway with a route your phone can reach. Tailscale Serve is the recommended remote path:
openclaw gateway --port 18789 --tailscale serve
For a trusted same-LAN setup, use an authenticated gateway.bind: "lan"
instead. The default loopback bind is not reachable from a phone. If the
Gateway has not been configured yet, run openclaw onboard first so setup-code
creation has a token or password auth path.
-
Open the Control UI, select Nodes, and click Pair device on the Devices page. Full access is recommended and selected by default; choose Limited access only when you want to omit administrative Gateway controls, then click Create setup code.
-
In the iOS app, open Settings -> Gateway, scan the QR code (or paste the setup code), and connect.
Paired gateways remain in the Gateways list. The checkmark identifies the focused gateway; use the bolt control on another row to keep its operator session connected at the same time. Switching focus does not disconnect other enabled gateways. Only the focused gateway receives the iPhone's capability-bearing node session, so camera, screen, location, and other device commands always have one unambiguous owner. iOS may suspend these foreground connections after the app enters the background.
-
The official app connects automatically. If Pending approval shows a request, review its role and scopes before approving it.
Settings → Gateway shows whether the saved operator connection has Full or Limited access. Plaintext LAN
ws://setup is automatically limited for bearer-token safety. If it is limited, configurewss://or Tailscale Serve, scan a new full-access code from Control UI oropenclaw qr, then reconnect to enable settings and upgrades.
The Control UI button requires an already paired session with operator.admin.
As a terminal fallback, pick a discovered gateway in the iOS app (or enable
Manual Host and enter host/port), then approve the request on the Gateway host:
openclaw devices list
openclaw devices approve <requestId>
If the app retries pairing with changed auth details (role/scopes/public key), the previous pending request is superseded and a new requestId is created. Run openclaw devices list again before approval.
Optional: if the iOS node always connects from a tightly controlled subnet, you can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:
{
gateway: {
nodes: {
pairing: {
autoApproveCidrs: ["192.168.1.0/24"],
},
},
},
}
This is disabled by default. It applies only to fresh role: node pairing with no requested scopes. Operator/browser pairing and any role, scope, metadata, or public-key change still require manual approval.
- Verify connection:
openclaw nodes status
openclaw gateway call node.list --params "{}"
Health summaries
The iOS node can return an opt-in, read-only HealthKit aggregate for the current calendar day. iOS device consent and explicit Gateway command authorization are independent gates. See HealthKit summaries for setup, invocation, payload fields, privacy behavior, and troubleshooting.
By default, the Apple Watch companion keeps using the existing iPhone relay and does not need a separate Gateway pairing. Pair the Watch with the iPhone in Apple's Watch app, install OpenClaw from Watch app -> My Watch -> Available Apps, then open OpenClaw once on both devices.
Review command approvals
An operator connection with operator.admin, or a paired
operator.approvals connection explicitly targeted by the Gateway, can review
pending exec requests on iPhone. The approval card shows the Gateway's
sanitized command preview, warning, host context, expiry, and only the
decisions offered by that request. The paired Apple Watch receives the same
reviewer-safe prompt through the existing iPhone relay and offers the compact
allow-once/deny decision subset. Direct Watch Gateway mode does not carry
approval prompts.
Approval state is shared with the Control UI and supported chat surfaces. The first committed answer wins. iPhone and Watch fetch the Gateway's canonical terminal record after another surface resolves the request, after a remote resolved notification, and whenever a resolve acknowledgement may have been lost. Actions stay unavailable until that readback confirms whether the request remains pending.
Approval ownership is bound to the selected Gateway. Switching gateways cannot apply an old prompt to the replacement connection. Gateways that predate the unified approval methods fall back to the shipped exec-specific methods; retained terminal state and richer cross-surface results require an updated Gateway.
Answer agent questions
For operator connections using operator.questions (or operator.admin), pending Gateway questions appear in Chat as native cards. These cards handle single-select and multi-select options, option descriptions, free-text Other responses, and a countdown to expiry. When a reconnect happens, pending questions are fetched again from the Gateway. A card becomes locked if this device submits an answer, another surface answers first, or the question expires or gets cancelled.
Optional direct Apple Watch node
With Direct mode, the watch obtains its own signed node identity and its own Gateway connection. Supported node commands keep functioning over the watch's Wi-Fi or cellular connection while OpenClaw runs, even if the paired iPhone is not reachable.
Requirements:
- The iPhone must connect to the Gateway using the
operator.adminscope. - The setup code must advertise a
wss://Gateway endpoint whose certificate watchOS trusts; the watch polls the matchinghttps://origin. Plain HTTP, self-signed certificates, and fingerprint-only trust are not supported. Endpoint configuration is covered in Gateway-owned pairing. Loopback, iPhone-only, and tailnet-only routes cannot be reached directly by the watch. - Cellular operation needs an Apple Watch with cellular capability and an active service plan.
- OpenClaw must be active on the watch. Since Apple prevents ordinary watchOS apps from maintaining generic WebSocket/TCP connections, the direct node relies on short HTTPS polls and reconnects when the app comes back to the foreground. Refer to Apple's watchOS low-level networking guidance.
Setup:
- On the iPhone, go to Settings -> Apple Watch.
- Select Enable Direct Gateway Connection.
- Open OpenClaw on the watch before the short-lived setup code runs out.
- Confirm the separate Apple Watch row showing
openclaw nodes status.
The setup code holds a short-lived bootstrap credential meant only for the node; handle it like a password until it expires. It never includes the iPhone's stored Gateway password or token. Once pairing finishes, the watch keeps its own device token and removes the bootstrap credential. Direct mode only applies to the commands listed below. Chat, Talk, approvals, and the current watch.* notification flow stay iPhone-relay features and continue to need the paired iPhone.
Direct watchOS node commands:
| Surface | Commands | Notes |
|---|---|---|
| Device | device.info, device.status | Watch identity, battery, thermal, storage, and network. |
| Notifications | system.notify | While the app is active; requires watch permission. |
Relay-backed push for official builds
Official distributed iOS builds route pushes through an external relay rather than exposing the raw APNs token to the gateway. Builds from the public release lane that reach the App Store use the hosted relay at https://ios-push-relay.openclaw.ai; that base URL is fixed for App Store distribution and ignores any override.
A custom relay deployment demands a deliberately separate iOS build and deployment path whose relay URL matches the gateway's relay URL. The App Store release lane will never accept a custom relay URL. When using a custom relay build, configure the matching gateway relay URL:
{
gateway: {
push: {
apns: {
relay: {
baseUrl: "https://relay.example.com",
},
},
},
},
}
The flow works like this:
- The iOS app registers with the relay using App Attest plus a StoreKit app transaction JWS.
- In return, the relay supplies an opaque relay handle and a send grant scoped to that registration.
- The iOS app obtains the paired gateway identity (
gateway.identity.get) and includes it in relay registration, so the relay-backed registration is delegated to that specific gateway. - That relay-backed registration is then passed to the paired gateway via
push.apns.register. - The stored relay handle is what the gateway uses for
push.test, background wakes, and wake nudges. - Should the app later pair with a different gateway, or run a build with a different relay base URL, it refreshes the relay registration instead of reusing the previous binding.
What the gateway does not need for this path: no deployment-wide relay token, no direct APNs key for official App Store relay-backed sends.
Expected operator flow:
- Install the official iOS app.
- Optional: set
gateway.push.apns.relay.baseUrlon the gateway only when running a deliberately separate custom relay build. - Pair the app to the gateway and wait for the connection to complete.
- Once the app holds an APNs token, the operator session is connected, and relay registration succeeds, it publishes
push.apns.register. - From that point,
push.test, reconnect wakes, and wake nudges can rely on the stored relay-backed registration.
Background alive beacons
When iOS launches the app for a silent push, background refresh, or significant-location event, the app attempts a brief node reconnect and then invokes node.event with event: "node.presence.alive". The gateway marks this as lastSeenAtMs/lastSeenReason on the paired node/device metadata, but only after the authenticated node device identity is established.
The app considers a background wake successfully recorded only when the gateway response includes handled: true. Older gateways may acknowledge node.event with { "ok": true }; that response is compatible, yet it does not qualify as a durable last-seen update.
Compatibility note:
OPENCLAW_APNS_RELAY_BASE_URLremains a temporary env override for the gateway (gateway.push.apns.relay.baseUrlis the config-first route).- The App Store release build's push mode fixes the hosted relay host and never reads a relay-URL override; the
OPENCLAW_PUSH_RELAY_BASE_URLbuild-time env var affects only local/sandbox iOS build modes.
Authentication and trust flow
The relay exists to enforce two constraints that direct APNs-on-gateway cannot deliver for official iOS builds:
- Only genuine OpenClaw iOS builds distributed through Apple may use the hosted relay.
- A gateway can send relay-backed pushes only to iOS devices that paired with that same gateway.
Hop by hop:
iOS app -> gateway: the app pairs with the gateway via the standard Gateway auth flow, which yields an authenticated node session and an authenticated operator session. The operator session callsgateway.identity.get.iOS app -> relay: the app hits the relay registration endpoints over HTTPS with App Attest proof plus a StoreKit app transaction JWS. The relay checks the bundle ID, App Attest proof, and Apple distribution proof, and insists on the official/production distribution path; this is what keeps local Xcode/dev builds off the hosted relay, because a local build cannot satisfy the official Apple distribution proof.gateway identity delegation: before relay registration, the app pulls the paired gateway identity fromgateway.identity.getand puts it in the relay registration payload. The relay then returns a relay handle and a registration-scoped send grant delegated to that gateway identity.gateway -> relay: the gateway saves the relay handle and send grant received frompush.apns.register. Onpush.test, reconnect wakes, and wake nudges, the gateway signs the send request with its own device identity; the relay checks both the stored send grant and the gateway signature against the delegated gateway identity from registration. Another gateway cannot reuse that stored registration, even if it somehow gets the handle.relay -> APNs: the relay holds the production APNs credentials and the raw APNs token for the official build. The gateway never stores the raw APNs token for relay-backed official builds; the relay delivers the final push to APNs on behalf of the paired gateway.
Why this design was chosen: to keep production APNs credentials out of user gateways, avoid storing raw official-build APNs tokens on the gateway, restrict hosted relay usage to official OpenClaw iOS builds, and stop one gateway from sending wake pushes to iOS devices owned by a different gateway.
Local/manual builds stay on direct APNs. If you are testing those builds without the relay, the gateway still needs direct APNs credentials:
export OPENCLAW_APNS_TEAM_ID="TEAMID"
export OPENCLAW_APNS_KEY_ID="KEYID"
export OPENCLAW_APNS_PRIVATE_KEY_P8="$(cat /path/to/AuthKey_KEYID.p8)"
These are gateway-host runtime env vars, not Fastlane settings. apps/ios/fastlane/.env only holds App Store Connect auth such as APP_STORE_CONNECT_KEY_ID and APP_STORE_CONNECT_ISSUER_ID; it does not set up direct APNs delivery for local iOS builds.
Recommended gateway-host storage, consistent with other provider credentials under ~/.openclaw/credentials/:
mkdir -p ~/.openclaw/credentials/apns
chmod 700 ~/.openclaw/credentials/apns
mv /path/to/AuthKey_KEYID.p8 ~/.openclaw/credentials/apns/AuthKey_KEYID.p8
chmod 600 ~/.openclaw/credentials/apns/AuthKey_KEYID.p8
export OPENCLAW_APNS_PRIVATE_KEY_PATH="$HOME/.openclaw/credentials/apns/AuthKey_KEYID.p8"
Keep the .p8 file out of the repository and never commit it.
Discovery paths
Bonjour (LAN)
When set up, the iOS app searches _openclaw-gw._tcp on local. plus the same wide-area DNS-SD discovery domain. Gateways on the same LAN show up automatically from local.; for discovery across networks, the configured wide-area domain works without altering the beacon type.
Tailnet (cross-network)
When mDNS is unavailable, switch to a unicast DNS-SD zone (pick a domain, for instance openclaw.internal.) with Tailscale split DNS. The CoreDNS example is covered in Bonjour.
Manual host/port
Turn on Manual Host in Settings and provide the gateway host and port (default 18789).
Multiple gateways
A registry of every paired gateway is kept by the app, letting you move between them without re-pairing:
- Under Settings -> Gateway, a Paired Gateways list appears with the current gateway highlighted. Selecting one switches to it; existing sessions are shut down and re-established with the chosen gateway. When multiple gateways are paired, a quick-switch menu shows up next to the connection row.
- Credentials, TLS trust choices, gateway-specific preferences, and stored chat history all live per gateway. State never bleeds between gateways during a switch, and push registration tracks whichever gateway is active.
- Swiping a paired gateway (or using its context menu) triggers Forget, deleting its credentials, device tokens, TLS pin, and cached chats.
- A discovered gateway must be reachable on the network to switch to it; manual gateways reconnect using the saved host and port.
Computer Use relationship
The iOS app acts as a mobile node surface, not a Codex Computer Use backend. Codex Computer Use and cua-driver mcp drive a local macOS desktop via MCP tools; the iOS app exposes iPhone features through OpenClaw node commands such as camera.*, screen.*, location.*, and talk.*.
Agents can still drive the iOS app through OpenClaw by calling node commands, though those requests travel via the gateway node protocol and respect iOS foreground/background constraints. For local desktop control use Codex Computer Use; this page covers iOS node capabilities.
Voice wake + talk mode
- Voice wake and talk mode can be enabled in Settings.
- Voice wake forwards recognized commands to the active session and surfaces Gateway delivery failures in Settings; talk mode handles spoken assistant responses.
- OpenAI realtime Talk relies on client-owned WebRTC when
talk.realtime.transportis set towebrtc; an explicitgateway-relayconfiguration remains Gateway-owned. See Talk mode. - iOS nodes that support Talk advertise the
talkcapability and can declaretalk.ptt.start,talk.ptt.stop,talk.ptt.cancel, andtalk.ptt.once; for trusted Talk-capable nodes, the Gateway permits these push-to-talk commands by default. - Background audio can be suspended by iOS; treat voice features as best-effort when the app is not in the foreground.
Common errors
NODE_BACKGROUND_UNAVAILABLE: bring the iOS app to the foreground (camera/screen commands need this).- Pairing prompt never appears: execute
openclaw devices listand approve it manually. Gateway setup incomplete: the Gateway supplied only one of the node or operator credentials. Create a fresh iPhone setup code from Devices -> Pair device in the Control UI or viaopenclaw qr, then scan it in Settings -> Gateway. Automatic reconnect stays paused until setup is retried; this is not a device-storage issue.- Watch shows no iPhone state: verify the iPhone reports
watchPaired: trueandwatchAppInstalled: trueinwatch.status. If pairing is false, pair the Watch through Apple's Watch app. If installation is false, install the companion from My Watch -> Available Apps. After either change, open OpenClaw on the Watch once; immediate reachability still needs both apps running, while queued updates can arrive later in the background. - Reconnect fails after reinstall: the Keychain pairing token was cleared; re-pair the node.