#mattermost callback

1 messages · Page 1 of 1 (latest)

vagrant inlet
#

Here is my mattermost configuration

"mattermost": {
"enabled": true,
"botToken": "7eeejrat478p7mbsc1ktjgcdwy",
"baseUrl": "https://external.host.com",
"dmPolicy": "pairing",
"commands": {
"native": true,
"nativeSkills": true,
"callbackPath": "/api/channels/mattermost/command",
"callbackUrl": "http://internal-host:18789/api/channels/mattermost/command"
}

And the slash commands are not working. I did see this in logs, and wondering why it thinks it is localhost

mattermost: interactions callbackUrl resolved to http://localhost:18789/mattermost/interactions/default (loopback) while baseUrl is https://external.host.com. This MAY be unreachable depending on your deployment. If button clicks don't work, set channels.mattermost.interactions.callbackBaseUrl to a URL reachable from the Mattermost server (e.g. your public reverse proxy URL).

cerulean gustBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

glacial barnBOT
#

First: rotate that Mattermost bot token — it’s now posted in a public/indexed Discord thread.

The localhost warning is probably not your slash-command callback. It says:

/mattermost/interactions/default

That is the interactive buttons/model-picker callback, separate from:

/api/channels/mattermost/command

For slash commands, check logs for something like:

mattermost: slash commands registered (... callback=...)

or:

mattermost: failed to register slash commands...

#

Your slash callback must be reachable from the Mattermost server. Test from the Mattermost host/container:

curl -i http://internal-host:18789/api/channels/mattermost/command

A GET should return 405 Method Not Allowed from OpenClaw. If it times out/404s, use a public/reverse-proxied gateway URL instead:

commands: {
  native: true,
  nativeSkills: true,
  callbackPath: "/api/channels/mattermost/command",
  callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
}
#

If you also want to fix the localhost interactions warning:

interactions: {
  callbackBaseUrl: "https://gateway.example.com",
  allowedSourceIps: ["<mattermost-server-or-proxy-ip>"],
}

If using an internal/private host, Mattermost may also need ServiceSettings.AllowedUntrustedInternalConnections set to the host/domain only, not a full URL.

Docs: https://docs.openclaw.ai/channels/mattermost

Also please paste openclaw --version if it still fails — Mattermost support has moved a bit recently.

vagrant inlet
#

using my current configuration, what do I need for callbackBaseUrl

glacial barnBOT
#

Use the base URL of the OpenClaw gateway as Mattermost can reach it, with no path.

With your current slash callback:

callbackUrl: "http://internal-host:18789/api/channels/mattermost/command"

the matching interactions config would likely be:

interactions: {
  callbackBaseUrl: "http://internal-host:18789",
  allowedSourceIps: ["<mattermost-server-ip-or-reverse-proxy-ip>"],
}
#

OpenClaw will append the path itself, producing:

http://internal-host:18789/mattermost/interactions/default

Don’t use:

callbackBaseUrl: "http://internal-host:18789/api/channels/mattermost/command"

because that’s only for slash commands.

Also don’t use https://external.host.com unless that host reverse-proxies OpenClaw’s /mattermost/interactions/... path to your gateway. And please rotate the bot token you pasted. 🦐

vagrant inlet
#

ok I do alrady have MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS=100.76.32.93 on my mattermost server, but I do see this in openclaw now.

[security] blocked URL fetch (mattermost-api) targetOrigin=http://100.111.228.114:8065 reason=Blocked hostname or private/internal/special-use IP address
2026-04-28T14:02:23.210-04:00 [mattermost] API auth failed: SsrFBlockedError: Blocked hostname or private/internal/special-use IP address
2026-04-28T14:02:30.965-04:00 [security] blocked URL fetch (mattermost-api) targetOrigin=http://100.111.228.114:8065 reason=Blocked hostname or private/internal/special-use IP address
2026-04-28T14:02:30.967-04:00 [mattermost] API auth failed: SsrFBlockedError: Blocked hostname or private/internal/special-use IP address

glacial barnBOT
#

That log is the other direction.

  • MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS=100.76.32.93 = lets Mattermost call OpenClaw at that private/tailnet IP.
  • The OpenClaw log shows OpenClaw calling Mattermost at http://100.111.228.114:8065, and OpenClaw’s SSRF guard is blocking that private IP.

Add this to your OpenClaw Mattermost config:

channels: {
  mattermost: {
    enabled: true,
    botToken: "...",
    baseUrl: "http://100.111.228.114:8065", // or your current MM URL
    dmPolicy: "pairing",

    network: {
#
      dangerouslyAllowPrivateNetwork: true,
    },

    commands: {
      native: true,
      nativeSkills: true,
      callbackPath: "/api/channels/mattermost/command",
      callbackUrl: "http://100.76.32.93:18789/api/channels/mattermost/command",
    },

    interactions: {
      callbackBaseUrl: "http://100.76.32.93:18789",
      allowedSourceIps: ["100.111.228.114"],
    },
  },
#
}

Use your actual IPs, but conceptually:

  • baseUrl / network.dangerouslyAllowPrivateNetwork → OpenClaw → Mattermost
  • commands.callbackUrl + interactions.callbackBaseUrl → Mattermost → OpenClaw
  • interactions.allowedSourceIps → the Mattermost server/proxy IP that will hit OpenClaw

Then restart the gateway. Also please paste openclaw --version if it still fails.

vagrant inlet
#

ok getting better, now when I do /oc_status in mattermost I get "Slash commands are not configured for this channel (no allowlist)."

glacial barnBOT
#

Nice, that means the callback is reaching OpenClaw now 🎉

That error is channel access control, not networking. Mattermost channel/group commands default to:

groupPolicy: "allowlist"

So add a sender allowlist for the Mattermost user(s) allowed to run /oc_* in channels:

mattermost: {
  enabled: true,
  botToken: "...",
  baseUrl: "http://100.111.228.114:8065",
  dmPolicy: "pairing",
#

  groupPolicy: "allowlist",
  groupAllowFrom: ["<your-mattermost-user-id>"],

  network: {
    dangerouslyAllowPrivateNetwork: true,
  },

  commands: {
    native: true,
    nativeSkills: true,
    callbackPath: "/api/channels/mattermost/command",
    callbackUrl: "http://100.76.32.93:18789/api/channels/mattermost/command",
  },
#
  interactions: {
    callbackBaseUrl: "http://100.76.32.93:18789",
    allowedSourceIps: ["100.111.228.114"],
  },
}

Use the Mattermost user ID, not the display name. DM pairing doesn’t automatically allow that user in channels.

If you intentionally want anyone in Mattermost channels to be able to use it, use:

groupAllowFrom: ["*"]

Restart the gateway after editing. Docs: https://docs.openclaw.ai/channels/mattermost

vagrant inlet
#

great that worked, last thing is threads. How do handle threads in mattermost?

glacial barnBOT
#

Yep — add replyToMode at the Mattermost account level.

Recommended:

mattermost: {
  // ...
  replyToMode: "all",
}

Modes:

  • "off" — no auto-threading/default behavior
  • "first" — top-level channel/group message starts a thread; follow-ups stay in that thread
  • "all" — recommended; for Mattermost this is effectively the same as first right now
  • DMs ignore this and stay non-threaded
#

So your config would include:

groupPolicy: "allowlist",
groupAllowFrom: ["<your-mattermost-user-id>"],
replyToMode: "all",

Threaded conversations get their own session keyed by the Mattermost thread root, so separate channel threads won’t all bleed into one shared channel session.

Docs: https://docs.openclaw.ai/channels/mattermost

Glad it’s working now 🎉 If this solved it, please right-click the solution message → Apps → ✅ Mark Solution so Answer Overflow indexes it for future users.

vagrant inlet
#

ok lets say I want to start a thread with agent2, can I do that easily, or do I have to setup bindings, etc.

glacial barnBOT
#

Short version: threads are easy; per-thread agent switching is not currently an ad-hoc /oc_* thing for Mattermost.

With:

replyToMode: "all"

each Mattermost thread gets its own session, but it runs on the agent selected by routing/bindings for that channel/account.

If you want a whole Mattermost channel to use agent2, add a binding:

bindings: [
  {
    agentId: "agent2",
#
    match: {
      channel: "mattermost",
      accountId: "default",
      peer: { kind: "channel", id: "<mattermost-channel-id>" },
    },
  },
  {
    agentId: "main",
    match: { channel: "mattermost", accountId: "default" },
  },
]

Then any thread started in that channel routes to agent2.

If you mean “same Mattermost channel, but this one specific thread should use agent2,” I don’t think Mattermost has the Discord/Telegram-style /focus thread binding path wired for that. Best options are:

vagrant inlet
#

Can I use #channel-name in binding? Or do I need to use channel-id? Can I use #channel-id in openclaw message send right?

glacial barnBOT
#

For bindings, use the actual Mattermost channel ID, not #channel-name.

Example:

bindings: [
  {
    agentId: "agent2",
    match: {
      channel: "mattermost",
      accountId: "default",
      peer: { kind: "channel", id: "<mattermost-channel-id>" },
    },
  },
]
#

For private Mattermost channels, use:

peer: { kind: "group", id: "<mattermost-channel-id>" }

For openclaw message send:

  • #channel-name ✅ works via Mattermost directory lookup
  • channel:<channel-id> ✅ best/deterministic for IDs
  • #channel-id ❌ don’t use this; #... means “channel name”, not ID
  • bare channel IDs can be ambiguous, so prefer channel:<id>

Docs: https://docs.openclaw.ai/channels/mattermost and https://docs.openclaw.ai/concepts/multi-agent

vagrant inlet
#

how to get the channel-id of a mattermost channel?

glacial barnBOT
#

Easiest ways:

Option 1: use OpenClaw logs
Run:

openclaw logs --follow

Then in Mattermost run /oc_status in that channel. Look for a line like:

mattermost: slash command /oc_status from <user> in <channelId>

That last value is the channel ID.

#

Option 2: Mattermost API

MM_URL="http://100.111.228.114:8065"
MM_TOKEN="your-bot-token"
TEAM_NAME="your-team-name"
CHANNEL_NAME="town-square"   # no #

TEAM_ID=$(curl -s -H "Authorization: Bearer $MM_TOKEN" \
  "$MM_URL/api/v4/teams/name/$TEAM_NAME" | jq -r .id)

curl -s -H "Authorization: Bearer $MM_TOKEN" \
  "$MM_URL/api/v4/teams/$TEAM_ID/channels/name/$CHANNEL_NAME" | jq -r .id

For binding, use that ID: