Turbo Notify: WhatsApp for AI agents

com.turbonotify由 turbonotify.com 发布官方发布者v0.2.1更新于 Nov 6, 2026

Send and receive WhatsApp messages from your AI agent, on your own number.

发布方自己的服务:Turbo Notify: WhatsApp for AI agents

已验证Streamable HTTP可网页运行效率与工作流沟通与协作

概览

AI 生成的概览

远程 MCP 服务器,让 AI 助手用组织自己的号码收发 WhatsApp 消息,支持群组、媒体和日历邀请。

可以做什么
  • 向号码、群组或联系人发送文本、链接媒体、位置、名片、日历邀请和链接按钮消息
  • 回复、编辑、添加表情回应、标记已读、删除消息以及显示正在输入
  • 读取会话:列出和获取消息、投递状态、已读回执、表情回应和日历答复
  • 管理联系人和群组:列出、查看详情、头像、刷新名称
  • 管理号码:列出、查看、连接或断开,并查询消息、联系人和用量配额
  • 轮询事件流获取收到的消息和其他变化,无需自建 webhook
适用场景
适合需要让助手通过企业 WhatsApp 号码与客户或同事沟通的场景,例如自动应答客户咨询的虚拟接待员、发送通知或媒体并跟踪投递。也适合需要读取 WhatsApp 会话并对新消息作出反应的流程。
运行要求
远程 streamable HTTP 端点,无需本地安装包。需要 Turbo Notify 账号,或以 Authorization Bearer 头传入的 API 密钥。每个消息工具都接受 number_alias,默认 main。部分功能(如已读回执)需要包含该功能的套餐。
安装前请注意
服务器会以你的号码发送真实 WhatsApp 消息,并可对所有人删除消息,且不可撤销;删除或群发前应先由人工确认。它还能编辑和取消日历事件、断开号码。配额按号码计算并按计费周期收费,断开号码不会停止计费。凭据通过 Authorization 头传递,消息内容和联系人数据会发送给 Turbo Notify。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Turbo Notify: WhatsApp for AI agents,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/com.turbonotify%2Fturbo-notify/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。

{
  "mcpServers": {
    "turbo-notify-whatsapp-for-ai-agents": {
      "type": "http",
      "url": "https://mcp.turbonotify.com/"
    }
  }
}

README

Turbo Notify Agent Skills

Skills that put the Turbo Notify MCP server to work inside your AI agent: Claude Code, Claude Desktop and Cowork, Codex, the Antigravity CLI and the Gemini CLI, and any other agent that reads the Agent Skills format.

Skills

SkillWhat it does
virtual-attendantServes a business's customers on WhatsApp: answers in the customer's language, remembers each customer, calls a person when it should, and takes commands from administrators and attendants on WhatsApp itself.

Customer guides (Portuguese and English): WhatsApp virtual attendant, Agent skills and AI agents, with one page per agent.

Install

AgentCommands
Claude Codeclaude plugin marketplace add turbo-notify/agent-skills then claude plugin install turbo-notify@turbo-notify
Claude Desktop and CoworkCustomize, Plugins, Add, Add marketplace, Add from a repository: turbo-notify/agent-skills; then Discover, turbo-notify, Add
Codexcodex plugin marketplace add turbo-notify/agent-skills then codex plugin add turbo-notify@turbo-notify
Antigravityagy plugin install https://github.com/turbo-notify/agent-skills
Gemini CLIgemini extensions install https://github.com/turbo-notify/agent-skills
Hermes Agenthermes skills install turbo-notify/agent-skills/skills/virtual-attendant, then add the MCP server https://mcp.turbonotify.com in Hermes
OpenCodenpx skills add turbo-notify/agent-skills -a opencode (project folder .agents/skills/, or -g for ~/.config/opencode/skills/), then opencode mcp add --global --url https://mcp.turbonotify.com turbo-notify and opencode mcp auth turbo-notify to sign in. Guide: Turbo Notify in OpenCode
Other agents that read the Agent Skills format (Cursor, Copilot and more)npx skills add turbo-notify/agent-skills, then add the MCP server https://mcp.turbonotify.com in that agent's own settings

The unattended runner exists for Claude Code and Codex only. In the other agents the attendant runs in assisted mode: a session you keep open, where a person approves each draft.

