docs/book/src/channels/nextcloud-talk.md
Nextcloud Talk integration via the Talk Bot webhook protocol. Self-hosted, federated, and E2E-capable: another sovereign-communication option alongside Matrix and Mattermost.
{{#peer-group nextcloud}}
POST /nextcloud-talk/<alias> on the gateway (bare /nextcloud-talk still works as a deprecated fallback)Nextcloud server 27.1 or later with Talk 17.1 or later. This is a hard
minimum, not a recommendation: the signed Talk Bot API this integration uses to
send replies was introduced in Talk 17.1, and occ talk:bot:install below is
unavailable on earlier releases.
Bot installed with both the webhook and response features, which let
Nextcloud deliver room messages to ZeroClaw and let ZeroClaw send replies:
sudo -u www-data php occ talk:bot:install \
-f webhook -f response \
zeroclaw-bot '<shared-secret>' \
'https://<your-public-url>/nextcloud-talk/<alias>'
Bot secret from that installation. Nextcloud issues one shared secret per
bot, used both to verify inbound webhook signatures and to sign outbound bot-API
replies. Set it as webhook_secret, which is canonical. bot_token is a
deprecated alias for the same value: if both are set they must be identical.
It cannot hold a different outbound secret. Conflicting non-empty values are
not silently resolved in favour of one; the conflict is logged and the alias
resolves to no secret, so the channel then behaves exactly as if unconfigured:
inbound 401, no outbound send.
Publicly-reachable gateway: see Setup → Container for tunnel options if self-hosted
Both directions fail closed on a missing secret, and there is no unauthenticated mode:
401 and never reaches the agent. There is no
"public" mode that accepts unverified webhooks.Upgrading is a breaking change. A deployment that previously ran without a secret accepted webhooks; it now rejects every one of them with
401. Install the bot withocc talk:bot:install, then set that secret aswebhook_secretbefore upgrading, or inbound messages stop being processed.
{{#config-fields channels.nextcloud_talk}}
The channel is read from the default alias. Set it through any config surface:
{{#config-where channels nextcloud_talk}}
webhook_secret can also be supplied at runtime via the generic env override {{#env-var-name channels.nextcloud_talk.default.webhook_secret}}, useful for rotating it without editing the config.
app_token is deprecated and unused (replies no longer go through OCS bearer auth); it's only still accepted so old configs that set it don't fail to parse.
zeroclaw daemon
Configure your Talk bot's webhook URL to point at the alias of the
[channels.nextcloud_talk.<alias>] instance that should receive it:
https://<your-public-url>/nextcloud-talk/<alias>
For example, [channels.nextcloud_talk.work] receives POST /nextcloud-talk/work.
This per-alias routing (#6312) lets you run several Talk bots side by side and
deliver each one's webhooks to the right instance.
The bare https://<your-public-url>/nextcloud-talk path still works but is
deprecated: it resolves to the lexicographically-first alias (deterministic
across restarts) and returns an X-Zeroclaw-Deprecation response header.
Single-instance deployments can keep using it unchanged. An unknown alias returns 404.
Local development? Configure [tunnel] in your config (ngrok, Cloudflare, or Tailscale) and the gateway exposes itself on startup: see Operations → Network deployment.
Inbound requests must carry:
X-Nextcloud-Talk-Random headerX-Nextcloud-Talk-Signature headerZeroClaw verifies:
expected_sig = hex(hmac_sha256(secret, random + raw_request_body))
if X-Nextcloud-Talk-Signature != expected_sig:
return 401
Without a resolved secret, ZeroClaw returns 401 before parsing or dispatching
the webhook. There is no mode that accepts an unverified request.
actorType = "bots") are ignored: prevents feedback loopstoken in the webhook payloadexternal_peers = ["*"] in the peer group for first-time testing["alice", "bob"])404 Nextcloud Talk not configured: [channels.nextcloud_talk.default] section missing or enabled = false401 Invalid signature: secret mismatch, wrong random header, or body-signing bug. Check the raw body is being signed (not the parsed JSON)200: event was filtered. Check logs for "actorType = bots" or a sender not in the peer setNextcloud Talk does not support message edits via the Bot API, so streaming draft updates are disabled for this channel. Replies are sent on stream completion only.
webhook_secret/bot_token), not a bearer token; there is no separate OCS bearer credential to manageproxy_url to override the global [proxy] setting for Nextcloud Talk only (http://, https://, socks5://, socks5h://)