Skip to main content
Send Message lets a running agent reach the user proactively on their configured channels — without waiting for the next user turn. Need the answer back, not just delivery? See Ask Conversation.
The user starts a long job; the agent proactively messages them on Telegram, Slack, or email when it finishes.

Quick Start

1

Give Your Agent the Tool

The agent can now call send_message at any point mid-task to push a notification to the user.
2

List Available Targets

Use action="list" first when you want the agent to pick the right channel rather than defaulting to "origin".
3

Send with a File Attachment

Append 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


Targets

Choose the right target form for your situation:

Attachments (MEDIA:)

Append MEDIA:<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.
The emoji rides in the 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 a ReactionResult with a typed status (plus optional target and detail):
Channels that support reactions today: Telegram, Slack, Discord. Others (WhatsApp, Signal, email, agentmail, Linear) return "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.

Listing Targets

action="list" returns a JSON array of all reachable targets so the agent can pick the right one before sending.
Each entry in the array: "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:
Agent instructions can check for this string and fall back gracefully.

User Interaction Flow

A user starts a long research task on Telegram, then closes their phone. The agent finishes compiling the report, calls send_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.

Configuration Reference

send_message takes three arguments. All are optional: When action is "react" or "unreact", pass the emoji as the message argument. 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


Best Practices

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.
"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).
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.
Mobile push notifications truncate long text. Send a one-line summary; attach details as a file with MEDIA:.

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