messages
messages.delete
Delete Message
Effects: delete, write
Delete a message from a thread. Supports Telegram, WhatsApp, and other connected channels. Note: Some channels have time limits on message deletion.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
message_id | string | yes | ID of the message to delete |
thread_id | string | yes | Thread/channel ID containing the message |
messages.edit
Edit Message
Effects: write
Edit the text of a message you already sent, in place. Supported on Telegram, WhatsApp, LiveChat, Max.ru, LINE, WeChat and similar channels; NOT supported on Gmail, Instagram, or LinkedIn (their platforms forbid editing) — those return an explicit error. Note: channels impose their own limits (own messages only, edit time windows such as ~48h on Telegram / ~15min on WhatsApp).
Get the message_id from messages.read_history (each row's id).
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
message_id | string | yes | ID of the message to edit (from messages.read_history). |
new_text | string | yes | Replacement text for the message. |
thread_id | string | yes | Thread ID containing the message (numeric DB id or channel_ref like 'telegram:-100123'). |
messages.forward
Forward Message
Effects: send, write
Forward a message from one thread to another. Supports native Telegram forwarding (preserves original sender attribution) and text-based forwarding for cross-channel scenarios.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
dest_thread_id | string | no | Destination thread to forward into. Provide at least one of dest_thread_id or recipient_name. To forward into the active conversation, pass the current thread_id. (If both are provided, dest_thread_id wins and recipient_name is ignored.) |
recipient_name | string | no | Name of person to forward to (channel auto-resolved). Provide at least one of dest_thread_id or recipient_name. Use only when forwarding to a different contact than the current conversation. |
source_message_id | string | yes | ID of the message to forward |
source_thread_id | string | yes | Thread containing the message to forward (e.g., 'telegram:123456' or numeric DB ID) |
messages.press_button
Press Message Button
Effects: send, external_api
Press a button under a message in a Telegram chat, as the connected Telegram account: the inline menus bots such as @BotFather reply with. Name the button by its exact label.
Returns the message as it looks after the press (bots usually edit it into the next menu) and anything the bot sent after it, each with its own buttons and a message_id you can pass back to press the next one.
Refused: buttons that need the account's cloud password (the owner confirms those in the Telegram app), and buttons that share a phone number or location, pay, or open a web app. A link button is not pressed; its URL is returned. A label that matches no button returns the buttons that exist.
Get the message_id from messages.read_history. Telegram user accounts only; a bot account cannot press buttons.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
button_text | string | yes | The button's label, exactly as shown (case is ignored if nothing matches exactly). |
message_id | string | yes | Message the button is under: a DB id from messages.read_history, or a 'telegram:<id>' returned by a previous press. |
thread_id | string | yes | Thread containing the message (numeric DB id or channel_ref like 'telegram:123456789'). |
messages.read_history
Read Message History
Effects: read
Read messages from a conversation thread. Use text_contains to find specific messages by content; media_type (photo, document, video, voice, link), date_from/date_to (YYYY-MM-DD, inclusive) and direction (incoming, outgoing) narrow the search and work without text: a photo with no caption sent by the other side in a given month is media_type='photo', direction='incoming' and the dates. A search runs over the loaded part of the conversation and, when that is not all of it, asks the channel's own server (Telegram) to search the rest and stores the hits (a search with text_contains also stores the neighbouring messages of its first 20 hits; one without words stores the hits alone). search_coverage says which: complete = true with a source, or complete = false with the reason, in which case an empty list is not proof the text is absent. sync.loaded_from is the date since which EVERY message is stored (null when unknown); sync.oldest_stored_at is the oldest message stored at all, gaps or not. Returns the most recent messages, including sender info and timestamps.
Voice calls: each row carries a meta object with allowlisted keys (event_type ∈ 'call_started'|'call_ended'|null, source ∈ 'voice_transcript'|null, call_id, speaker_display_name, duration_seconds, outcome, direction) plus per-message channel. To find calls without scanning every row, use calls.list_history instead.
Usage:
- Get thread_id from threads.list first, OR
- Use contact_name to auto-resolve thread_id
Examples:
- User: 'show me messages from chat with [contact]' → read_history(contact_name='[contact]', limit=10)
- User: 'last 5 messages from thread 571' → read_history(thread_id=571, limit=5)
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
before | string | no | Cursor for the page of OLDER messages of a chat read live (source: "live", a Discord channel that is not synced): pass the previous answer's next_before. Pages hold at most 100 messages (limit). A stored chat pages with offset instead (the answer's next_offset). OMIT for the newest page. |
channel_account_id | string | no | Connected account the conversation must belong to, when you know it (from channels.list). Scopes the lookup to that account, so the same person reached on two connected accounts cannot be confused for one, and a chat that does not exist on this account is 'not found' instead of someone else's thread with a matching id. |
contact_name | string | no | Contact/thread name to search for (optional if thread_id provided). Example: 'Jane Smith', 'John Doe' |
date_from | string | no | Filter: only messages sent on or after this day (YYYY-MM-DD, UTC). |
date_to | string | no | Filter: only messages sent on or before this day (YYYY-MM-DD, UTC). |
direction | string | no | Filter: 'incoming' = written by the other side, 'outgoing' = written by the owner of the connected account. OMIT for both. (one of incoming, outgoing) |
include_outgoing | boolean | no | Include messages sent by you (default: true) (default True) |
limit | integer | no | Maximum number of messages to return (default: 10, max: 100) (default 10; min 1.0; max 100.0) |
load_until | string | no | Load this conversation's history down to this day (YYYY-MM-DD, UTC) in the background, a page at a time. For a channel whose server cannot search (WhatsApp), or to read everything around a date. The answer's history_load says how far back the history reaches now and, once the run is over, why it stopped; call again to follow it. OMIT for a normal read. |
media_type | string | no | Filter: only messages carrying this kind of attachment. Works without text_contains, which is how a photo or document sent without a caption is found. OMIT for every kind. (one of photo, document, video, voice, link) |
offset | integer | no | Number of messages to skip (for pagination, default: 0) (min 0.0) |
text_contains | string | no | Filter: only return messages containing this text (case-insensitive substring match) |
thread_id | string | no | Thread ID from threads.list (e.g. '571'), or a channel_ref (e.g. 'telegram:1306644770'). Optional if contact_name provided. |
messages.send
Send Message
Effects: send, write
Send a message to a thread, channel, or contact. Supports Telegram, Email, LinkedIn, and other connected channels. For LinkedIn posts (comment_thread kind), this posts a comment on the post. Can automatically resolve recipients and channels when not specified. Can send files/images/documents as attachments — pass attachments=[file_id, ...] with integer file IDs obtained from collections.list_files, search.files, or files.search. text is optional when attachments are provided.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
attachments | array | no | Array of integer file IDs to send as attachments (images, documents, any files). Get file IDs from collections.list_files (field file_id), search.files (field file_id), or files.search — only ids returned by those calls in this workspace. The file must already exist in the workspace (status=ready) — no separate upload step needed. When attachments are provided, text becomes optional (a caption can be included alongside). |
buttons | array | no | Inline reply buttons shown under the message, one per row (max 10). Each item is {"label": "<visible button text>"} plus exactly one target: "value" (a string sent back as a normal incoming message when tapped, defaults to the label, max 64 bytes), "url" (opens a link) or "web_app" (opens an https page as a Telegram Mini App). A "url" button may add "track": true: the tap is then counted for this conversation (analytics_link_clicks_v in analytics.query) and the person is sent on to the url; "tag": <ai_tag id> beside it also puts that tag on the CONVERSATION at the tap (a group's tag is any member's tap; a first message to a new contact has no conversation yet and goes out untagged); "pass": "<name>" opens the url with <name>=<the chat's id on its channel> added, so the site can name the person back in its own events. Only on channels that support buttons (Telegram bot accounts); can accompany at most one attachment, and when the text is too long for a media caption the buttons arrive with the text as a second message. |
format | string | no | Message format (one of text, markdown, html; default text) |
from_account_id | integer | no | Which of the workspace's accounts on this channel SENDS the message, i.e. the number or handle the recipient sees. A numeric thread_id is one conversation on one account: naming a different account for it is refused. With a channel_ref or recipient_username, the conversation ON THIS ACCOUNT is used, or opened where the channel can start one; if it has none, the send is refused rather than made from another account. OMIT and the platform picks the most recently active account, which is a coin flip in a workspace with several numbers: pass it whenever one of them must not be used to open conversations (a personal line, or one under a spam restriction). An account that is not active in this workspace is refused, never silently swapped. |
mentions | array | no | People of a group chat to @-tag so the platform really notifies them (WhatsApp, Telegram groups). Each item names one member of THIS chat by one of: "name" (as shown in the chat), "phone" (international number) or "entity_id". If the text already holds "@<name>" that spot is the tag, otherwise "@<name> " is put in front of the text. A person who is not in the chat, or who matches several, refuses the whole send and lists the members. Without this parameter a "@Name" in the text of a group message tags the member it names in the same way, and an @-word that names nobody refuses the send (on Telegram it stays text: a typed @username is a link there). Not combinable with attachments or buttons, and not available while the agent sends drafts. Text may be empty: the tags alone are sent. |
private_reply | boolean | no | Comment threads only (Instagram, Facebook): answer the comment in the commenter's private messages instead of publicly under the post (Meta's Private Replies). One message per comment, within 7 days of the comment. The message is filed in the person's private conversation. To also answer publicly, make a second call without this flag. OMIT for a normal send. |
recipient_name | string | no | Name of a person already in contacts (e.g., 'Jane', 'John'). Tool will auto-resolve channel. Optional if thread_id provided. Not for a phone number: a new number goes in thread_id (see there). |
recipient_username | string | no | Telegram @username to message (e.g. '@some_username'). Use this for a Telegram user NOT yet in contacts — it resolves the handle, adds the contact, and creates the thread. Telegram only; for existing contacts prefer thread_id or recipient_name. |
reply_to_message_id | string | no | ID of message to reply to (optional) |
silent | boolean | no | Send without notification |
template | object | no | WhatsApp Business API only. An approved template to send instead of free text, in Meta's shape: {name, language, components:[{type:'body', parameters:[{type:'text', text:...}]}, ...]}; language is the code as a string ('en_US'). Required when the 24-hour window of the conversation is closed (the send answers WINDOW_EXPIRED otherwise). Read the approved templates and their variables with channels.list(account_id=...). |
text | string | no | Message text to send. Optional if attachments provided. (max 4096.0) |
thread_id | string | no | Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. To write to a phone number nobody has a thread with yet: 'whatsapp:+<number in international format>' writes to it from a personal WhatsApp account (from_account_id picks which one), 'whatsapp_business:+<number in international format>' opens a WhatsApp conversation from a WhatsApp Business API number (from_account_id picks which one; the first message must be a template), and 'sms:+<number>' texts it from a workspace phone number that has SMS turned on (from_account_id picks which one), landing in that number's conversation with the recipient, next to their calls. 'x:@<handle>' sends a first X direct message to that person from a connected X account; X refuses it when their inbox is closed to the sender. 'discord:<user id>' (the person's numeric Discord id) sends a first Discord DM from a connected Discord account, into the DM that exists with them or one opened for it; new DMs are limited per account and day, and refused in words with a retry_after when over. 'skool:user:<user id>' (optionally '@<community id>' after the id, when the two share several communities) sends a first message on Skool from a connected Skool account; a Skool post thread ('skool:<account>:post:<post id>') takes a comment instead. NEVER use a chat-type word (dm, group, channel, livechat, comment_thread) — those are category labels from the SITUATION block, not ids. |
messages.send_email
Send Email
Effects: send, write
Compose and send an email — with subject, CC/BCC, and attachments. Use for email; for chat messages (Telegram/WhatsApp/livechat) use messages.send instead.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
attachments | array | no | Array of integer file IDs to attach. |
bcc | array | no | Email addresses to BCC. OMIT to skip. |
cc | array | no | Email addresses to CC. OMIT to skip. |
from_account_id | integer | no | Which of the workspace's mailboxes SENDS an email to recipient_email (an id from channels.list). With several mailboxes and none named, an address one mailbox already writes with goes from that mailbox, any other is refused with the list. A mailbox outside the workspace is refused, never swapped. A reply in the current thread (no recipient_email) goes from that thread's mailbox. |
recipient_email | string | no | Recipient email address (e.g. 'john@example.com'). Provide to start a new email thread; OMIT to reply in the current email thread. |
subject | string | no | Email subject line. Required for new emails; for replies it auto-generates 'Re: ...' when omitted. |
text | string | no | Email body. |