---
name: openclaw-mobile-node
description: Set up OpenClaw gateway as a notification relay for an Android device. Enables the agent to read phone notifications (financial apps, messaging, etc.) via the OpenClaw Android companion app's NotificationListenerService.
version: 1.0.0
category: devops
dependencies:
  - OpenClaw gateway (npm: openclaw)
  - Android device with OpenClaw app (ai.openclaw.app) installed
  - Tailscale (or LAN) connectivity between server and phone
tools:
  - terminal
  - adb
---

# OpenClaw Mobile Node — Notification Relay

Forward Android device notifications to the agent via the OpenClaw gateway + companion app.

## Architecture

```
Phone (OpenClaw app) → WebSocket → OpenClaw Gateway (port 18789) → Agent reads notifications
```

The OpenClaw Android app has a built-in `DeviceNotificationListenerService` that captures notifications from all apps (WhatsApp, banking apps, etc.) and forwards them through the gateway WebSocket.

## Prerequisites

- OpenClaw CLI installed (`/usr/bin/openclaw`)
- Phone on same Tailscale network (or LAN)
- Phone has OpenClaw app installed (`ai.openclaw.app`)

## Setup Steps

### 1. Grant Notification Access via ADB

```bash
adb connect <phone-tailscale-ip>:<port>
adb shell cmd notification allow_listener ai.openclaw.app/.node.DeviceNotificationListenerService
```

Verify:
```bash
adb shell settings get secure enabled_notification_listeners
# Should contain: ai.openclaw.app/ai.openclaw.app.node.DeviceNotificationListenerService
```

### 2. Create Gateway Config

File: `~/.openclaw/openclaw.json`

```json
{
  "agents": {
    "defaults": {
      "workspace": "~/.openclaw/workspace"
    }
  },
  "gateway": {
    "mode": "local",
    "bind": "tailnet",
    "controlUi": {
      "dangerouslyAllowHostHeaderOriginFallback": true
    },
    "auth": {
      "mode": "token",
      "allowTailscale": true,
      "token": "<auto-generated-on-first-start>"
    }
  }
}
```

**CRITICAL: `bind` must be in the config file.** CLI tools (`openclaw qr`, `openclaw devices list`) read config to determine bind mode. Without `bind: "tailnet"` in config, CLI thinks gateway is loopback even if the running process was started with `--bind tailnet`. This causes `openclaw qr` to fail with "Gateway is only bound to loopback."

**PITFALL: Use `--bind lan` instead of `--bind tailnet`** if you also need CLI tools (`openclaw devices list`, `openclaw devices approve`) to work from the same machine. The CLI connects to `ws://127.0.0.1:18789` by default — if the gateway only listens on the Tailscale IP, the CLI can't reach it. `--bind lan` makes the gateway listen on `0.0.0.0` (all interfaces), accessible from both loopback and Tailscale.

**CRITICAL: `allowTailscale: true` is required for device pairing over Tailscale.** Without it, every bootstrap token attempt fails with `bootstrap_token_invalid` in gateway logs. The telltale log line: `"authProvided":"bootstrap-token","authReason":"bootstrap_token_invalid","allowTailscale":false`.

### 3. Start Gateway

```bash
# Kill existing if running
pkill -f "openclaw gateway"; sleep 2

# Recommended: bind to all interfaces (loopback + Tailscale)
# so both CLI tools AND the phone app can reach the gateway
nohup openclaw gateway --port 18789 --bind lan </dev/null > /tmp/openclaw-gateway.log 2>&1 &
```

If using `--bind lan`, also add `"allowTailscale": true` to `gateway.auth` in config.

After starting, **wait 5-8 seconds** for the gateway to fully initialize and write its auto-generated token to config.

**Bind options:** `loopback`, `lan`, `tailnet`, `auto`, `custom`

**`--bind tailnet` requires:** `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback: true` in config (or explicit `allowedOrigins`).

Verify:
```bash
ss -tlnp | grep 18789
# Should show: 100.x.y.z:18789 (not 127.0.0.1)
curl -s http://100.x.y.z:18789/health
# Should return: {"ok":true,"status":"live"}
```

### 4. Generate Setup Code

```bash
openclaw qr --setup-code-only --url ws://<tailscale-ip>:18789
```

This returns a base64-encoded JSON payload containing the gateway URL and bootstrap token.

### 5. Connect the Phone App

Two options:

**Option A — Setup Code (recommended):**
OpenClaw app → Gateway → Advanced setup → Setup Code tab → paste the base64 code → Next

**Option B — Manual:**
OpenClaw app → Gateway → Advanced setup → Manual tab → Host: `<tailscale-ip>` → Port: `18789` → Next

