openclaw message CLI: Send Messages & Channel Actions
Reference for the openclaw message command, covering target formats and channel selection for Discord, Slack, Telegram, and more. Essential for developers automating outbound messaging.
Read this when
- Adding or modifying message CLI actions
- Changing outbound channel behavior
openclaw message
One command handles outbound messaging and channel actions across Discord, Google Chat, iMessage, Matrix, Mattermost (plugin), Microsoft Teams, Signal, Slack, Telegram, and WhatsApp.
openclaw message <subcommand> [flags]
Channel selection
- When multiple channels are set up,
--channel <name>becomes mandatory; a single configured channel automatically serves as the default. - Acceptable inputs are
discord|googlechat|imessage|matrix|mattermost|msteams|signal|slack|telegram|whatsapp(the Mattermost plugin is a prerequisite). - Targets prefixed with a channel, such as
discord:channel:123, point to the correct plugin without needing an explicit--channel.
Target formats (-t, --target)
| Channel | Format |
|---|---|
| Discord | channel:<id>, user:<id>, <@id> mention, or a plain numeric id (interpreted as a channel id) |
| Google Chat | spaces/<spaceId> or users/<userId> |
| iMessage | handle, chat_id:<id>, chat_guid:<guid>, or chat_identifier:<id> |
| Mattermost (plugin) | channel:<id>, user:<id>, @username, or a plain id (interpreted as a channel) |
| Matrix | @user:server, !room:server, or #alias:server |
| Microsoft Teams | conversation:<id> (19:...@thread.tacv2), a plain conversation id, or user:<aad-object-id> |
| Signal | +E.164, group:<id>, uuid:<id>, username:<name>/u:<name>, or any of these with signal: as a prefix |
| Slack | channel:<id> or user:<id> (a plain id is interpreted as a channel) |
| Telegram | chat id, @username, or a forum topic target: <chatId>:topic:<topicId> (or --thread-id <topicId>) |
E.164, group JID (...@g.us), or Channel/Newsletter JID (...@newsletter) |
Resolving channel names: on platforms with a directory (Discord/Slack/etc), entries like Help or #help come from the directory cache; a miss triggers a live lookup where the provider allows it.
Common flags
Each action takes these parameters: --channel <name>, --account <id>, --json, --dry-run, --verbose. When an action needs a destination, -t, --target <dest> is also accepted.
SecretRef resolution
Before executing, openclaw message resolves channel SecretRefs with the tightest possible scope:
- channel-level when
--channelis provided (or deduced from a prefixed target) - account-level when
--accountis additionally provided - all configured channels when neither is provided
Missing SecretRefs on unrelated channels don't interfere with a targeted action; a missing SecretRef on the chosen channel or account causes the action to fail closed.
Actions
Core
| Action | Channels | Required | Notes |
|---|---|---|---|
send | Discord, Google Chat, iMessage, Matrix, Mattermost (plugin), Microsoft Teams, Signal, Slack, Telegram, WhatsApp | --target, plus one of --message/--media/--presentation | Refer to Send further down. |
poll | Discord, Matrix, Microsoft Teams, Telegram, WhatsApp | --target, --poll-question, --poll-option (repeat) | Check Poll further down. |
react | Discord, Matrix, Nextcloud Talk, Signal, Slack, Telegram, WhatsApp | --message-id, --target | --emoji, --remove (requires --emoji; leave it out to remove your own reactions where the platform allows it, see Reactions). On WhatsApp: --participant, --from-me. Signal group reactions depend on --target-author or --target-author-uuid. Nextcloud Talk only supports adding reactions; --remove produces an error. |
reactions | Discord, Matrix, Microsoft Teams, Slack | --message-id, --target | --limit. |
read | Discord, Matrix, Microsoft Teams, Slack | --target | --limit, --message-id, --before, --after. For Discord: --around, --include-thread. For Slack: --message-id targets a particular timestamp, pair it with --thread-id to reply to a specific thread. |
edit | Discord, Matrix, Microsoft Teams, Slack, Telegram | --message-id, --message, --target | Telegram forum threads rely on --thread-id. |
delete | Discord, Matrix, Microsoft Teams, Slack, Telegram | --message-id, --target | |
pin / unpin | Discord, Matrix, Microsoft Teams, Slack | --message-id, --target | unpin also takes --pinned-message-id (in Microsoft Teams, that is the pin/list-pins resource id, not the chat message id). |
pins (list) | Discord, Matrix, Microsoft Teams, Slack | --target | --limit. |
permissions | Discord, Matrix | --target | On Matrix, this works only when encryption is turned on and verification actions are permitted. |
search | Discord | --guild-id, --query | --channel-id, --channel-ids (repeat), --author-id, --author-ids (repeat), --limit. |
member info | Discord, Matrix, Microsoft Teams, Slack | --user-id | --guild-id (Discord). |
Send
openclaw message send --channel discord \
--target channel:123 --message "hi" --reply-to 456
--media <path-or-url>: attach image/audio/video/document (local path or URL).--presentation <json>: shared payload withtext,context,divider,chart,table,buttons, andselectblocks, rendered per channel capability. See Message Presentation.--delivery <json>: generic delivery preferences, for example{"pin": true}.--pinis shorthand for pinned delivery when the channel supports it.--reply-to <id>,--thread-id <id>(Telegram forum topic; Slack thread timestamp, same field as--reply-to).--force-document: preserve original image bytes on Slack, or send images/GIFs/videos as documents on Telegram and WhatsApp, to avoid channel compression.--silent(Telegram, Discord): send without a notification.--gif-playback(WhatsApp only): treat video media as GIF playback.
When a send is suppressed by a message hook, fails, or only partially succeeds,
the command explains the outcome and exits nonzero. Partial delivery keeps any
confirmed message ID. JSON failures include ok: false, deliveryStatus, and
error; successful JSON responses retain their existing shape.
openclaw message send --channel discord \
--target channel:123 --message "Choose:" \
--presentation '{"blocks":[{"type":"buttons","buttons":[{"label":"Approve","value":"approve","style":"success"},{"label":"Decline","value":"decline","style":"danger"}]}]}'
openclaw message send --channel telegram --target @mychat --message "Choose:" \
--presentation '{"blocks":[{"type":"buttons","buttons":[{"label":"Yes","value":"cmd:yes"},{"label":"No","value":"cmd:no"}]}]}'
Slack renders supported chart blocks natively; other channels receive the same data as readable text:
openclaw message send --channel slack --target channel:C123 \
--presentation '{"blocks":[{"type":"chart","chartType":"bar","title":"Quarterly revenue","categories":["Q1","Q2"],"series":[{"name":"Revenue","values":[120,145]}],"xLabel":"Quarter"}]}'
Slack also renders explicit table blocks natively. Other channels receive the caption and every row as deterministic text:
openclaw message send --channel slack --target channel:C123 \
--presentation '{"title":"Pipeline report","blocks":[{"type":"table","caption":"Open pipeline","headers":["Account","Stage","ARR"],"rows":[["Acme","Won",125000],["Globex","Review",82000]],"rowHeaderColumnIndex":0}]}'
Telegram Mini App buttons use webApp (web_app still parses for legacy
JSON) and only render in private chats between a user and the bot:
openclaw message send --channel telegram --target 123456789 --message "Open app:" \
--presentation '{"blocks":[{"type":"buttons","buttons":[{"label":"Launch","webApp":{"url":"https://example.com/app"}}]}]}'
openclaw message send --channel telegram --target @mychat \
--media ./diagram.png --force-document
openclaw message send --channel msteams \
--target conversation:19:abc@thread.tacv2 \
--presentation '{"title":"Status update","blocks":[{"type":"text","text":"Build completed"}]}'
Poll
openclaw message poll --channel discord \
--target channel:123 \
--poll-question "Snack?" \
--poll-option Pizza --poll-option Sushi \
--poll-multi --poll-duration-hours 48
--poll-option <choice>: repeat 2-12 times.--poll-multi: allow multiple selections.- Discord:
--poll-duration-hours,--silent,--message. - Telegram:
--poll-duration-seconds <n>(5-604800; up to seven days),--silent,--poll-anonymous/--poll-public,--thread-id.
openclaw message poll --channel telegram \
--target @mychat \
--poll-question "Lunch?" \
--poll-option Pizza --poll-option Sushi \
--poll-duration-seconds 120 --silent
openclaw message poll --channel msteams \
--target conversation:19:abc@thread.tacv2 \
--poll-question "Lunch?" \
--poll-option Pizza --poll-option Sushi
Threads
thread create: Discord channel targeting. Mandatory fields:--thread-name,--target(channel id). Non-required fields:--message-id,--message,--auto-archive-min.thread list: Discord channel targeting. Mandatory field:--guild-id. Non-required fields:--channel-id,--include-archived,--before,--limit.thread reply: Discord channel targeting. Mandatory fields:--target(thread id),--message. Non-required fields:--media,--reply-to.
Emojis
emoji list: Discord (--guild-id), Slack (no extra flags).emoji upload: Discord. Mandatory fields:--guild-id,--emoji-name,--media. Non-required field:--role-ids(repeat).
Stickers
sticker send: Discord. Mandatory fields:--target,--sticker-id(repeat). Non-required field:--message.sticker upload: Discord. Mandatory fields:--guild-id,--sticker-name,--sticker-desc,--sticker-tags,--media.
Roles, channels, voice, events (Discord)
role info:--guild-id.role add/role remove:--guild-id,--user-id,--role-id.channel info:--target.channel list:--guild-id.voice status:--guild-id,--user-id.event list:--guild-id.event create: required--guild-id,--event-name,--start-time; optional--end-time,--desc,--channel-id,--location,--event-type,--image <url-or-path>.
Moderation (Discord)
timeout:--guild-id,--user-id; optional--duration-minor--until(omit both to clear the timeout),--reason.kick:--guild-id,--user-id,--reason.ban:--guild-id,--user-id,--delete-days,--reason.
Broadcast
openclaw message broadcast --targets <target...> [--channel all] [--message <text>] [--media <url>] [--dry-run]
A single payload can be delivered to several destinations at once. Pass a space-separated list to --targets. To reach every provider that is set up, use --channel all.