Synology Chat Webhook Setup and OpenClaw Configuration

Set up Synology Chat with OpenClaw using paired incoming and outgoing webhooks for direct messages. Covers plugin install, webhook configuration, and channel setup commands.

Read this when

  • Setting up Synology Chat with OpenClaw
  • Debugging Synology Chat webhook routing

Synology Chat reaches OpenClaw by way of two webhooks working as a pair. An outgoing webhook on the Synology side delivers inbound direct messages to the Gateway, and answers travel back over a Synology Chat incoming webhook.

Status: official plugin, installed separately. Direct messages only; text and hosted file sends are supported.

Install

openclaw plugins install @openclaw/synology-chat

Local checkout (when running from a git repo):

openclaw plugins install ./path/to/local/synology-chat-plugin

Details: Plugins

Quick setup

  1. Install the plugin (above).
  2. In Synology Chat integrations:
    • Create an incoming webhook and copy its URL.
    • Create an outgoing webhook with your secret token.
  3. Point the outgoing webhook URL to your OpenClaw Gateway:
    • https://gateway-host/webhook/synology by default.
    • Or your custom channels.synology-chat.webhookPath.
    • Record that exact externally reachable HTTPS URL as channels.synology-chat.webhookUrl so the NAS can retrieve hosted attachments.
  4. Finish setup in OpenClaw. Synology Chat appears in the same channel setup list in both flows:
    • Guided: openclaw onboard or openclaw channels add
    • Direct: openclaw channels add --channel synology-chat --token <token> --url <incoming-webhook-url> --webhook-url <public-outgoing-webhook-url>
  5. Check openclaw channels status --probe, then send a DM to the Synology Chat bot. Start the Gateway if it is offline; config changes follow hot reload.

Webhook auth details:

  • OpenClaw accepts the outgoing webhook token from body.token, then ?token=..., then headers.
  • Accepted header forms:
    • x-synology-token
    • x-webhook-token
    • x-openclaw-token
    • Authorization: Bearer <token>
  • Empty or missing tokens are rejected.
  • Payloads may be application/x-www-form-urlencoded or application/json; token, user_id, and text are required.

Inbound durability

Once token, sender-policy, and rate-limit checks pass, OpenClaw strips the webhook token from the stored envelope, queues the event durably, and only then acknowledges it. The route returns 204 only after that append succeeds; a persistence failure returns 503 so Synology Chat can retry instead of silently losing the message. The durable 204 carries x-openclaw-delivery-accepted: durable; authentication, validation, and storage-error responses omit the marker, so reverse proxies can require it to distinguish durable acceptance from a generic response.

Pending or retryable events survive a Gateway restart. Synology's stable post_id suppresses duplicate queue entries while the corresponding active or retained completion record exists. Delivery remains at least once across the queue-to-agent handoff, so a crash at that boundary can still replay a turn.

Minimal config:

{
  channels: {
    "synology-chat": {
      enabled: true,
      token: "synology-outgoing-token",
      incomingUrl: "https://nas.example.com/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=2&token=...",
      webhookUrl: "https://gateway.example.com/webhook/synology",
      webhookPath: "/webhook/synology",
      dmPolicy: "allowlist",
      allowedUserIds: ["123456"],
      rateLimitPerMinute: 30,
      allowInsecureSsl: false,
    },
  },
}

Environment variables

For the default account, you can use env vars:

  • SYNOLOGY_CHAT_TOKEN
  • SYNOLOGY_CHAT_INCOMING_URL
  • SYNOLOGY_NAS_HOST
  • SYNOLOGY_ALLOWED_USER_IDS (comma-separated)
  • SYNOLOGY_RATE_LIMIT
  • OPENCLAW_BOT_NAME

Config values override env vars.

SYNOLOGY_CHAT_INCOMING_URL and SYNOLOGY_NAS_HOST cannot be set from a workspace .env; see Workspace .env files.

DM policy and access control

  • Supported dmPolicy values: allowlist (default), open, and disabled. Synology Chat has no pairing flow; approve senders by adding their numeric Synology user IDs to allowedUserIds.
  • allowedUserIds accepts a list (or comma-separated string) of Synology user IDs.
  • In allowlist mode, an empty allowedUserIds list is treated as misconfiguration and the webhook route will not start.
  • dmPolicy: "open" allows public DMs only when allowedUserIds includes "*"; with restrictive entries, only matching users can chat. open with an empty allowedUserIds list also refuses to start the route.
  • dmPolicy: "disabled" blocks DMs.
  • Reply recipient binding stays on stable numeric user_id by default. channels.synology-chat.dangerouslyAllowNameMatching: true is break-glass compatibility mode that re-enables mutable username/nickname lookup for reply delivery.

Outbound delivery

Targets are numeric Synology Chat user IDs. The prefixes synology-chat:, synology_chat:, and synology: are all accepted. Recipients that fail validation are rejected before anything is sent.

Examples:

openclaw message send --channel synology-chat --target 123456 --message "Hello from OpenClaw"
openclaw message send --channel synology-chat --target synology-chat:123456 --message "Hello again"
openclaw message send --channel synology-chat --target synology:123456 --message "Short prefix"

Outbound text is split into 2000-character chunks, and normal links are left whole. Attachment captions are subject to the same ceiling; any excess continues as text messages sent after the attachment. On a supported Chat Server release, leave Hide URL previews in conversations and channels turned on in the Synology Chat Admin Console.

When Synology hands back the recipient's post ID in data.succ.user_id_post_map, OpenClaw relies on it to confirm delivery of text and attachments. A split text counts as confirmed only if every chunk carries an ID. Acknowledgments that lack usable recipient-matched IDs stay unconfirmed even when the message shows up in Chat; OpenClaw never fabricates message IDs.

For attachments, the source is loaded by OpenClaw under its guarded outbound-media policy, the resulting bytes are frozen in bounded plugin-scoped SQLite state, and Synology is given a short-lived opaque HTTPS capability on the configured webhook route. The NAS sees only that OpenClaw-hosted URL, never the original remote or local media reference. Capabilities are scoped to an account and route, can be reused for delayed GET or HEAD requests within their ten-minute lifetime, and expire on their own. The file limit is 32 MB. Per account, no more than four attachment responses may be served at once and 128 MB per minute is allowed; stalled responses are closed after two minutes. Byte-range responses are not advertised.

webhookUrl and webhookPath serve different purposes:

  • webhookUrl is the exact externally reachable HTTPS callback configured in Synology Chat. Its public origin, path, and existing query string are what OpenClaw uses when creating attachment capabilities.
  • webhookPath is the internal Gateway route. A reverse proxy may map the public URL onto this route, but only this plugin path should be exposed, not the general Gateway HTTP surface.
  • incomingUrl works the other way: OpenClaw uses it to post replies to the NAS.

OpenClaw never derives the public URL from Host or X-Forwarded-* headers, and it never falls back to forwarding the original source URL. Should webhookUrl be missing or invalid, inbound messages and outbound text keep working, but attachment sends fail with an actionable setup error.

Multi-account

Several Synology Chat accounts can run under channels.synology-chat.accounts. Token, incoming URL, public webhook URL, webhook path, DM policy, and limits may each be overridden per account. Direct-message sessions are isolated by account and user, so the same numeric user_id on two different Synology accounts does not share transcript state. Outbound sends are paced separately for every incoming webhook URL, token included. Text and attachments going to the same webhook share the 500 ms minimum interval; a burst on one webhook does not hold up another. Every enabled account needs its own distinct webhookPath. OpenClaw rejects duplicate exact paths and refuses to start named accounts that merely inherit a shared webhook path in multi-account setups. If legacy inheritance for a named account is genuinely needed, set dangerouslyAllowInheritedWebhookPath: true on that account or at channels.synology-chat, though duplicate exact paths are still rejected. Explicit per-account paths are preferred.

{
  channels: {
    "synology-chat": {
      enabled: true,
      accounts: {
        default: {
          token: "token-a",
          incomingUrl: "https://nas-a.example.com/...token=...",
          webhookUrl: "https://gateway.example.com/webhook/synology",
        },
        alerts: {
          token: "token-b",
          incomingUrl: "https://nas-b.example.com/...token=...",
          webhookUrl: "https://gateway.example.com/webhook/synology-alerts",
          webhookPath: "/webhook/synology-alerts",
          dmPolicy: "allowlist",
          allowedUserIds: ["987654"],
        },
      },
    },
  },
}

Security notes

  • Treat token as secret and rotate it if it leaks.
  • Leave allowInsecureSsl: false enabled unless you explicitly trust a self-signed local NAS cert.
  • Inbound webhook requests are verified by token and rate-limited per sender (rateLimitPerMinute, default 30).
  • Invalid token checks compare secrets in constant time and reject the request; repeated invalid-token attempts temporarily lock out the source IP.
  • Inbound message text is sanitized against known prompt-injection patterns and truncated at 4000 characters.
  • dmPolicy: "allowlist" is preferred for production.
  • Leave dangerouslyAllowNameMatching off unless you explicitly need legacy username-based reply delivery.
  • Leave dangerouslyAllowInheritedWebhookPath off unless you explicitly accept shared-path routing risk in a multi-account setup.
  • Attachment capability tokens can end up in reverse-proxy access logs. Disable query-string logging or redact __openclaw_synology_media_token_* parameters, and keep application logs free of full capability URLs.
  • Hosted attachments use Content-Disposition: attachment, X-Content-Type-Options: nosniff, and Cache-Control: no-store. Files declared or named as HTML, SVG, or XML are rejected. Frozen bytes that begin as a UTF-8, UTF-16, or UTF-32 markup document after an optional encoding marker, whitespace, and comments are also rejected; literal tags appearing later in passive text or source files do not turn those files into active documents.

Troubleshooting

  • Missing required fields (token, user_id, text):
    • one of the required fields is absent from the outgoing webhook payload
    • if Synology sends the token in headers, make sure the gateway/proxy preserves those headers
  • Invalid token:
    • the outgoing webhook secret does not match channels.synology-chat.token
    • the request is hitting the wrong account/webhook path
    • a reverse proxy stripped the token header before the request reached OpenClaw
  • Rate limit exceeded:
    • too many invalid token attempts from the same source can temporarily lock that source out
    • authenticated senders also have a separate per-user message rate limit
  • Allowlist is empty. Configure allowedUserIds or use dmPolicy=open with allowedUserIds=["*"].:
    • dmPolicy="allowlist" is enabled but no users are configured
  • User not authorized:
    • the sender's numeric user_id is not in allowedUserIds
  • Synology Chat attachments require webhookUrl:
    • set the account's exact externally reachable HTTPS outgoing-webhook callback URL
    • confirm the reverse proxy maps only that public route to webhookPath
    • text and inbound messaging remain available while attachment setup is incomplete
1,589 words · updated Oct 25, 2026