The plugin rows above already declare the Turbo Notify MCP server (https://mcp.turbonotify.com), so there is nothing to add. Sign in with your Turbo Notify account the first time the agent uses it (Claude Code: /mcp; Codex: codex mcp login turbo-notify; Antigravity: Agent settings, Customizations, Authenticate next to the server), then ask it to "set up the Turbo Notify virtual attendant". No account yet? Create one at https://dashboard.turbonotify.com/auth/signup?locale=en. For anything that runs alone, use an API key instead (see the guide for your agent in skills/virtual-attendant/references/).

Update

The skill checks its version on every cycle and messages the administrators when a new one exists, with the command for the agent in use.

AgentCommand
Claude Codeclaude plugin marketplace update turbo-notify then claude plugin update turbo-notify@turbo-notify
Claude Desktop and CoworkThe marketplace syncs from the repository; if a new version does not show up, remove the marketplace and add it again
Codexcodex plugin marketplace upgrade then codex plugin add turbo-notify@turbo-notify
AntigravityRun the install command again
Gemini CLIgemini extensions update turbo-notify

What it reaches

  • The Turbo Notify MCP server at https://mcp.turbonotify.com, which the plugin declares in .mcp.json, mcp.json and mcp_config.json (one file for each family of agents; none holds a key or a header). You sign in with your account, or you connect your own with an API key. It runs with your account or API key permissions.
  • One folder on your computer (by default ~/.turbo-notify/attendant) holding, for each customer, the conversation (kept 90 days by default, then deleted), short summaries of older parts of it, and a file with the facts that customer gave (name, preferences, what they asked for), plus what you taught. Turbo Notify gets no copy. /forget erases one customer's data, and the retention is a number you can change in memory-policy.json.
  • The Turbo Notify API at https://api.turbonotify.com, called by the helper with your API key when the unattended runner or a console command sends a message.
  • One network address besides Turbo Notify: raw.githubusercontent.com/turbo-notify/agent-skills/main/latest.json, read about once an hour (and again after 10 minutes if a check fails) for the version check. A failure changes nothing.
  • Your other MCP servers only if you allow each one during setup.

What it runs and what it sends

  • It runs the tn_assist helper with python3. The helper uses only the standard library and installs nothing.
  • In the unattended runner (Claude Code and Codex), it starts your own claude or codex command once per customer, in a separate process with no tools, to write that customer's answer. The process gets that customer's messages, their file and the business knowledge that matches the message (the company, tone and hand-off files, plus up to three more picked by the words of the message), and nothing else.
  • The text of a customer's message goes to the model of the agent you use, under your account with that agent. It goes nowhere else.
  • The unattended runner reads your API key from the TURBO_NOTIFY_API_KEY variable you set for it and sends it only to api.turbonotify.com, as the sign-in of each request. It is never passed to the process that writes a customer's answer.
  • Answers go out through the Turbo Notify API with your API key, to the customer who wrote. A check before every send stops another customer's number or name, non-public text and credentials.
  • The first answer of each chat tells the customer that it comes from an AI. The line is added by the helper, not by the model.
  • A customer who only says thanks, "ok" or sends a thumbs up gets no answer and is not handed to a person. The model has to ask for that, and the helper agrees only when the message is nothing but a thanks or an acknowledgement (no question mark, no number, a few words from a fixed list). Anything more is answered, or goes to a person.
  • A customer goes to a person when the answer is not in what you taught (a price, stock, a deadline, a promise, a policy), when they ask for one or are upset, when the request is outside the business, when a payment problem, a refund or exchange, a complaint or a legal matter comes up, or when they ask the assistant to promise or confirm something in writing. A plain question about a rule you wrote, such as the exchange window, is answered.
  • If the model's answer cannot be read, the helper asks again once with a line naming what was wrong, and a second failure in a row sends the customer a message saying a person will continue.

Layout

.claude-plugin/        plugin and marketplace manifests (Claude Code, Codex).codex-plugin/         the manifest Codex reads for the name, logo and listing textplugin.json            manifest for Antigravitygemini-extension.json  manifest for the Gemini CLI (the connection to the server).mcp.json              the Turbo Notify MCP server, as Claude Code, Claude Desktop, Cowork and Codex read itmcp.json               the same server, as the Agent Plugins format reads it (Copilot, Cursor)mcp_config.json        the same server, as Antigravity reads itassets/icon.png        the plugin iconagents/                the tool-less agent that answers a customerskills/virtual-attendant/  SKILL.md             the entry point the agent reads  references/          one short file per topic (setup, tick, console, memory, privacy, ...)  assets/templates/    interview templates (pt, en) and the memory policy  scripts/tn_assist/   the helper: routing, memory, egress check, runner (Python 3.9+, no dependencies)tools/release.py       version consistency check and synctests/                 unit, property and security tests (Poetry, development only)evals/attendant/       live evaluation of a customer's turn on a real model, with a made-up shop

The helper holds the guarantees the model cannot be trusted with: who is who is decided by code from the registered numbers, a customer's turn runs without tools, every reply passes an egress check before it can leave, and the data never leaves the workspace folder.

Development

poetry installpoetry run pytestpoetry run ruff check . && poetry run ruff format --check .poetry run mypypython3 tools/release.py check

The live evaluation needs your own claude command, logged in. It asks 40 questions that the knowledge cannot answer and 11 that it can, and plays seven conversations (some end with a thank you, which is owed no answer). It checks every reply for a price, a date, a time, a quantity, a discount or a promise the knowledge does not state, for the language of the reply, and that a thank you is left alone while an "ok" that is really a question is answered. Two last checks cut the model's answer in half to see what the runner does with an answer it cannot read, and play an "ok" after an offer ("quer que eu chame alguém?") to see that it is answered, not left alone. Nothing in it is real data.

poetry run python -m evals.attendant run                # writes evals/attendant/results/<date>-<label>/poetry run python -m evals.attendant run --set refusal  # only the check on an unreadable answerpoetry run python -m evals.attendant run --set offers   # only the check on an ok that accepts an offerTN_EVALS_LIVE=1 poetry run pytest evals                 # the same, as tests (skipped otherwise)

Releases bump VERSION, latest.json, the five manifests, the helper and pyproject versions and CHANGELOG.md together (tools/release.py sync), then move the v1 tag, which stays available for anyone who wants to pin to 1.x (...agent-skills#v1, or --ref v1 in Codex).

License

MIT. See LICENSE.

来源:README.md,提交 8e55d26

工具

36

send_text

Send a text message

Send a WhatsApp text message. Set exactly one recipient: `number`, `group_id`, or `contact_id`. Writing `@<number>` in the text mentions that person, for example `@5511999999999`. The platform detects it automatically; there is no separate mentions argument. This is also how to answer the message you just received: a normal message to its sender, as a person does in a chat, with no quote. Quote (`quoted_message_id`, or `reply_to_message`) only an earlier message that your answer depends on. A text message you sent can be corrected later with `edit_text_message`. Like a person opening the chat, the number first appears online and its unread messages in that conversation are marked as read, then the message is sent. Opening the conversation this way is automatic and uses no quota of its own. Returns the new `message_id` and its initial status. Delivery happens asynchronously; poll `get_message_status` or watch `list_events` for the outcome.

输入参数text必填numbergroup_idcontact_idnumber_aliasidempotency_keyquoted_message_id

send_media

Send an image, video, audio, document or sticker

Send media by URL: an image, video, audio clip, document or sticker. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `url` must still be reachable when the send happens: Turbo Notify fetches the bytes from it. A copy is kept in the organization's own object storage afterwards when that is configured, which never blocks the send and is not required for one. `caption` applies to `image`, `video` and `document` only; audio and stickers have nowhere to render one. `filename` applies to `document` only. Pairing either with a kind that cannot carry it is rejected here, before anything is sent. After sending, only the caption of an image, video or document can be changed, with `edit_media_caption`. The file itself, an audio clip and a sticker cannot be edited: `delete_message` is the only change for them. `mime_type` is optional and does not change how the message is sent. The type WhatsApp receives comes from what the URL's own server serves, so point `url` at a file that server labels correctly.

输入参数url必填kind必填numbercaptionfilenamegroup_idmime_typecontact_idnumber_aliasidempotency_keyquoted_message_id

send_location

Send a map location

Send a location pin. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `latitude` is between -90 and 90, `longitude` between -180 and 180. `name` and `address` label the pin; `url` adds a link beside it. A location cannot be edited after it is sent: `delete_message` is the only change for it.

输入参数urlnamenumberaddressgroup_idlatitude必填longitude必填contact_idnumber_aliasidempotency_keyquoted_message_id

send_contact_card

Send a contact card

Send a contact card the recipient can save to their phone. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `phones` takes plain E.164 numbers, 1 to 10 of them. `emails`, `organization`, `title` and `address` are optional extra fields on the card; `emails` allows up to 10 as well. A contact card cannot be edited after it is sent: `delete_message` is the only change for it.

输入参数titleemailsnumberphones必填addressgroup_idcontact_iddisplay_name必填number_aliasorganizationidempotency_keyquoted_message_id

send_calendar_event

Send a calendar event

Send a calendar event invitation. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `start_at` and `end_at` are ISO 8601 **with an offset**, for example `2026-09-10T14:00:00Z`. A timestamp without one is rejected: the recipient's phone renders the event in their own timezone, so an ambiguous instant would show the wrong hour on their screen. When set, `end_at` must be after `start_at`; an event that ends before (or exactly when) it starts is rejected. `location` pins where it happens (`latitude` and `longitude`, with an optional `name` and `address`). `has_reminder` reminds guests, and `reminder_offset_sec` says how many seconds before the event; `extra_guests_allowed` lets guests bring extra people. To fix a mistake later (a typo, the description, a link), use `edit_calendar_event`: guests keep their answer. When the date, time or place change and guests must accept again, cancel the event with `cancel_calendar_event` and send a new one with this tool. The entry a guest added to their own calendar with the card's "Add to calendar" button never follows an edit or a cancel: it stays on the old date and adding again creates a second entry, so tell guests to delete the old one. To see who said they are coming, use `get_message_responses`.

输入参数name必填end_atnumbergroup_idlocationstart_at必填join_linkcontact_iddescriptionhas_remindernumber_aliasidempotency_key另有 3 个

send_cta_button

Send a message with a call-to-action link button

Send an interactive message carrying one link button. Set exactly one recipient: `number`, `group_id`, or `contact_id`. A link button is the only interactive type WhatsApp accepts on this kind of connection. Reply buttons and list pickers are not available and will not become available, so do not try to emulate them. `body` is the text above the button (up to 1024 characters), `button_text` its label (up to 20), `header` and `footer` optional short lines above and below (up to 60 each). A message with a link button cannot be edited after it is sent: `delete_message` is the only change for it.

输入参数body必填footerheadernumbergroup_idbutton_url必填contact_idbutton_text必填number_aliasidempotency_keyquoted_message_id

reply_to_message

Reply to a message in the same conversation

Reply to an EARLIER message, quoting it, without naming a recipient. Use it to bring context into the conversation: when the conversation has moved on and your answer depends on something said a while ago (by the contact, by you or, in a group, by any participant). To answer the message you just received, do not use this tool: send a normal message with `send_text` and let the chat carry the context, as a person would. The recipient is worked out from the original: an inbound message is answered to its sender, an outbound one to whoever received it, and a group message to the group. The message must belong to the inbox named by `number_alias`; a `message_id` from a different number returns not found. Like a person opening the chat, the number first appears online and its unread messages in that conversation are marked as read, then the reply is sent. Opening the conversation this way is automatic and uses no quota of its own.

输入参数text必填message_id必填number_aliasidempotency_key

edit_text_message

Edit the text of a message already sent

Replace the text of a text message this number already sent. The recipient sees the new text marked as edited. The message keeps its id and its place in the conversation; only the text changes. Only a text message can be edited here. For the caption of an image, video or document use `edit_media_caption`, and for a calendar event `edit_calendar_event` or `cancel_calendar_event`. A location, contact card, link button, sticker or audio message cannot be edited at all: `delete_message` is the only change for them. Using this tool on any message that is not text answers `invalid_request: message_not_editable`. Only a message sent THROUGH Turbo Notify can be edited. One typed on the phone itself belongs to the same number but cannot be edited here, and neither can a message received from someone else. WhatsApp only accepts a change to a text within 15 minutes of sending it. A later edit is refused up front with `invalid_request: edit_window_expired`; send a new message instead. The edit is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the edit happens. Opening the conversation this way is automatic and uses no quota of its own.

输入参数text必填message_id必填number_alias

edit_media_caption

Edit the caption of an image, video or document

Replace the caption of an image, video or document this number already sent. The recipient sees the new caption marked as edited. The file itself stays exactly as it was and the message keeps its id: only the caption changes. There is no way to replace the file; to send a different one, send a new message with `send_media`. Only an image, video or document can be edited this way. Audio and stickers have no caption, and a location, contact card or link button cannot be edited at all: `delete_message` is the only change for them. For the text of a text message use `edit_text_message`. Using this tool on any other message answers `invalid_request: message_not_editable`. Only a message sent THROUGH Turbo Notify can be edited. One typed on the phone itself belongs to the same number but cannot be edited here, and neither can a message received from someone else. WhatsApp only accepts a change to a caption within 15 minutes of sending it. A later edit is refused up front with `invalid_request: edit_window_expired`; send a new message instead. The edit is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the edit happens. Opening the conversation this way is automatic and uses no quota of its own.

输入参数caption必填message_id必填number_alias

edit_calendar_event

Edit a calendar event already sent

Correct a calendar event this number already sent. `name` and `start_at` are always required, plus whatever else the event has: `description`, `end_at`, `join_link`, `location`, `has_reminder` with `reminder_offset_sec`, and `extra_guests_allowed`. This REPLACES the whole event: anything you leave out is removed from it, including its location, reminder and guest setting, so send again every detail you want to keep. The event keeps its place in the chat and the guests keep the answer they already gave. Use an edit to fix a mistake (a typo, the description, a link). When the date, time or place change and the guests must accept again, do not edit: cancel the event with `cancel_calendar_event` and send a new one with `send_calendar_event`, because the old answer does not carry over to a new event. Neither an edit nor a cancel changes the entry a guest already added to their own calendar with the card's "Add to calendar" button. It stays on the old date, and adding again creates a second entry, which only the guest can delete. Only a calendar event can be edited here. Using this tool on any other message answers `invalid_request: message_not_editable`. Only a message sent THROUGH Turbo Notify can be edited. One typed on the phone itself belongs to the same number but cannot be edited here, and neither can a message received from someone else. The 15-minute limit does not apply to a calendar event. The edit is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the edit happens. Opening the conversation this way is automatic and uses no quota of its own.

输入参数name必填end_atlocationstart_at必填join_linkmessage_id必填descriptionhas_remindernumber_aliasreminder_offset_secextra_guests_allowed

cancel_calendar_event

Cancel a calendar event already sent

Cancel a calendar event this number already sent. The event stays in the chat marked as canceled, and WhatsApp tells the guests. Send the same `name` and `start_at` the event has, plus the details you want the canceled card to still show (`description`, `end_at`, `join_link`, `location`, `has_reminder` with `reminder_offset_sec`, `extra_guests_allowed`): the canceled event is shown with what you send here, and anything you leave out disappears from it. Cancel when the date, time or place change and the guests must accept again: cancel the old event here, then send the new one with `send_calendar_event`, because the old answer does not carry over to a new event. To correct a mistake (a typo, the description, a link) use `edit_calendar_event` instead, and the guests keep their answer. A cancel does not change the entry a guest already added to their own calendar with the card's "Add to calendar" button. It stays on the old date, and adding again creates a second entry, which only the guest can delete. Only a calendar event can be canceled. Using this tool on any other message answers `invalid_request: message_not_editable`; to take back a message of another kind, use `delete_message`. Only a message sent THROUGH Turbo Notify can be canceled. One typed on the phone itself belongs to the same number but cannot be canceled here, and neither can a message received from someone else. The 15-minute limit for a text or a caption does not apply to a calendar event. The cancel is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the cancel happens. Opening the conversation this way is automatic and uses no quota of its own.

输入参数name必填end_atlocationstart_at必填join_linkmessage_id必填descriptionhas_remindernumber_aliasreminder_offset_secextra_guests_allowed

delete_message

Delete a message for everyone

Revoke a message for everyone in the conversation. This is WhatsApp's "delete for everyone". It is not reversible, and recipients see a tombstone where the message was, so they can tell something was deleted. Confirm with a human before using it on anything a person has plausibly already read. The number first appears online and its unread messages in that conversation are marked as read, then the message is deleted. Opening the conversation this way is automatic and uses no quota of its own.

输入参数message_id必填number_alias

react_to_message

React to a message with an emoji

Attach an emoji reaction to a message, or remove one. Pass a single emoji character, e.g. `👍`. Passing an empty string removes the reaction this number previously left. A new emoji replaces this number's previous reaction rather than adding beside it; to bring the earlier one back, react with it again. Works on messages in either direction: reacting to something a customer sent is often a cheaper acknowledgement than a reply. The number first appears online and its unread messages in that conversation are marked as read, then the reaction is sent. Opening the conversation this way is automatic and uses no quota of its own. Needs a plan that includes reactions. On a plan that does not, this answers `payment_required: feature_not_available`, and the answer will not change by retrying.

输入参数emoji必填message_id必填number_alias

send_typing_indicator

Show the typing indicator in a conversation

Show "typing…" to a recipient while you prepare an answer. Set exactly one recipient: `number`, `group_id`, or `contact_id`. Useful before a reply that takes a few seconds to compose, so the person on the other end sees something is happening. It expires on its own, so clearing it is optional: pass `is_typing=false` only if you decide not to answer after all and want the indicator gone at once. There is no duration argument. WhatsApp decides how long it lives. Starting to type also opens the conversation: the number appears online, its unread messages in that conversation are marked as read, and after a short pause "typing…" appears, so it never shows over unread messages. Stopping (`is_typing=false`) only clears the indicator and reads nothing. Opening the conversation this way is automatic and uses no quota of its own. Needs a plan that includes the typing indicator, and showing it in a group needs one that includes group messaging as well. Without either, this answers `payment_required: feature_not_available`, and the answer will not change by retrying.

输入参数numbergroup_idis_typingcontact_idnumber_alias

mark_as_read

Clear a conversation's unread backlog

Mark a conversation's unread messages as read, without sending anything. Set exactly one recipient: `number`, `group_id`, or `contact_id`. This is independent of the automatic mark-as-read that already happens when you send, reply, react, edit, delete or start typing (the number also appears online first): use it to clear an unread badge on its own, for example when handing a conversation to a human who has already seen it. No message is sent and no `message.*` event is produced, but the sender sees the messages as read (blue ticks) on WhatsApp. Needs a plan that includes read receipts (BUSINESS and above). Without it this answers `payment_required: feature_not_available`, and the answer will not change by retrying.

输入参数numbergroup_idcontact_idnumber_alias

list_messages

List messages in an inbox

List messages for one number, newest first, received and sent. Each item says who it is from and to, which number it belongs to, its `direction`, `content_type`, `origin`, a short `preview`, `at`, `status` and `content_expired`. Mentions, transcription and full media detail are only on `get_message`, so fetch the one message you care about rather than paging for detail. To read a group, find its `grp_` id with `list_groups` and pass it as `group_id`. `contact_id` returns what that contact sent or received, which in a group includes what they said there. `direction` keeps only `inbound` or `outbound`. Filters combine: `group_id` with `contact_id` answers what one person said in one group. Ids belong to one number, so use the same `number_alias` you got them from: an id from another number answers not found. For the next page, pass back the `cursor` from the previous response together with the same filters. Content older than the plan's retention window comes back with `preview` empty and `content_expired` true. The message, its sender and its group stay listed for 12 months.

输入参数limitcursorgroup_iddirectioncontact_idnumber_alias

get_message

Get one message in full

Fetch one message with its full content, parties and mentions. The answer includes the number's stable `number_id` (`num_…`) next to `number_alias`. `at` is when the message actually happened on WhatsApp; `status.at` is when it reached its current delivery status and keeps moving as receipts arrive. They answer different questions and are not interchangeable. Returns not found if the id belongs to a different number. Past the organization's content retention window (1 hour by default, up to 72 hours depending on the plan) the message is still returned, with `content_expired` true and its content empty.

输入参数message_id必填number_alias

get_message_status

Get a message's delivery status

Check where a message got to: queued, sent, delivered, read, failed. Status only moves forward. A failed status always carries an error code, and for a few media and reaction problems it also carries a hint naming the field to change; most failures give the code alone. Read the code before retrying, because most failures repeat identically.

输入参数message_id必填number_alias

get_message_receipts

Get per-recipient delivery and read receipts

See who received and who read a message. Most useful for group messages, where the single status on the message cannot express that some members read it and others have not. Requires a plan that includes read receipts; on plans without it the call is refused with `feature_not_available`, which no retry will fix.

输入参数message_id必填number_alias

get_message_reactions

Get the reactions on a message

List the emoji reactions on a message, and who left each one. Each entry names the reactor and the emoji. Reactions are replaced rather than accumulated per person: someone who reacts twice leaves one entry showing their latest emoji, so this is a current snapshot and not a history. An empty list is the normal answer for most messages, and is not an error.

输入参数message_id必填number_alias

get_message_responses

Get the guests' answers to a calendar event

See how the guests answered a calendar event: going, not going or maybe. Only works on a calendar event; any other message answers `invalid_request: message_not_calendar`. The answer has `counts` (how many guests are going, not going or maybe, and how many extra guests those who are going or maybe are bringing) and `responses`, one entry per guest with who they are, their answer, any extra guests they bring and when they answered. A guest who changes their mind leaves one entry showing the latest answer, so this is a current snapshot and not a history. To be told as answers come in, watch for `message.calendar_responded` with `list_events`. An event nobody has answered yet returns zero counts and an empty list, which is the normal answer and not an error.

输入参数message_id必填number_alias

get_message_quota

Get a number's message quota for this billing cycle

Check how much of this number's message allowance is left. The quota is **per number**, not per organization: an organization with three numbers has three separate allowances. It resets at `cycle_end`, which is the organization's real billing boundary rather than a rolling window, so `resets_at` is a date you can plan around. Only messages sent through Turbo Notify consume it. Messages someone sends from the phone itself do not.

输入参数number_alias

list_contacts

List an inbox's contacts

List the people this number has exchanged messages with. Filter by `phone` or by `name`. They are separate filters, not one search box: `name` matches the contact's name, `phone` matches the number. `source` restricts to how the contact first appeared: `inbound` (they messaged this number), `outbound` (this number messaged them), `mention` (named in a group message) or `self` (the number's own contact card). `contacted_in_cycle=true` narrows the list to exactly the people counting against this number's contact quota this billing cycle: those messaged through Turbo Notify inside the cycle, whether newly or again. Use it to see what the quota gauge is actually counting.

输入参数namelimitphonecursorsourcenumber_aliascontacted_in_cycle

get_contact

Get one contact

Fetch one contact: name, phone number and activity timestamps. `first_messaged_at` is when a message from this number first reached them on WhatsApp, and `last_interaction_at` is the last message that passed in EITHER direction, so it moves when they write to you as well. A message that failed before reaching WhatsApp moves neither. `refresh` shows the last `refresh_contact` you asked for: when it was requested, when it finished, and its `outcome` (`updated`, `unchanged`, `number_not_connected` or `unavailable`). While `completed_at` is null it is still running. The id must belong to the inbox named by `number_alias`. An id from a different number returns not found even though the person exists.

输入参数contact_id必填number_alias

get_contact_profile_picture

Get a contact's profile picture

Get a contact's profile picture, or the reason there is none. The answer carries `kind`: `available` with a temporary URL, or `unavailable` with a reason. `no_storage` means the organization has not configured object storage, which is a setup step rather than a fault with this contact. The person may also simply have no picture, or have hidden it in their privacy settings.

输入参数contact_id必填number_alias

refresh_contact

Refresh a contact's WhatsApp name

Ask WhatsApp for this contact's current name. Only the name is refreshed. The profile picture updates on its own the next time this number exchanges a message with the contact, and this does not change how messages reach them. It answers right away with `requested_at`; the refresh then runs in the background. Call `get_contact` and read `refresh`: when `completed_at` is set, `outcome` says what happened (`updated` means the name changed, `unchanged` means it was already current, `number_not_connected` means the number was offline, `unavailable` means WhatsApp did not answer). This is the one contact tool that is not read-only: it queries WhatsApp over the live connection and spends the organization's per-minute refresh budget, so it needs a key with contacts write access. Do not call it in a loop over a list. Refresh the one contact whose name looks stale.

输入参数contact_id必填number_alias

get_contact_quota

Get a number's contact quota for this billing cycle

Check how much of this number's contact allowance is left. A contact counts once per billing cycle when this number messages them through Turbo Notify, new **or** returning. Messaging the same person again in a later cycle counts again. Like the message quota, this is per number rather than per organization, and it resets at `cycle_end`.

输入参数number_alias

list_groups

List the groups a number belongs to

List the WhatsApp groups this number is a member of. The list is built when the number connects: every group it belongs to is picked up then, whether or not anything has been said in it. A group joined since then is picked up from the join itself, without waiting for anyone to speak in it. `name` filters by group name. `include_left=true` also returns groups this number has left, which are otherwise hidden.

输入参数namelimitcursorinclude_leftnumber_alias

get_group

Get one group

Fetch a group: its name, description, picture and settings. Returns how many participants it has, not who they are. Membership is deliberately not readable: it arrives as it changes, on the `group.participants_added` / `_removed` / `_promoted` / `_demoted` events, which `list_events` can read.

输入参数group_id必填number_alias

get_group_picture

Get a group's picture

Get a group's picture, or the reason there is none. Same shape as a contact's picture. The answer carries `kind`: `available` with a temporary URL, or `unavailable` with a reason. `no_storage` means the organization has not configured object storage.

输入参数group_id必填number_alias

list_numbers

List the organization's WhatsApp numbers

List every WhatsApp number this organization has, with its status. Start here when you do not know which aliases exist. The main number is always `main`; extras carry the alias the customer chose, and that alias is what every other tool's `number_alias` expects. Each number also carries `id` (`num_…`), which never changes, even when the alias is renamed. Keep it if you need to recognise a number later: events carry the same value as `number_id`, and the other tools accept either one as `number_alias`. To find the numbers that need attention, pass `status`, for example `disconnected`. Only `connected` numbers can send. `status.at` is when the number entered its current state, which is how you tell a fresh disconnection from one that has been down for hours.

输入参数status

get_number

Get one number and its connection status

Check one number: its phone number, status, and when that status began. `id` (`num_…`) is the number's permanent identifier. The alias can be renamed, the `id` cannot. Either one works as `number_alias`. `connected` means it can send right now. `connecting` means a connection was just requested and is being set up; check again in a few seconds. `reconnecting` means the number lost its connection and Turbo Notify is bringing it back by itself: there is nothing to do but wait and check again. Until it is `connected` again, sends are refused with `number_not_connected`. `disconnected` means the link to the phone still exists but the number is offline, so `connect_number` brings it back. `inactive` is the same for a number that was linked and has not been brought online yet: `connect_number` brings it online too. `pending` means the number was never linked to a phone, and `no_main_number` (what a brand-new account gets for `main`, and `list_numbers` is empty) means there is no number at all yet: either way a person has to link it from the Numbers page of the dashboard, so tell them and do not retry the send meanwhile. `revoked` means the phone unlinked the device, or another connection took it over. `failed` means a pairing did not complete, and `reason` says why (for example `pairing_timeout` when the code or QR code was not used in time, `paired_phone_mismatch` when another WhatsApp used it). Either one has to be paired again by a person holding that phone: from the dashboard (main or extra number) or, for an extra number, through the API's pairing endpoint, by code or by QR code. No tool here pairs a number: it needs a person with the phone. `pairing` means a person is pairing the number right now. The code or QR code they use is not part of this answer: it belongs to that person. Check again later; the status moves to `connected`, or to `failed` with a `reason`.

输入参数number_alias

connect_number

Bring a number back online

Reconnect a number that is currently offline. Uses the credentials already stored for the number, so no QR code and no human are involved. Use it when `get_number` reports `disconnected` and you want to send. Returns immediately, before the connection is established. The answer has the number's `alias`, `status` set to `connecting` and `at`, the moment the request was accepted. The number reaches `connected` a few seconds later: poll `get_number`, which stays the source of the current status and of its own `at`, rather than assuming it worked. It works the same on every number and never charges. On an extra number scheduled for removal it does not change the removal: the number keeps working until its date. Cancelling a removal is not available to agents. This cannot rescue a `revoked` number: that one was unlinked from the phone and needs a person to pair it again. Calling it again changes nothing further. A number that is already `connected` or `connecting` answers with an `already_connected` or `already_connecting` error, which means the goal is met, not that the call failed.

输入参数number_alias

disconnect_number

Take a number offline without unlinking it

Take a number offline deliberately, keeping its link to the phone. The stored credentials survive, so `connect_number` brings it straight back with no pairing. Nothing is deleted. Disconnecting does not stop billing: an extra number is billed every cycle until it is removed. Returns immediately, before the number is offline. The answer has the number's `alias`, `status` set to `disconnecting` and `at`, the moment the request was accepted. `disconnecting` is not a status that `get_number` reports: it keeps showing the previous status and its own `at` until the number is offline. While disconnected the number cannot send or receive, and messages addressed to it are refused rather than queued. Deliberate: a message held for an unknown period and delivered later is worse than one that failed clearly. Calling it again changes nothing further. A number that is already offline answers with an `already_disconnected` error, which means the goal is met, not that the call failed.

输入参数number_alias

get_usage

Get the organization's usage across all numbers

Get message and contact usage for the whole organization. The top-level totals are the **sum across every live number**: an organization with two numbers on a 50,000-message plan reports a quota of 100,000, not 50,000. The `numbers[]` breakdown shows each number separately, which is what you need to find the one that is close to its own ceiling, so a healthy-looking total can hide a number at 99%. For one number, `get_message_quota` and `get_contact_quota` are more direct.

无需输入参数。

list_events

Poll the organization's event log

Poll everything that happened: messages, contacts, groups, numbers, quotas. This is how an agent learns about inbound messages without hosting a webhook endpoint. **Events come back newest first, so polling for new ones uses `ending_before`, not `starting_after`.** Typical loop: call with no cursor and remember the id of the FIRST event in the page (the newest one); next time, pass that id as `ending_before` to get only what has happened since. An empty page means you are caught up. `starting_after` does the opposite: it walks BACKWARD into history, which is what you want for reading a backlog, and never what you want for noticing something new. Passing the last id you handled to `starting_after` in a loop returns steadily older events and never reaches the present. `request_id` returns everything that came of one earlier call. A write tool's result carries `request_id` when the platform issued one; keep it and you can ask later what became of that specific send. `number_id` restricts to one number and keeps matching its events after the number is renamed. `number_alias` restricts to one inbox by the alias it had when the event happened, so a rename hides the older events; the two can be combined. Either one excludes `storage.*` and `media.unavailable` events, which belong to the organization rather than to any one number. Most events carry a `number_alias` telling you which inbox they belong to, and `number_id` (`num_…`), the same number's permanent identifier. The alias is the one at the time of the event and can be renamed later; the `number_id` cannot. You need one of them: message, contact and group ids are unique but not self-addressing, so an id alone does not tell you which number's tools to use with it, and both work as `number_alias` in the other tools. Read the fields defensively rather than assuming they are there: `storage.*` and `media.unavailable` belong to the organization as a whole and have no single number to name, so on those the keys are a

输入参数limitnumber_idevent_typerequest_idcreated_gtecreated_lteevent_typesnumber_aliasending_beforestarting_after

版本历史

1
  1. v0.2.1最新Nov 6, 2026

评分

已满足适用检查项的 100%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分

其他 Turbonotify 集成