# Turbo Notify: WhatsApp for AI agents (remote · mcp.turbonotify.com)

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

- Trust score: 77/100 (medium)
- Change this week: +40
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-11-04

## Components

- remote · `mcp.turbonotify.com`: 77/100 (this document), [markdown](https://verifymcp.io/servers/com-turbonotify-whatsapp/mcp.md), [page](https://verifymcp.io/servers/com-turbonotify-whatsapp/mcp)

## Channel facts

- Endpoint: `https://mcp.turbonotify.com`
- Transports: `streamable-http`
- Auth: `required`
- Version: `0.1.4`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-11-04.

- **Endpoint Security**: 92/100
  - The endpoint's TLS certificate is valid, in date, and uses a strong key.
  - Authorisation is enforced on tool calls, advertised via RFC 9728 protected-resource metadata. Discovery is public, which costs nothing: no tool can be invoked without a token.
  - HTTPS is enforced; there's no plaintext access path.
  - HSTS check failed: the Strict-Transport-Security header is absent.
  - DNSSEC check failed: this domain isn't protected by DNSSEC.
  - The authorisation server supports Client ID Metadata Documents, the current MCP client-registration mechanism.
- **Transport & Reachability**: 100/100
  - Verified streamable-http transport via a live MCP handshake.
- **Schema Quality & AI Usability**: 61/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 15099 tokens (~419/item across 36 items; 36 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 7/100
  - Stability observed for 2 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 8 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - An AI judge read all 37 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### How do I install the Turbo Notify: WhatsApp for AI agents MCP server?

Turbo Notify: WhatsApp for AI agents is a hosted endpoint at https://mcp.turbonotify.com/, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add --transport http com-turbonotify-whatsapp 'https://mcp.turbonotify.com/'
```

### Cursor

```json
{
  "mcpServers": {
    "com-turbonotify-whatsapp": {
      "url": "https://mcp.turbonotify.com/"
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "com-turbonotify-whatsapp": {
      "type": "http",
      "url": "https://mcp.turbonotify.com/"
    }
  }
}
```

### Codex

```toml
[mcp_servers.com-turbonotify-whatsapp]
url = "https://mcp.turbonotify.com/"
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "com-turbonotify-whatsapp": {
      "type": "remote",
      "url": "https://mcp.turbonotify.com/",
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add com-turbonotify-whatsapp --url 'https://mcp.turbonotify.com/' --transport streamable-http
```

### Hermes

```yaml
mcp_servers:
  com-turbonotify-whatsapp:
    url: "https://mcp.turbonotify.com/"
```

### Netclaw

```json
{
  "McpServers": {
    "com-turbonotify-whatsapp": {
      "Transport": "http",
      "Url": "https://mcp.turbonotify.com/"
    }
  }
}
```

### Vellum

```bash
assistant mcp add com-turbonotify-whatsapp -t streamable-http -u 'https://mcp.turbonotify.com/'
```

### Other

```json
{
  "mcpServers": {
    "com-turbonotify-whatsapp": {
      "type": "http",
      "url": "https://mcp.turbonotify.com/"
    }
  }
}
```

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-11-04 (score 77, +1)

- [security] Tool “get_number” rewrote its description, which is the text the model reads

### 2026-11-03 (score 76, 0)

- [functional improvement] Stability: unverified → 0.03
- [functional] This server's schema is too large to store in full, so we cannot compare its tools day to day

### 2026-11-02 (score 76, +39)

- [security improvement] Transport: unverified → pass
- [security improvement] Injection markers: unverified → pass
- [security] First check of Judged manipulation: pass
- [security] Authorization: Authorisation is enforced on tool calls, advertised via RFC 9728 protected-resource metadata. Discovery is public, which costs nothing: no tool can be invoked without a token.
- [functional improvement] MCP protocol: unverified → pass
- [functional improvement] Tool coverage: unverified → 100
- [functional] First check of Schema quality: fail
- [functional] First check of Schema quality: excellent
- [functional] First check of Schema quality: fail
- [functional] First check of Destructive annotations: 100
- [functional] First check of Tool coverage: 100
- [functional] First check of Tool coverage: 100
- [functional] This server's schema is too large to store in full, so we cannot compare its tools day to day

### 2026-10-28 (score 37)

First indexed and scored.

## MCP tools (36)

### `send_text` (~636 tokens)

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.

Input parameters:

- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `idempotency_key`: Any printable ASCII text you choose, 1 to 255 characters, for example the id of the event you are answering. Reusing it for the same number suppresses a duplicate if you retry. Without it, retrying a…
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `quoted_message_id`: Quote an earlier message (`msg_…`) so it appears above yours. Do this only when your text depends on something said a while ago, yours or theirs; do not quote the message you are answering right now,…
- `text` (string, required): The message text, 1 to 4096 characters. Write `@<number>` to mention someone.

### `send_media` (~765 tokens)

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.

Input parameters:

- `caption`: Text shown with an image, video or document, up to 1024 characters. Audio and stickers cannot carry one.
- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `filename`: The file name the recipient sees on a document, up to 255 characters. Documents only.
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `idempotency_key`: Any printable ASCII text you choose, 1 to 255 characters, for example the id of the event you are answering. Reusing it for the same number suppresses a duplicate if you retry. Without it, retrying a…
- `kind` (required): What the file is: image, video, audio, document or sticker.
- `mime_type`: The media's MIME type, recorded alongside the message. It does not change how the file is sent: the type WhatsApp receives comes from what the URL's own server serves. Optional.
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `quoted_message_id`: Quote an earlier message (`msg_…`) so it appears above yours. Do this only when your text depends on something said a while ago, yours or theirs; do not quote the message you are answering right now,…
- `url` (string, required): Link to the file to send. It must still be reachable when the message goes out.

### `send_location` (~547 tokens)

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.

Input parameters:

- `address`: The place's address, up to 500 characters.
- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `idempotency_key`: Any printable ASCII text you choose, 1 to 255 characters, for example the id of the event you are answering. Reusing it for the same number suppresses a duplicate if you retry. Without it, retrying a…
- `latitude` (number, required): Latitude, between -90 and 90.
- `longitude` (number, required): Longitude, between -180 and 180.
- `name`: A name for the place, up to 200 characters.
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `quoted_message_id`: Quote an earlier message (`msg_…`) so it appears above yours. Do this only when your text depends on something said a while ago, yours or theirs; do not quote the message you are answering right now,…
- `url`: A link shown beside the pin.

### `send_contact_card` (~625 tokens)

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.

Input parameters:

- `address`: The postal address on the card, up to 500 characters.
- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `display_name` (string, required): The name shown on the card, 1 to 200 characters.
- `emails`: The email addresses on the card, up to 10.
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `idempotency_key`: Any printable ASCII text you choose, 1 to 255 characters, for example the id of the event you are answering. Reusing it for the same number suppresses a duplicate if you retry. Without it, retrying a…
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `organization`: The company shown on the card, up to 200 characters.
- `phones` (array, required): The phone numbers on the card, 1 to 10, each in E.164 form with the country code first (e.g. `5511999999999`).
- `quoted_message_id`: Quote an earlier message (`msg_…`) so it appears above yours. Do this only when your text depends on something said a while ago, yours or theirs; do not quote the message you are answering right now,…
- `title`: The job title shown on the card, up to 100 characters.

### `send_calendar_event` (~927 tokens)

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`.

Input parameters:

- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `description`: Details shown on the event, up to 2000 characters.
- `end_at`: When the event ends. ISO 8601 with an offset, and later than `start_at`.
- `extra_guests_allowed`: Set to true to let guests bring extra people. Off when left out.
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `has_reminder`: Set to true to remind guests before the event. Off when left out.
- `idempotency_key`: Any printable ASCII text you choose, 1 to 255 characters, for example the id of the event you are answering. Reusing it for the same number suppresses a duplicate if you retry. Without it, retrying a…
- `join_link`: A link guests can open to join, for example a video call.
- `location`: Where the event takes place: a map pin with an optional name and address.
- `name` (string, required): The event's title, up to 200 characters.
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `quoted_message_id`: Quote an earlier message (`msg_…`) so it appears above yours. Do this only when your text depends on something said a while ago, yours or theirs; do not quote the message you are answering right now,…
- `reminder_offset_sec`: How many seconds before the event the reminder goes off, for example 600 for ten minutes. Only takes effect together with `has_reminder`.
- `start_at` (string, required): When the event starts. ISO 8601 with an offset, for example `2026-09-10T14:00:00Z`. A time without an offset is rejected.

### `send_cta_button` (~628 tokens)

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.

Input parameters:

- `body` (string, required): The text above the button, 1 to 1024 characters.
- `button_text` (string, required): The label on the button, 1 to 20 characters.
- `button_url` (string, required): The link the button opens when the recipient taps it.
- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `footer`: A short line below the text, up to 60 characters.
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `header`: A short line above the text, 1 to 60 characters.
- `idempotency_key`: Any printable ASCII text you choose, 1 to 255 characters, for example the id of the event you are answering. Reusing it for the same number suppresses a duplicate if you retry. Without it, retrying a…
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `quoted_message_id`: Quote an earlier message (`msg_…`) so it appears above yours. Do this only when your text depends on something said a while ago, yours or theirs; do not quote the message you are answering right now,…

### `reply_to_message` (~398 tokens)

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.

Input parameters:

- `idempotency_key`: Any printable ASCII text you choose, 1 to 255 characters, for example the id of the event you are answering. Reusing it for the same number suppresses a duplicate if you retry. Without it, retrying a…
- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `text` (string, required): The text of your reply, 1 to 4096 characters.

### `edit_text_message` (~423 tokens)

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.

Input parameters:

- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `text` (string, required): The new text. It replaces the whole text of the message.

### `edit_media_caption` (~462 tokens)

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.

Input parameters:

- `caption` (string, required): The new caption, up to 1024 characters. It replaces the whole caption. Writing `@<number>` mentions that person, as in a caption on send.
- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `edit_calendar_event` (~734 tokens)

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.

Input parameters:

- `description`: Details shown on the event, up to 2000 characters.
- `end_at`: When the event ends. ISO 8601 with an offset, and later than `start_at`.
- `extra_guests_allowed`: Set to true to let guests bring extra people. Off when left out.
- `has_reminder`: Set to true to remind guests before the event. Off when left out.
- `join_link`: A link guests can open to join, for example a video call.
- `location`: Where the event takes place: a map pin with an optional name and address.
- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `name` (string, required): The event's title, up to 200 characters.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `reminder_offset_sec`: How many seconds before the event the reminder goes off, for example 600 for ten minutes. Only takes effect together with `has_reminder`.
- `start_at` (string, required): When the event starts. ISO 8601 with an offset, for example `2026-09-10T14:00:00Z`. A time without an offset is rejected.

### `cancel_calendar_event` (~751 tokens)

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.

Input parameters:

- `description`: Details shown on the event, up to 2000 characters.
- `end_at`: When the event ends. ISO 8601 with an offset, and later than `start_at`.
- `extra_guests_allowed`: Set to true to let guests bring extra people. Off when left out.
- `has_reminder`: Set to true to remind guests before the event. Off when left out.
- `join_link`: A link guests can open to join, for example a video call.
- `location`: Where the event takes place: a map pin with an optional name and address.
- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `name` (string, required): The event's title, up to 200 characters.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `reminder_offset_sec`: How many seconds before the event the reminder goes off, for example 600 for ten minutes. Only takes effect together with `has_reminder`.
- `start_at` (string, required): When the event starts. ISO 8601 with an offset, for example `2026-09-10T14:00:00Z`. A time without an offset is rejected.

### `delete_message` (~204 tokens)

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.

Input parameters:

- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `react_to_message` (~293 tokens)

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.

Input parameters:

- `emoji` (string, required): A single emoji, for example `👍`. An empty string removes the reaction this number left before.
- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `send_typing_indicator` (~525 tokens)

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.

Input parameters:

- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `is_typing` (boolean): `true` shows "typing…" (the default), `false` clears it.
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `mark_as_read` (~430 tokens)

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.

Input parameters:

- `contact_id`: Recipient contact id (`ct_…`), from `list_contacts`. A contact id belongs to one number: an id from one inbox does not address the same person in another. Set exactly one of `number`, `group_id` or `…
- `group_id`: Recipient group id (`grp_…`), from `list_groups`. A group id belongs to one number: an id from one inbox does not address the same group in another. Set exactly one of `number`, `group_id` or `contac…
- `number`: Recipient phone number in E.164 form, digits only, country code first (e.g. `5511999999999`). Set exactly one of `number`, `group_id` or `contact_id`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `list_messages` (~535 tokens)

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.

Input parameters:

- `contact_id`: Only the messages this contact sent or received (`ct_…`, from `list_contacts`). In a group that includes what the contact said there. The contact must be one of this number's: an id from another numb…
- `cursor`: Opaque cursor for the next page. Pass back the `cursor` from the previous response, with the same filters; omit it for the first page.
- `direction`: Only messages the number received (`inbound`) or sent (`outbound`). Absent means both.
- `group_id`: Only the messages of one group (`grp_…`, from `list_groups`). The group must be one of this number's: an id from another number answers not found. Can be combined with `contact_id`.
- `limit`: How many items to return, 1-200. Defaults to 50.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_message` (~235 tokens)

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.

Input parameters:

- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_message_status` (~176 tokens)

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.

Input parameters:

- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_message_receipts` (~166 tokens)

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.

Input parameters:

- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_message_reactions` (~179 tokens)

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.

Input parameters:

- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_message_responses` (~272 tokens)

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.

Input parameters:

- `message_id` (string, required): Message id (`msg_…`), as returned by a send or by `list_messages`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_message_quota` (~166 tokens)

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.

Input parameters:

- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `list_contacts` (~421 tokens)

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.

Input parameters:

- `contacted_in_cycle`: When true, return only the people who count against this number's contact quota in the current billing cycle: those messaged through Turbo Notify inside the cycle, new or returning. Use it to see exa…
- `cursor`: Opaque cursor for the next page. Pass back the `cursor` from the previous response, with the same filters; omit it for the first page.
- `limit`: How many items to return, 1-200. Defaults to 50.
- `name`: Only contacts whose name matches.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…
- `phone`: Only contacts whose phone number matches.
- `source`: Only contacts that first appeared this way: `inbound` (they messaged this number), `outbound` (this number messaged them), `mention` (named in a group message) or `self` (the number's own contact car…

### `get_contact` (~258 tokens)

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.

Input parameters:

- `contact_id` (string, required): Contact id (`ct_…`), from `list_contacts`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_contact_profile_picture` (~180 tokens)

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.

Input parameters:

- `contact_id` (string, required): Contact id (`ct_…`), from `list_contacts`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `refresh_contact` (~289 tokens)

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.

Input parameters:

- `contact_id` (string, required): Contact id (`ct_…`), from `list_contacts`.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_contact_quota` (~146 tokens)

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`.

Input parameters:

- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `list_groups` (~261 tokens)

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.

Input parameters:

- `cursor`: Opaque cursor for the next page. Pass back the `cursor` from the previous response, with the same filters; omit it for the first page.
- `include_left`: Include groups this number has left. Off by default, so a plain listing shows only groups the number is currently in.
- `limit`: How many items to return, 1-200. Defaults to 50.
- `name`: Only groups whose name matches.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_group` (~195 tokens)

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.

Input parameters:

- `group_id` (string, required): Group id (`grp_…`), from `list_groups`. It belongs to the number you listed it under: passing it under a different number answers not found, which means wrong inbox rather than deleted.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_group_picture` (~181 tokens)

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.

Input parameters:

- `group_id` (string, required): Group id (`grp_…`), from `list_groups`. It belongs to the number you listed it under: passing it under a different number answers not found, which means wrong inbox rather than deleted.
- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `list_numbers` (~208 tokens)

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.

Input parameters:

- `status`: Only numbers currently in this status, for example `disconnected` to find the numbers that need attention.

### `get_number` (~536 tokens)

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`.

Input parameters:

- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `connect_number` (~323 tokens)

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.

Input parameters:

- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `disconnect_number` (~287 tokens)

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.

Input parameters:

- `number_alias` (string): Which of the organization's WhatsApp numbers this acts on: `main`, the alias of an extra number, or the number's `id` (`num_…`), which stays the same when the alias is renamed. Call `list_numbers` to…

### `get_usage` (~129 tokens)

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` (~1081 tokens)

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…

Input parameters:

- `created_gte`: Only events created at or after this instant. ISO 8601 with an offset, e.g. `2026-09-01T00:00:00Z`.
- `created_lte`: Only events created at or before this instant. ISO 8601 with an offset, e.g. `2026-09-10T00:00:00Z`.
- `ending_before`: Catch up: return events NEWER than this id. Pass the newest id you have already handled to see only what has happened since. This is the one to poll with.
- `event_type`: Filter to exactly one event type, e.g. `message.received`. Common ones: `message.sent`, `message.delivered`, `message.read`, `message.received`, `message.reacted`, `message.edited` (a text or caption…
- `event_types`: Filter to several event types at once, e.g. `message.sent` and `message.delivered`. Same vocabulary as `event_type`. The API accepts at most 20.
- `limit`: How many events to return, 1-100. Defaults to 25.
- `number_alias`: Restrict results to one inbox by the alias it had when the event happened, so events from before a rename are missed. Use `number_id` to follow a number across renames. **Absent means every number**,…
- `number_id`: Restrict results to one number by its `id` (`num_…`, as returned by `list_numbers`). It keeps matching the number's events after the number is renamed, unlike `number_alias`, which matches the alias…
- `request_id`: Return only the events that came of one earlier call (`req_…`). A write tool's result carries `request_id` when the platform issued one; keep it and you can ask later what became of that send.
- `starting_after`: Read further back: return events OLDER than this id. This is the one for walking a backlog, and the wrong one for noticing something new.

## Diagnostics

Captured diagnostic sections: TLS, DNSSEC, Authorisation, Transports. The full working is on the page: https://verifymcp.io/servers/com-turbonotify-whatsapp/mcp#diagnostics

## Score history

- 2026-11-04: 77
- 2026-11-03: 76
- 2026-11-02: 76
- 2026-11-01: 37
- 2026-10-31: 37
- 2026-10-30: 37
- 2026-10-29: 37
- 2026-10-28: 37

## Common questions

### What is the Turbo Notify: WhatsApp for AI agents MCP server?

Turbo Notify: WhatsApp for AI agents is an MCP server listed in the public MCP registry as com.turbonotify/whatsapp. Send and receive WhatsApp messages from your AI agent, on your own number, with Turbo Notify. This page covers its hosted endpoint (https://mcp.turbonotify.com).

### Is the Turbo Notify: WhatsApp for AI agents MCP server safe to use?

Turbo Notify: WhatsApp for AI agents scores 77 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Turbo Notify: WhatsApp for AI agents MCP server expose?

Turbo Notify: WhatsApp for AI agents exposes 36 tools: send_text, send_media, send_location, send_contact_card, send_calendar_event, and 31 more. Their descriptions and schemas cost roughly 14,572 tokens of context every time the server is loaded.

### Does the Turbo Notify: WhatsApp for AI agents MCP server require authentication?

Yes. Turbo Notify: WhatsApp for AI agents asked us for credentials when we connected, so you will need to authorise it in your MCP client before it can do anything.

### Is the Turbo Notify: WhatsApp for AI agents MCP server still maintained?

Turbo Notify: WhatsApp for AI agents is still listed as active in the MCP registry. We last reached this channel on 4 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- Remote endpoint: https://mcp.turbonotify.com/
- Repository: https://github.com/turbo-notify/agent-skills
- Website: https://docs.turbonotify.com/en/agents/mcp-server/
- Changelog RSS feed: https://verifymcp.io/servers/com-turbonotify-whatsapp/mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/com-turbonotify-whatsapp/mcp.json
- HTML version of this page: https://verifymcp.io/servers/com-turbonotify-whatsapp/mcp
