Developer — OpenClaw Integration
A complete, step-by-step guide to connect a Synapse deployment to an OpenClaw AI agent. Includes shell scripts and agent prompts you can paste into OpenClaw to automate the setup.
1. Architecture overview
Synapse talks to OpenClaw over two HTTP round-trips. Outbound, Synapse POSTs each user message to the
plugin's /webhook/synapse route.
Inbound, the plugin POSTs the agent's reply back to
/api/ai/inbound on Synapse, which
relays it to the user over SignalR (AiReply event).
iOS app
│
▼ POST /api/ai/message { "message": "…" } (Bearer JWT)
Synapse backend
│
▼ POST {gateway}/webhook/synapse { text, user_id, token }
OpenClaw gateway (plugin route)
│
▼ agent.run(text) → reply
OpenClaw plugin outbound handler
│
▼ POST {synapse}/api/ai/inbound { user_id, text, token } (inboundToken)
Synapse backend
│
▼ SignalR AiReply → user:{userId} group
iOS app displays reply
- Two tokens, two directions. Gateway Token authenticates Synapse → OpenClaw. Inbound Token authenticates OpenClaw → Synapse.
- Per-user allowlist. Admins explicitly grant each member access. Users not on the allowlist get HTTP 403 and never see the chat tile in iOS.
- Not E2E-encrypted. Unlike regular Synapse chats, messages to the agent flow as plaintext through your backend to your OpenClaw instance. Treat the agent like any SaaS integration that sees message content.
2. Prerequisites
- • A deployed Synapse backend with admin access (your instance of
synapse-communicator). - • A running OpenClaw gateway reachable over HTTPS with a valid Gateway Token (
OPENCLAW_GATEWAY_TOKEN). - • SSH (or equivalent) access to the OpenClaw server, or the ability to install plugins via your OpenClaw control plane.
- • The
@synapse/openclaw-synapse-channelplugin source (shipped in the Synapse repo atopenclaw-synapse-plugin/).
3. Step 1 — Configure the gateway in Synapse Admin
- Sign in to the Synapse admin portal and navigate to Integrations → OpenClaw.
- Gateway URL — enter the base URL of your OpenClaw gateway
(e.g.
https://openclaw.example.comorhttp://openclaw:18789). Do not include the/webhook/synapsesuffix — Synapse appends it automatically. - Gateway Token — paste the token that matches your plugin's
channels.synapse.token. If the plugin is not installed yet, write it down — you will use the same value in step 2. - Click Test connection. You should see "Connected successfully" with a latency reading.
- Click Save.
- In the Plugin setup section that appears, click Generate next to Inbound Token. Copy the token immediately — it is shown exactly once.
- Copy the Synapse Inbound Webhook URL shown below the token. You will paste both values into the plugin config.
4. Step 2 — Install the Synapse channel plugin on OpenClaw
The plugin registers the POST /webhook/synapse route on the gateway and wires the agent's outbound reply to Synapse's inbound endpoint.
2.1 — Copy the plugin onto the OpenClaw host
From your workstation:
cd <your-synapse-checkout>/openclaw-synapse-plugin
tar czf openclaw-synapse-plugin.tgz --exclude=node_modules --exclude=.git .
scp openclaw-synapse-plugin.tgz <user>@<openclaw-host>:/tmp/ 2.2 — Extract and install dependencies
On the OpenClaw host:
ssh <user>@<openclaw-host>
sudo mkdir -p /opt/openclaw/plugins/synapse
sudo tar xzf /tmp/openclaw-synapse-plugin.tgz -C /opt/openclaw/plugins/synapse
cd /opt/openclaw/plugins/synapse
sudo npm install --omit=dev If your OpenClaw distribution ships a plugin installer, prefer it:
openclaw plugins install /opt/openclaw/plugins/synapse 2.3 — Configure the channel
Edit openclaw.config.yaml on the gateway and add a synapse block under channels:. Use the exact values copied from Synapse Admin → Integrations.
channels:
synapse:
# The same value you saved as Gateway Token in Synapse Admin.
token: "<OPENCLAW_GATEWAY_TOKEN>"
# Paste the "Synapse Inbound Webhook URL" from the Plugin setup panel.
incomingUrl: "https://<your-synapse>/api/ai/inbound"
# Paste the Inbound Token you generated (shown only once).
inboundToken: "<INBOUND_TOKEN>"
# "open" = any Synapse user allowed by admin can DM the agent.
# "allowlist" = also require the user's composite ID to be in allowedUserIds.
dmPolicy: open
# allowedUserIds:
# - "<companyId>:<userId>" 2.4 — Restart the gateway
The webhook route is registered at startup, so a restart is mandatory.
# Pick whichever matches your deployment:
sudo systemctl restart openclaw
# or
docker restart openclaw
# or
pm2 restart openclaw 2.5 — Verify from the server
curl -i -X POST http://localhost:18789/webhook/synapse \
-H 'Content-Type: application/json' \
-d '{"text":"ping","user_id":"00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000","token":"<OPENCLAW_GATEWAY_TOKEN>"}' Expected: HTTP 200 or 202. If you get 404, the plugin did not load — recheck the install path and the server logs. If you get 401, the token does not match.
5. Step 3 — Grant users access
OpenClaw access is per-user. A user who is not on the allowlist cannot send messages and does not even see the "OpenClaw Agent" tile in their iOS conversation list.
- In Synapse Admin go to Users.
- Find the column titled OpenClaw. Each row has a toggle.
- Toggle ON for each member who should be able to chat with the agent. The change takes effect immediately.
The allowlist is stored in CompanySettings.OpenClawAllowedUserIds. It is a standard part of the per-company settings JSON and travels with your database backups.
6. Step 4 — Verify end-to-end
6.1 — From Synapse Admin
In Integrations → OpenClaw, click Send test message. You get back the gateway's raw status code, the response body, and an interpretation hint.
- HTTP 200/202 — the plugin accepted the test payload. Proceed to iOS.
- HTTP 404 — plugin not loaded. Recheck step 2.3 and restart.
- HTTP 401 / 403 — Gateway Token mismatch between Admin and
channels.synapse.token. - HTTP 400 — payload schema mismatch. Update the plugin to the version shipped with your Synapse release.
6.2 — From iOS
- Sign in as an allowlisted user. The OpenClaw Agent tile appears at the top of the conversation list.
- Tap it, type a message, send.
- The reply arrives asynchronously over SignalR — typically within the agent's normal response time.
7. Agent prompts — automate setup from inside OpenClaw
If your OpenClaw deployment runs an agent with shell / file-system tools, you can have it perform steps 2.1 – 2.5 on its own host. Paste the prompts below into the agent. Replace placeholders in angle brackets first.
Prompt A — Install the Synapse channel plugin
You are running on the OpenClaw host and have shell access.
Install the Synapse channel plugin so this gateway can receive and send messages through Synapse.
Inputs:
SYNAPSE_GATEWAY_TOKEN = <paste value from Synapse Admin → Integrations → Gateway Token>
SYNAPSE_INBOUND_URL = <paste value from Integrations → Plugin setup → Synapse Inbound Webhook URL>
SYNAPSE_INBOUND_TOKEN = <paste value from Integrations → Plugin setup → Inbound Token (shown once)>
PLUGIN_TARBALL_URL = <URL to openclaw-synapse-plugin.tgz, or /tmp path if already uploaded>
OPENCLAW_CONFIG_PATH = <absolute path to openclaw.config.yaml on this host>
Do the following, in order:
1. Create /opt/openclaw/plugins/synapse if it does not exist.
2. Download or copy the plugin tarball to that directory and extract it.
3. Run `npm install --omit=dev` inside the plugin directory.
4. Edit OPENCLAW_CONFIG_PATH and insert (or update) under `channels:`:
synapse:
token: "${SYNAPSE_GATEWAY_TOKEN}"
incomingUrl: "${SYNAPSE_INBOUND_URL}"
inboundToken: "${SYNAPSE_INBOUND_TOKEN}"
dmPolicy: open
Preserve any other channels that already exist. Never overwrite unrelated keys.
5. Validate the YAML before saving. If the file is malformed, abort and report the error.
6. Restart the OpenClaw service (try `systemctl restart openclaw`; if it fails, try
`docker restart openclaw`; if neither exists, report how the service runs).
7. Wait 5 seconds, then curl `http://localhost:18789/webhook/synapse` with a dummy ping body:
{"text":"ping","user_id":"00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000","token":"${SYNAPSE_GATEWAY_TOKEN}"}
8. Report the HTTP status + body. Any non-2xx is an installation failure — explain likely cause.
Never log any token value in plaintext to stdout or to any file other than the config itself. Prompt B — Rotate the Inbound Token safely
You are on the OpenClaw host. Rotate the inboundToken used by the Synapse channel.
Preconditions: the admin has clicked "Regenerate" in Synapse Admin → Integrations and given you:
NEW_INBOUND_TOKEN = <the freshly generated token, only shown once>
Steps:
1. Read OPENCLAW_CONFIG_PATH and locate channels.synapse.inboundToken.
2. Replace its value with NEW_INBOUND_TOKEN. Keep all other keys as-is.
3. Save and reload OpenClaw (`systemctl reload openclaw` if supported, otherwise restart).
4. Ask the admin to send a test message from Synapse Admin → "Send test message".
Expect HTTP 200/202. If 401/403, roll back to the previous value and report.
Never echo NEW_INBOUND_TOKEN in logs. Prompt C — Verify inbound connectivity from OpenClaw → Synapse
You are on the OpenClaw host. Confirm that this host can reach Synapse for replies.
Inputs:
SYNAPSE_INBOUND_URL = <https://<your-synapse>/api/ai/inbound>
SYNAPSE_INBOUND_TOKEN = <as configured in channels.synapse.inboundToken>
Run:
curl -sS -o /tmp/out -w '%{http_code}\n' -X POST "$SYNAPSE_INBOUND_URL" \
-H 'Content-Type: application/json' \
--data '{
"user_id": "00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000",
"text": "[inbound-probe]",
"token": "'"$SYNAPSE_INBOUND_TOKEN"'"
}'
Interpret:
401 = inbound token is wrong — fix channels.synapse.inboundToken.
404 = Synapse URL is wrong or backend does not expose /api/ai/inbound.
Any 5xx = Synapse backend error — share the body from /tmp/out.
200 = Synapse accepted the probe. Plugin-to-Synapse path is healthy.
Report the status code and a one-line diagnosis. 8. Scripts
8.1 — One-shot plugin installer
Save as install-synapse-plugin.sh on the OpenClaw host, fill in the three variables at the top, and run.
#!/usr/bin/env bash
set -euo pipefail
# ─── fill these in ─────────────────────────────────────────────
SYNAPSE_GATEWAY_TOKEN="${SYNAPSE_GATEWAY_TOKEN:?set me}"
SYNAPSE_INBOUND_URL="${SYNAPSE_INBOUND_URL:?set me}"
SYNAPSE_INBOUND_TOKEN="${SYNAPSE_INBOUND_TOKEN:?set me}"
PLUGIN_TARBALL="${PLUGIN_TARBALL:-/tmp/openclaw-synapse-plugin.tgz}"
OPENCLAW_CONFIG="${OPENCLAW_CONFIG:-/etc/openclaw/openclaw.config.yaml}"
PLUGIN_DIR="${PLUGIN_DIR:-/opt/openclaw/plugins/synapse}"
# ───────────────────────────────────────────────────────────────
echo "→ installing plugin into $PLUGIN_DIR"
sudo mkdir -p "$PLUGIN_DIR"
sudo tar xzf "$PLUGIN_TARBALL" -C "$PLUGIN_DIR"
(cd "$PLUGIN_DIR" && sudo npm install --omit=dev)
echo "→ patching $OPENCLAW_CONFIG"
if ! command -v yq >/dev/null; then
echo "yq is required (https://github.com/mikefarah/yq). Aborting." >&2
exit 1
fi
sudo cp "$OPENCLAW_CONFIG" "$OPENCLAW_CONFIG.bak.$(date +%s)"
sudo yq -i "
.channels.synapse.token = strenv(SYNAPSE_GATEWAY_TOKEN) |
.channels.synapse.incomingUrl = strenv(SYNAPSE_INBOUND_URL) |
.channels.synapse.inboundToken = strenv(SYNAPSE_INBOUND_TOKEN) |
.channels.synapse.dmPolicy = \"open\"
" "$OPENCLAW_CONFIG"
echo "→ restarting OpenClaw"
if systemctl list-units --type=service | grep -q '^openclaw'; then
sudo systemctl restart openclaw
elif docker ps --format '{{.Names}}' | grep -q '^openclaw$'; then
docker restart openclaw
else
echo "Unknown service manager — restart OpenClaw manually." >&2
exit 1
fi
echo "→ smoke test"
sleep 5
curl -sS -o /tmp/synapse-plugin-probe -w 'HTTP %{http_code}\n' -X POST \
http://localhost:18789/webhook/synapse \
-H 'Content-Type: application/json' \
--data "{\"text\":\"[probe]\",\"user_id\":\"00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000\",\"token\":\"$SYNAPSE_GATEWAY_TOKEN\"}"
echo "Response body:"
cat /tmp/synapse-plugin-probe
echo
8.2 — Check current plugin health
#!/usr/bin/env bash
set -euo pipefail
GATEWAY="${GATEWAY:-http://localhost:18789}"
TOKEN="${SYNAPSE_GATEWAY_TOKEN:?set me}"
status=$(curl -sS -o /tmp/out -w '%{http_code}' -X POST \
"$GATEWAY/webhook/synapse" \
-H 'Content-Type: application/json' \
--data "{\"text\":\"[healthcheck]\",\"user_id\":\"00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000\",\"token\":\"$TOKEN\"}")
case "$status" in
200|202) echo "OK — $status"; exit 0 ;;
401|403) echo "AUTH FAIL — token mismatch"; exit 2 ;;
404) echo "PLUGIN MISSING — route not registered"; exit 3 ;;
*) echo "UNEXPECTED — HTTP $status"; cat /tmp/out; exit 4 ;;
esac
9. API reference
User endpoints (require JWT)
| Method | Path | Purpose |
|---|---|---|
| GET | /api/ai/status | Returns { allowed: boolean }. iOS hides the agent tile when false. |
| POST | /api/ai/message | Body { message: string }. Returns 202 {queued:true}. Reply arrives via SignalR AiReply. |
Admin endpoints (require CompanyAdmin policy)
| Method | Path | Purpose |
|---|---|---|
| GET | /api/admin/integrations/openclaw | Current gateway URL, token hints, inbound webhook URL. |
| PUT | /api/admin/integrations/openclaw | Save Gateway URL + Token. Leave token blank to keep the existing one. |
| DELETE | /api/admin/integrations/openclaw | Remove the entire OpenClaw configuration. |
| POST | /api/admin/integrations/openclaw/test | Tests Gateway URL + Token against /v1/models. |
| POST | /api/admin/integrations/openclaw/ping | Full round-trip: sends a probe to /webhook/synapse and returns gateway status + body. |
| POST | /api/admin/integrations/openclaw/inbound-token | Generates a fresh Inbound Token (returned once). |
| GET | /api/admin/integrations/openclaw/allowed-users | Lists allowlisted user IDs. |
| PUT | /api/admin/integrations/openclaw/allowed-users/{userId} | Body { allowed: boolean }. Toggles a single user. |
OpenClaw → Synapse inbound webhook
| Method | Path | Body |
|---|---|---|
| POST | /api/ai/inbound | { user_id: "<companyId>:<userId>", text, token } |
10. Troubleshooting
iOS: "OpenClaw gateway is reachable, but the Synapse channel plugin isn't installed on it (got 404)"
Plugin route /webhook/synapse is not registered. Recheck step 2.3 — is the plugin block in openclaw.config.yaml? Did you restart the gateway after editing?
iOS: "OpenClaw rejected Synapse's gateway token"
The value in Admin → Integrations → Gateway Token does not match channels.synapse.token in openclaw.config.yaml. Watch out for env-var substitution: if the plugin expands ${OPENCLAW_GATEWAY_TOKEN} but the variable is empty in the service environment, the plugin sees an empty token and rejects everything.
iOS: "Your admin has not granted you access to the OpenClaw agent"
The user is not on the allowlist. Admin → Users → toggle OpenClaw on for this user.
iOS: "OpenClaw timed out"
Synapse waited 30 s for the gateway. Agent took longer or is stuck. Check OpenClaw logs. If a specific user triggers this reliably, narrow down the prompt or raise the plugin's internal timeout.
No reply arrives but Synapse returned 202
OpenClaw accepted the message but never POSTed a reply back. Usual causes: the plugin cannot reach incomingUrl (firewall or TLS handshake failure), the inboundToken is wrong (Synapse logs a 401 on /api/ai/inbound), or the agent is crashing mid-run. Run Prompt C from section 7.
11. Security notes
- •Not E2E-encrypted. Messages to the agent are plaintext at both the Synapse backend and OpenClaw. Treat them like any other third-party integration and inform your users accordingly.
- •Per-user allowlist is mandatory. Even after configuring the gateway, no user can talk to the agent until explicitly enabled. This keeps accidental data exposure contained.
- •Rotate the Inbound Token when a plugin deployment is decommissioned or when a shared environment changes hands. The old token is invalidated immediately on regeneration.
- •Keep gateway traffic TLS-terminated end-to-end. Even inside a VPC, prefer HTTPS between Synapse and the gateway so tokens are never observable by packet captures.
Need help integrating Synapse with a different agent platform?
Get in Touch