Quick Start
1
Give Your Agent the Tool
send_message at any point mid-task to push a notification to the user.2
List Available Targets
action="list" first when you want the agent to pick the right channel rather than defaulting to "origin".3
Send with a File Attachment
MEDIA:<path> to the message text to attach a local file. Multiple attachments are supported.Media attachments aren’t delivered yet. The text portion is sent, the
MEDIA: paths are parsed and counted, and the result detail confirms how many files were skipped. File delivery will land in a future transport update — track PraisonAI#2374 for follow-ups.The MEDIA: caveat applies only to the send_message tool path (via BotOutboundMessenger). Proactive / scheduled / gateway-driven deliveries via DeliveryRouter.send_media do deliver attachments and honour threaded targets — see Outbound Media Delivery.How It Works
The verbs
react, thread, edit, and delete add a capability-gate step: the router checks the adapter’s supports_* flag before dispatch and returns a typed unsupported when the channel lacks the primitive — it never raises.
Targets
Choose the right target form for your situation:Attachments (MEDIA:)
AppendMEDIA:<path> to the message string to attach a local file to the delivery.
File paths must not contain whitespace. The
MEDIA: directive is stripped from the displayed message text before delivery.Reactions
Agents can tap a lightweight ✅ / 👍 / ⏳ reaction on a message instead of sending a full reply — perfect for busy group chats where every reply is noise.message argument and is .strip()-ed — an empty emoji returns {"status": "failed", "detail": "no emoji provided"}.
target="origin" reacts to the inbound message the agent is currently handling — its message_id is carried on the session automatically, so no extra parameter is needed. To react to a different message, use the explicit "<platform>:<chat_id>:<message_id>" form.Reaction outcomes
Reactions return a JSON string of aReactionResult with a typed status (plus optional target and detail):
"unsupported" — the tool never raises.
The same Send Policy that guards action="send" also guards react/unreact — a steered or prompt-injected agent cannot react on a channel the operator never intended.
Threads
Open a topic or thread under a target so a long-running effort has its own space.message argument. An empty title returns {"status": "failed", "detail": "no thread title provided"}.
Thread outcomes
Threads return a JSONThreadResult with a typed status (plus thread_id on success):
Edit and Delete (own messages)
Update a status in place, or retract a message the agent shouldn’t have sent — instead of spamming follow-ups.- Authority:
editanddeleteare the most-restricted class —own-prior-message. They only work on messages this agent/session sent. - Capability gate: powered by the adapter’s
supports_edit/supports_delete. Channels that can’t edit/delete return a typedunsupportedoutcome — the tool never raises. - Send-policy still guards
edit/delete— a steered agent cannot mutate messages on channels the operator hasn’t allowed. - Required
message_id: a missing id returns{"status": "failed", "detail": "no message_id provided"}. - Result shape: a JSON
MessageActionResultwithstatus(ok/unsupported/failed/no_route), optionaltarget, and optionaldetail.
Channel support today
Per-channel capability flags live in Bot Platform Capabilities.
Action Vocabulary and Authority
Every verb the tool accepts declares one of three authority classes — a closed set that keeps a steered agent from inventing new mutation verbs.
Any verb missing from the map is rejected — the agent cannot “invent” a new mutation verb that silently inherits
send’s guard:
Listing Targets
action="list" returns a JSON array of all reachable targets so the agent can pick the right one before sending.
"observed" means a channel the gateway has seen activity on but is not a configured home or alias.
When It’s Available
When no gateway is active the tool returns:
User Interaction Flow
A user starts a long research task on Telegram, then closes their phone. The agent finishes compiling the report, callssend_message("origin", "Your report is ready MEDIA:/tmp/report.pdf"), and the user receives a Telegram push notification with the text — without ever having to ask “are you done yet?”.
A user pings the agent in a 500-person Slack channel. Instead of replying and cluttering the thread, the agent taps a ⏳ reaction to acknowledge, works on the task, then swaps to ✅ when done — no message noise, but the user still sees progress.
An agent posts “Working…” on Telegram, keeps working, then edits that same message to “Done ✅” — the user sees a single evolving status line on their phone, not a stream of noisy follow-ups.
The agent notices it posted a message with the wrong figure and calls send_message("origin", "", action="delete", message_id="…") to unsend it before the user reads it.
Configuration Reference
send_message takes four arguments. All are optional:
action values at a glance:
Common Patterns
Notify on long-task completion
Cross-channel handoff
Pick the target from the list, then send
Acknowledge with a reaction in a busy channel
Post-then-edit a live status line
Open a thread for a long-running rollout
Best Practices
Confirm channel before sending to an explicit target
Confirm channel before sending to an explicit target
Call
send_message(action="list") first when the user hasn’t specified where they want to be notified. Sending to a channel the user doesn’t monitor is noise.Default to 'origin'
Default to 'origin'
"origin" sends to the chat the conversation started in — usually the right choice. Only override it when you have a specific reason (e.g., sending an ops alert to a dedicated Slack channel).Handle the no-gateway fallback in agent instructions
Handle the no-gateway fallback in agent instructions
Include a note like “If send_message returns a ‘No active gateway’ message, skip the notification and continue.” so the agent doesn’t retry endlessly on CLI runs.
Keep messages short — they hit a phone
Keep messages short — they hit a phone
Mobile push notifications truncate long text. Send a one-line summary; attach details as a file with
MEDIA:.Edit your own status instead of spamming replies
Edit your own status instead of spamming replies
On channels that support it,
edit keeps the chat clean; on channels that don’t, you’ll get unsupported and can post a follow-up.Only edit / delete your own messages
Only edit / delete your own messages
Adapters reject mutating anyone else’s message with a transport
failed.Always check the returned status
Always check the returned status
Never assume
ok. On unsupported or failed, fall back to a plain send.Related
Ask Conversation
Request/reply sibling — message a target and wait for its answer
Send Policy
Restrict which channels send_message is allowed to deliver to
Clarify Tool
Ask the user mid-task for clarifying input
Channels Gateway
Connect agents to Telegram, Slack, Discord, and WhatsApp
Bot Gateway
Run the gateway server
Bot Routing
Route messages by channel and platform
Gateway Threads
Thread-aware routing and creation across platforms
Bot Platform Capabilities
Per-channel
supports_edit / supports_delete / supports_reactions matrix