Then proceed through Permissions and Connect steps.

### 6. Approve Device Pairing

After the app connects with the setup code, it sends a pairing request. The app screen will show "Gateway error: pairing required" until you approve it:

```bash
openclaw devices list                  # Shows pending request
openclaw devices approve <REQUEST_ID>  # Approve the phone
```

After approval, go back to the app and tap Retry/Next.

## Pitfalls

- **Config key changes:** `agent.*` is deprecated, use `agents.defaults.*`. Check `openclaw doctor` output.
- **`gateway.host` is not a valid key** — use `bind: "tailnet"` in config or `--bind tailnet` CLI flag.
- **`bind` in config vs CLI flag:** Config `bind` is what CLI tools read. CLI flag `--bind tailnet` only affects the running gateway process. Both must agree for `openclaw qr` to work.
- **`--bind tailnet` fails without controlUi config** — must set `dangerouslyAllowHostHeaderOriginFallback: true` or configure explicit `allowedOrigins`.
- **`allowTailscale: true` in `gateway.auth`** is required for Tailscale device pairing. Without it, the gateway rejects all bootstrap tokens even if the token is valid.
- **Token regenerates on gateway restart.** Every `openclaw gateway` start generates a new `gateway.auth.token`. Setup codes from previous runs become invalid. Must regenerate setup code after each restart: `openclaw qr --setup-code-only`.
- **NEVER write a redacted token back to config.** `grep -o '"token": "[^"]*"'` shows `***` for privacy. If you copy this redacted value into the config file, the gateway uses a wrong token and ALL bootstrap auth fails. Always read the full token: `python3 -c "import json; print(json.load(open('/home/ubuntu/.openclaw/openclaw.json'))['gateway']['auth']['token'])"` — or let the gateway auto-generate a fresh one by removing the token from config and restarting.
- **To use a specific token with setup code:** `openclaw qr --setup-code-only --url ws://<ip>:18789 --token <FULL_TOKEN>`
- **Gateway needs restart (not just hot-reload) for auth changes.** Auth config (`allowTailscale`, `token`, `mode`) doesn't hot-reload. Must kill and restart the gateway process.
- **Agent needs API key to process notifications.** Notifications flow from phone → gateway, but the agent fails silently with "No API key found for provider" if no API key is configured. Without this, notifications arrive but are not stored or accessible. Fix: `openclaw agents add main --provider <provider> --key <key>`
- **ADB text input is unreliable** — `input text` mangles dots, special chars, and doesn't clear fields properly. Clipboard via `adb shell am broadcast -a clipper.set` also hits Android size limits. Best approach: have the user manually paste the setup code on the phone.
- **Notification listener permission:** Can be granted via `adb shell cmd notification allow_listener` (no root needed), but the gateway connection must be established separately.
- **Phone must be awake** for ADB operations; notification forwarding works once connected regardless of screen state (the listener service runs as a bound service).
- **Gateway logs show connection attempts:** Check `/tmp/openclaw/openclaw-YYYY-MM-DD.log` for `bootstrap_token_invalid`, `pairing-required`, `allowTailscale: false` to diagnose auth failures.

## Reading Notifications

Once connected, notifications from the phone flow through the gateway. Check gateway logs:
```bash
tail -f /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log | grep -i notif
```

## Diagnosing Connection Failures

**Route:** Tailscale (phone `100.77.44.46` → server `100.87.116.90:18789`)

**Debug commands:**
```bash
openclaw qr --json                    # Check bind source and auth mode
openclaw devices list                 # Should show pending pairing request
openclaw nodes status                 # Check connected nodes
tail -50 /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log | grep -i "auth\|token\|pair\|reject"
```

**Common failure chain (from logs):**
1. `bootstrap_token_invalid` + `allowTailscale: false` → Fix: add `allowTailscale: true` to config
2. `bootstrap_token_invalid` (with allowTailscale: true) → Fix: regenerate setup code (token changed)
3. `pairing-required` + `not-paired` → Fix: approve pending device via `openclaw devices list`

**To approve a pending device pairing:**
```bash
openclaw devices list                  # Shows pending request with requestId
openclaw devices approve <requestId>   # Approve the phone
```

## Relevant App Packages on Device

From `adb shell pm list packages -3`:
- `com.bbva.nxt_argentina` — BBVA Argentina
- `ar.com.bancar.uala` — Ualá
- `com.brubank` — Brubank
- `com.mercadopago.wallet` — Mercado Pago
- `air.PrexArgentina` — Prex
- `com.binance.dev` — Binance
- `com.ripio.android` — Ripio
- `com.tarjetanaranja.ncuenta` — Naranja X
- `com.sube.app` — SUBE
