
OpenClaw Integration with Matrix

By Sarah Jenkins


By Sarah Jenkins
Matrix is the Matrix channel plugin for OpenClaw. It uses the official matrix-js-sdk and supports DMs, rooms, threads, media, reactions, polls, location, and E2EE.
Matrix is a plugin and is not bundled with core OpenClaw.
Install from npm:
openclaw plugins install @openclaw/matrixInstall from a local checkout:
openclaw plugins install ./path/to/local/matrix-pluginchannels.matrix with either:Interactive setup paths:
openclaw channels add
openclaw configure --section channelsWhat the Matrix wizard actually asks for:
Wizard behavior that matters:
enabled: true for that account.Ops Bot becomes ops-bot.@user:server values immediately. Display names only work when live directory lookup finds one exact match; otherwise the wizard asks you to retry with a full Matrix ID.!room:server or #alias:server.openclaw channels resolve --channel matrix "Project Room".Minimal token-based setup:
{
channels: {
matrix: {
enabled: true,
homeserver: "https://matrix.example.org",
accessToken: "syt_xxx",
dm: { policy: "pairing" },
},
},
}Password-based setup (token is cached after login):
{
channels: {
matrix: {
enabled: true,
homeserver: "https://matrix.example.org",
userId: "@bot:example.org",
password: "replace-me", // pragma: allowlist secret
deviceName: "OpenClaw Gateway",
},
},
}Matrix stores cached credentials in ~/.openclaw/credentials/matrix/. The default account uses credentials.json; named accounts use credentials-<account>.json.
Environment variable equivalents (used when the config key is not set):
MATRIX_HOMESERVERMATRIX_ACCESS_TOKENMATRIX_USER_IDMATRIX_PASSWORDMATRIX_DEVICE_IDMATRIX_DEVICE_NAMEFor non-default accounts, use account-scoped env vars:
MATRIX_<ACCOUNT_ID>_HOMESERVERMATRIX_<ACCOUNT_ID>_ACCESS_TOKENMATRIX_<ACCOUNT_ID>_USER_IDMATRIX_<ACCOUNT_ID>_PASSWORDMATRIX_<ACCOUNT_ID>_DEVICE_IDMATRIX_<ACCOUNT_ID>_DEVICE_NAMEExample for account ops:
MATRIX_OPS_HOMESERVERMATRIX_OPS_ACCESS_TOKENFor normalized account ID ops-bot, use:
MATRIX_OPS_BOT_HOMESERVERMATRIX_OPS_BOT_ACCESS_TOKENThe interactive wizard only offers the env-var shortcut when those auth env vars are already present and the selected account does not already have Matrix auth saved in config.
This is a practical baseline config with DM pairing, room allowlist, and E2EE enabled:
{
channels: {
matrix: {
enabled: true,
homeserver: "https://matrix.example.org",
accessToken: "syt_xxx",
encryption: true,
dm: {
policy: "pairing",
},
groupPolicy: "allowlist",
groupAllowFrom: ["@admin:example.org"],
groups: {
"!roomid:example.org": {
requireMention: true,
},
},
autoJoin: "allowlist",
autoJoinAllowlist: ["!roomid:example.org"],
threadReplies: "inbound",
replyToMode: "off",
streaming: "partial",
},
},
}Matrix reply streaming is opt-in.
Set channels.matrix.streaming to "partial" when you want OpenClaw to send a single draft reply, edit that draft in place while the model is generating text, and then finalize it when the reply is done:
{
channels: {
matrix: {
streaming: "partial",
},
},
}streaming: "off" is the default. OpenClaw waits for the final reply and sends it once.streaming: "partial" creates one editable preview message instead of sending multiple partial messages.In encrypted (E2EE) rooms, outbound image events use thumbnail_file so image previews are encrypted alongside the full attachment. Unencrypted rooms still use plain thumbnail_url. No configuration is needed — the plugin detects E2EE state automatically.
By default, Matrix messages from other configured OpenClaw Matrix accounts are ignored.
Use allowBots when you intentionally want inter-agent Matrix traffic:
{
channels: {
matrix: {
allowBots: "mentions", // true | "mentions"
groups: {
"!roomid:example.org": {
requireMention: true,
},
},
},
},
}allowBots: true accepts messages from other configured Matrix bot accounts in allowed rooms and DMs.allowBots: "mentions" accepts those messages only when they visibly mention this bot in rooms. DMs are still allowed.groups.<room>.allowBots overrides the account-level setting for one room.Use strict room allowlists and mention requirements when enabling bot-to-bot traffic in shared rooms.
Enable encryption:
{
channels: {
matrix: {
enabled: true,
homeserver: "https://matrix.example.org",
accessToken: "syt_xxx",
encryption: true,
dm: { policy: "pairing" },
},
},
}Check verification status:
openclaw matrix verify statusVerbose status (full diagnostics):
openclaw matrix verify status --verboseInclude the stored recovery key in machine-readable output:
openclaw matrix verify status --include-recovery-key --jsonBootstrap cross-signing and verification state:
openclaw matrix verify bootstrapMulti-account support: use channels.matrix.accounts with per-account credentials and optional name.
Verbose bootstrap diagnostics:
openclaw matrix verify bootstrap --verboseForce a fresh cross-signing identity reset before bootstrapping:
openclaw matrix verify bootstrap --force-reset-cross-signingVerify this device with a recovery key:
openclaw matrix verify device "<your-recovery-key>"Verbose device verification details:
openclaw matrix verify device "<your-recovery-key>" --verboseCheck room-key backup health:
openclaw matrix verify backup statusVerbose backup health diagnostics:
openclaw matrix verify backup status --verboseRestore room keys from server backup:
openclaw matrix verify backup restoreVerbose restore diagnostics:
openclaw matrix verify backup restore --verboseDelete the current server backup and create a fresh backup baseline:
openclaw matrix verify backup reset --yesAll verify commands are concise by default (including quiet internal SDK logging) and show detailed diagnostics only with --verbose. Use --json for full machine-readable output when scripting.
In multi-account setups, Matrix CLI commands use the implicit Matrix default account unless you pass --account <id>. If you configure multiple named accounts, set channels.matrix.defaultAccount first or those implicit CLI operations will stop and ask you to choose an account explicitly. Use --account whenever you want verification or device operations to target a named account explicitly:
openclaw matrix verify status --account assistant
openclaw matrix verify backup restore --account assistant
openclaw matrix devices list --account assistantWhen encryption is disabled or unavailable for a named account, Matrix warnings and verification errors point at that account's config key, for example channels.matrix.accounts.assistant.encryption.
OpenClaw treats this Matrix device as verified only when it is verified by your own cross-signing identity. In practice, openclaw matrix verify status --verbose exposes three trust signals:
Locally trusted: this device is trusted by the current client onlyCross-signing verified: the SDK reports the device as verified through cross-signingSigned by owner: the device is signed by your own self-signing keyVerified by owner becomes yes only when cross-signing verification or owner-signing is present. Local trust by itself is not enough for OpenClaw to treat the device as fully verified.
openclaw matrix verify bootstrap is the repair and setup command for encrypted Matrix accounts. It does all of the following in order:
If the homeserver requires interactive auth to upload cross-signing keys, OpenClaw tries the upload without auth first, then with m.login.dummy, then with m.login.password when channels.matrix.password is configured.
Use --force-reset-cross-signing only when you intentionally want to discard the current cross-signing identity and create a new one.
If you intentionally want to discard the current room-key backup and start a new backup baseline for future messages, use openclaw matrix verify backup reset --yes. Do this only when you accept that unrecoverable old encrypted history will stay unavailable.
If you want to keep future encrypted messages working and accept losing unrecoverable old history, run these commands in order:
openclaw matrix verify backup reset --yes
openclaw matrix verify backup status --verbose
openclaw matrix verify statusAdd --account <id> to each command when you want to target a named Matrix account explicitly.
When encryption: true, Matrix defaults startupVerification to "if-unverified". On startup, if this device is still unverified, Matrix will request self-verification in another Matrix client, skip duplicate requests while one is already pending, and apply a local cooldown before retrying after restarts. Failed request attempts retry sooner than successful request creation by default. Set startupVerification: "off" to disable automatic startup requests, or tune startupVerificationCooldownHours if you want a shorter or longer retry window.
Startup also performs a conservative crypto bootstrap pass automatically. That pass tries to reuse the current secret storage and cross-signing identity first, and avoids resetting cross-signing unless you run an explicit bootstrap repair flow.
If startup finds broken bootstrap state and channels.matrix.password is configured, OpenClaw can attempt a stricter repair path. If the current device is already owner-signed, OpenClaw preserves that identity instead of resetting it automatically.
Upgrading from the previous public Matrix plugin:
~/Backups/openclaw-migrations/.channels.matrix.defaultAccount before upgrading from the old flat-store layout so OpenClaw knows which account should receive that shared legacy state.openclaw doctor --fix will import it into the new recovery-key flow automatically.Encrypted runtime state is organized under per-account, per-user token-hash roots in ~/.openclaw/matrix/accounts/<account>/<homeserver>__<user>/<token-hash>/. That directory contains the sync store (bot-storage.json), crypto store (crypto/), recovery key file (recovery-key.json), IndexedDB snapshot (crypto-idb-snapshot.json), thread bindings (thread-bindings.json), and startup verification state (startup-verification.json) when those features are in use. When the token changes but the account identity stays the same, OpenClaw reuses the best existing root for that account/homeserver/user tuple so prior sync state, crypto state, thread bindings, and startup verification state remain visible.
Matrix E2EE in this plugin uses the official matrix-js-sdk Rust crypto path in Node. That path expects IndexedDB-backed persistence when you want crypto state to survive restarts.
OpenClaw currently provides that in Node by:
fake-indexeddb as the IndexedDB API shim expected by the SDKcrypto-idb-snapshot.json before initRustCryptocrypto-idb-snapshot.json after init and during runtimeThis is compatibility/storage plumbing, not a custom crypto implementation. The snapshot file is sensitive runtime state and is stored with restrictive file permissions. Under OpenClaw's security model, the gateway host and local OpenClaw state directory are already inside the trusted operator boundary, so this is primarily an operational durability concern rather than a separate remote trust boundary.
Planned improvement:
Update the Matrix self-profile for the selected account with:
openclaw matrix profile set --name "OpenClaw Assistant"
openclaw matrix profile set --avatar-url https://cdn.example.org/avatar.pngAdd --account <id> when you want to target a named Matrix account explicitly.
Matrix accepts mxc:// avatar URLs directly. When you pass an http:// or https:// avatar URL, OpenClaw uploads it to Matrix first and stores the resolved mxc:// URL back into channels.matrix.avatarUrl (or the selected account override).
Matrix now posts verification lifecycle notices directly into the strict DM verification room as m.notice messages. That includes:
Incoming verification requests from another Matrix client are tracked and auto-accepted by OpenClaw. For self-verification flows, OpenClaw also starts the SAS flow automatically when emoji verification becomes available and confirms its own side. For verification requests from another Matrix user/device, OpenClaw auto-accepts the request and then waits for the SAS flow to proceed normally. You still need to compare the emoji or decimal SAS in your Matrix client and confirm "They match" there to complete the verification.
OpenClaw does not auto-accept self-initiated duplicate flows blindly. Startup skips creating a new request when a self-verification request is already pending.
Verification protocol/system notices are not forwarded to the agent chat pipeline, so they do not produce NO_REPLY.
Old OpenClaw-managed Matrix devices can accumulate on the account and make encrypted-room trust harder to reason about. List them with:
openclaw matrix devices listRemove stale OpenClaw-managed devices with:
openclaw matrix devices prune-staleIf direct-message state gets out of sync, OpenClaw can end up with stale m.direct mappings that point at old solo rooms instead of the live DM. Inspect the current mapping for a peer with:
openclaw matrix direct inspect --user-id @alice:example.orgRepair it with:
openclaw matrix direct repair --user-id @alice:example.orgRepair keeps the Matrix-specific logic inside the plugin:
m.directm.direct to point at itThe repair flow does not delete old rooms automatically. It only picks the healthy DM and updates the mapping so new Matrix sends, verification notices, and other direct-message flows target the right room again.
Matrix supports native Matrix threads for both automatic replies and message-tool sends.
threadReplies: "off" keeps replies top-level.threadReplies: "inbound" replies inside a thread only when the inbound message was already in that thread.threadReplies: "always" keeps room replies in a thread rooted at the triggering message.threadId is provided./focus, /unfocus, /agents, /session idle, /session max-age, and thread-bound /acp spawn now work in Matrix rooms and DMs./focus creates a new Matrix thread and binds it to the target session when threadBindings.spawnSubagentSessions=true./focus or /acp spawn --thread here inside an existing Matrix thread binds that current thread instead.Matrix rooms, DMs, and existing Matrix threads can be turned into durable ACP workspaces without changing the chat surface.
Fast operator flow:
/acp spawn codex --bind here inside the Matrix DM, room, or existing thread you want to keep using.--bind here binds that current thread in place./new and /reset reset the same bound ACP session in place./acp close closes the ACP session and removes the binding.Notes:
--bind here does not create a child Matrix thread.threadBindings.spawnAcpSessions is only required for /acp spawn --thread auto|here, where OpenClaw needs to create or bind a child Matrix thread.Matrix inherits global defaults from session.threadBindings, and also supports per-channel overrides:
threadBindings.enabledthreadBindings.idleHoursthreadBindings.maxAgeHoursthreadBindings.spawnSubagentSessionsthreadBindings.spawnAcpSessionsMatrix thread-bound spawn flags are opt-in:
threadBindings.spawnSubagentSessions: true to allow top-level /focus to create and bind new Matrix threads.threadBindings.spawnAcpSessions: true to allow /acp spawn --thread auto|here to bind ACP sessions to Matrix threads.Matrix supports outbound reaction actions, inbound reaction notifications, and inbound ack reactions.
channels["matrix"].actions.reactions.react adds a reaction to a specific Matrix event.reactions lists the current reaction summary for a specific Matrix event.emoji="" removes the bot account's own reactions on that event.remove: true removes only the specified emoji reaction from the bot account.Ack reactions use the standard OpenClaw resolution order:
channels["matrix"].accounts.<accountId>.ackReactionchannels["matrix"].ackReactionmessages.ackReactionAck reaction scope resolves in this order:
channels["matrix"].accounts.<accountId>.ackReactionScopechannels["matrix"].ackReactionScopemessages.ackReactionScopeReaction notification mode resolves in this order:
channels["matrix"].accounts.<accountId>.reactionNotificationschannels["matrix"].reactionNotificationsownCurrent behavior:
reactionNotifications: "own" forwards added m.reaction events when they target bot-authored Matrix messages.reactionNotifications: "off" disables reaction system events.m.reaction removals.{
channels: {
matrix: {
dm: {
policy: "allowlist",
allowFrom: ["@admin:example.org"],
},
groupPolicy: "allowlist",
groupAllowFrom: ["@admin:example.org"],
groups: {
"!roomid:example.org": {
requireMention: true,
},
},
},
},
}Pairing example for Matrix DMs:
openclaw pairing list matrix
openclaw pairing approve matrix <CODE>If an unapproved Matrix user keeps messaging you before approval, OpenClaw reuses the same pending pairing code and may send a reminder reply again after a short cooldown instead of minting a new code.
{
channels: {
matrix: {
enabled: true,
defaultAccount: "assistant",
dm: { policy: "pairing" },
accounts: {
assistant: {
homeserver: "https://matrix.example.org",
accessToken: "syt_assistant_xxx",
encryption: true,
},
alerts: {
homeserver: "https://matrix.example.org",
accessToken: "syt_alerts_xxx",
dm: {
policy: "allowlist",
allowFrom: ["@ops:example.org"],
},
},
},
},
},
}Top-level channels.matrix values act as defaults for named accounts unless an account overrides them. Set defaultAccount when you want OpenClaw to prefer one named Matrix account for implicit routing, probing, and CLI operations. If you configure multiple named accounts, set defaultAccount or pass --account <id> for CLI commands that rely on implicit account selection. Pass --account <id> to openclaw matrix verify ... and openclaw matrix devices ... when you want to override that implicit selection for one command.
By default, OpenClaw blocks private/internal Matrix homeservers for SSRF protection unless you explicitly opt in per account.
If your homeserver runs on localhost, a LAN/Tailscale IP, or an internal hostname, enable allowPrivateNetwork for that Matrix account:
{
channels: {
matrix: {
homeserver: "http://matrix-synapse:8008",
allowPrivateNetwork: true,
accessToken: "syt_internal_xxx",
},
},
}CLI setup example:
openclaw matrix account add \
--account ops \
--homeserver http://matrix-synapse:8008 \
--allow-private-network \
--access-token syt_ops_xxxThis opt-in only allows trusted private/internal targets. Public cleartext homeservers such as http://matrix.example.org:8008 remain blocked. Prefer https:// whenever possible.
Matrix accepts these target forms anywhere OpenClaw asks you for a room or user target:
@user:server, user:@user:server, or matrix:user:@user:server!room:server, room:!room:server, or matrix:room:!room:server#alias:server, channel:#alias:server, or matrix:channel:#alias:serverLive directory lookup uses the logged-in Matrix account:
enabled: enable or disable the channel.name: optional label for the account.defaultAccount: preferred account ID when multiple Matrix accounts are configured.homeserver: homeserver URL, for example https://matrix.example.org.allowPrivateNetwork: allow this Matrix account to connect to private/internal homeservers. Enable this when the homeserver resolves to localhost, a LAN/Tailscale IP, or an internal host such as matrix-synapse.userId: full Matrix user ID, for example @bot:example.org.accessToken: access token for token-based auth. Plaintext values and SecretRef values are supported for channels.matrix.accessToken and channels.matrix.accounts.<id>.accessToken across env/file/exec providers.password: password for password-based login. Plaintext values and SecretRef values are supported.deviceId: explicit Matrix device ID.deviceName: device display name for password login.avatarUrl: stored self-avatar URL for profile sync and set-profile updates.initialSyncLimit: startup sync event limit.encryption: enable E2EE.allowlistOnly: force allowlist-only behavior for DMs and rooms.groupPolicy: open, allowlist, or disabled.groupAllowFrom: allowlist of user IDs for room traffic.groupAllowFrom entries should be full Matrix user IDs. Unresolved names are ignored at runtime.replyToMode: off, first, or all.streaming: off (default) or partial. partial enables single-message draft previews with edit-in-place updates.threadReplies: off, inbound, or always.threadBindings: per-channel overrides for thread-bound session routing and lifecycle.startupVerification: automatic self-verification request mode on startup (if-unverified, off).startupVerificationCooldownHours: cooldown before retrying automatic startup verification requests.textChunkLimit: outbound message chunk size.chunkMode: length or newline.responsePrefix: optional message prefix for outbound replies.ackReaction: optional ack reaction override for this channel/account.ackReactionScope: optional ack reaction scope override (group-mentions, group-all, direct, all, none, off).reactionNotifications: inbound reaction notification mode (own, off).mediaMaxMb: media size cap in MB for Matrix media handling. It applies to outbound sends and inbound media processing.autoJoin: invite auto-join policy (always, allowlist, off). Default: off.autoJoinAllowlist: rooms/aliases allowed when autoJoin is allowlist. Alias entries are resolved to room IDs during invite handling; OpenClaw does not trust alias state claimed by the invited room.dm: DM policy block (enabled, policy, allowFrom).dm.allowFrom entries should be full Matrix user IDs unless you already resolved them through live directory lookup.accounts: named per-account overrides. Top-level channels.matrix values act as defaults for these entries.groups: per-room policy map. Prefer room IDs or aliases; unresolved room names are ignored at runtime. Session/group identity uses the stable room ID after resolution, while human-readable labels still come from room names.rooms: legacy alias for groups.actions: per-action tool gating (messages, reactions, pins, profile, memberInfo, channelInfo, verification).About the author

Sarah Jenkins is a seasoned OpenClaw developer with a strong focus on optimizing high-performance computing solutions. Her work primarily involves crafting efficient parallel algorithms and enhancing GPU acceleration for complex scientific simulations. Jenkins is renowned for her meticulous attention to detail and her ability to translate intricate theoretical concepts into practical, robust OpenClaw implementations.