Skip to content

MCP server

Turbo Notify exposes its public API as an MCP (Model Context Protocol) (opens in a new tab) server. Your AI agent gets 36 tools (send a message, reply, mark a chat as read, look up a contact, check quota, reconnect a number) and chooses which to use at each step.

Each tool calls one documented endpoint of the API, with your credential. Quotas, plan limits, sending pace and error codes are the same as in the REST API, and MCP has no separate charge.

When to use

You are buildingUse
An AI agent that decides what to do (support agent, assistant, triage)MCP server
A deterministic integration (transactional sends, scheduled job, ETL)REST API
Something that needs to react to incoming messagesWebhook to receive, plus MCP or the API to act

A support agent usually combines both sides: the webhook tells your application a message arrived, and the agent answers through the MCP tools. The example repository does that.

Connection

Addresshttps://mcp.turbonotify.com
TransportStreamable HTTP
Sign in with your accountJust the address. The client opens the Turbo Notify sign-in page, you sign in and approve access
API keyAuthorization: Bearer YOUR_KEY header on every request

Prefer signing in with your account. In Claude, Claude Code and other clients that can sign in to remote servers, add just the address. You sign in to Turbo Notify, choose what the agent can do and approve. There is no key to copy and no file to edit. Use an API key in clients without that option and for agents that run on their own, such as a support agent that answers around the clock.

The two differ in who owns the access. When you sign in, the access belongs to a person: the app acts on behalf of whoever approved it, in the organization that person chose, and stops working if they are removed from the organization or reset their password. An API key belongs to the organization: it keeps working even if whoever created it is removed. If the agent has to keep running through a change of team, use a key.

The AI agents (MCP) (opens in a new tab) screen in the dashboard has the configurations below ready to copy and shows which tools each of your keys can reach.

Signing in with your account (recommended). From the terminal, add the server without a key:

Terminal window
claude mcp add --transport http turbo-notify https://mcp.turbonotify.com

Then run /mcp inside Claude Code, choose turbo-notify and follow the steps in your browser: you sign in to Turbo Notify and approve access.

With an API key. Add the header, and Claude Code uses the key instead of asking you to sign in:

Terminal window
claude mcp add --transport http turbo-notify https://mcp.turbonotify.com --header "Authorization: Bearer YOUR_KEY"

Or, with a key, in the project’s .mcp.json file. The "type": "http" field is required for a remote server:

.mcp.json
{
"mcpServers": {
"turbo-notify": {
"type": "http",
"url": "https://mcp.turbonotify.com",
"headers": {
"Authorization": "Bearer YOUR_KEY"
}
}
}
}

Managing app access

Each app you connect by signing in appears in Connected apps (opens in a new tab) in the dashboard, with the option to disconnect it. Disconnecting takes effect within a few minutes. After that, the app has to ask for your approval again before it works.

Only owners and admins of the organization can connect an app.

An app’s access lasts up to 90 days, or 30 days without use. After that the app stops working and has to ask for your approval again.

For an agent that runs on its own, use an API key: it does not need a new approval every 90 days.

Resetting your password disconnects every app connected to your account: each one has to ask for your approval again.

If the person who connected an app is removed from the organization, the app is disconnected and an owner or admin has to connect it again. API keys are not affected.

Connecting the same app again, from another device, keeps the earlier connection. Each app, person and organization can have at most 10 live connections. Beyond that, the least recently used one is disconnected (one never used goes first).

On the approval screen, the six read and send permissions come ticked. Manage numbers (connecting a number) and Manage contacts (refreshing a contact’s name) appear only when the app asks for them and come unticked. They change your account, but connecting and disconnecting a number never charge a fee. Tick them only if the agent needs them.

Some apps, such as ChatGPT, also ask for your email address. That choice appears as a separate ticked box, and you can untick it: the app still connects, without your address. The box appears only when your email address is verified; otherwise the app connects without it.

Key and scopes

When you sign in with your account, you choose the permissions on the approval screen. The ones that change the account (reconnecting a number, refreshing a contact) appear only if the app asks for them and start unticked. With a key, use a regular API key (opens in a new tab) . The Full access template reaches all 36 tools, but an agent is better off with a key that has only what it needs.

When you create the key in the dashboard, the AI agent (MCP) template already selects the recommended set for a support agent: messages:send, messages:read, contacts:read, groups:read, numbers:read and events:read. It leaves out:

  • numbers:write: reconnecting and disconnecting a number is a decision for whoever runs the account, and it does not generate a charge.
  • contacts:write: refreshing a contact makes a real lookup on WhatsApp.

Each tool requires the scope shown in the catalog. Two rules often surprise people: replying, editing, deleting, reacting and marking as read count as sending (messages:send), and the quota and usage tools require no scope, only a valid key.

Tools

Every tool that acts on messages, contacts or groups accepts number_alias and assumes "main" (the main number) when you leave it out. The number’s id (num_…) works in place of the alias. Plan limits are the same as in the API: reacting (react_to_message) and the typing indicator (send_typing_indicator) require the Lone Wolf plan or higher; sending to groups (including the typing indicator in a group), read receipts (get_message_receipts) and mark_as_read require the Business plan or higher. Receiving messages by webhook requires the Lone Wolf plan or higher; on the Free plan, the agent follows incoming messages with list_events. Without the required plan, the tool answers payment_required: feature_not_available.

When replying, reacting, editing, deleting or starting to type, the number opens the chat the way a person would: it shows as online, marks that chat’s unread messages as read and only then acts. You do not need to call mark_as_read first. Opening the chat this way is automatic and does not use any quota of its own (the reply, the reaction and the send count as before). See Typing indicator and Mark as read.

Sending

ToolWhat it doesScope
send_textSends text, with @number mentions in a groupmessages:send
send_mediaSends an image, video, audio, document or sticker from a URL (kind picks the type)messages:send
send_locationSends a locationmessages:send
send_contact_cardSends a contact cardmessages:send
send_calendar_eventSends a calendar event invitationmessages:send
send_cta_buttonSends a message with a link buttonmessages:send

Reply and act

ToolWhat it doesScope
reply_to_messageReplies quoting an earlier message, to bring contextmessages:send
edit_text_messageReplaces the text of a sent text message, within 15 minutes of sendingmessages:send
edit_media_captionReplaces the caption of a sent image, video or document, within 15 minutes of sendingmessages:send
edit_calendar_eventReplaces a sent calendar event with the corrected onemessages:send
cancel_calendar_eventCancels a sent calendar eventmessages:send
delete_messageDeletes a sent messagemessages:send
react_to_messageReacts to a message (replaces the previous reaction)messages:send
send_typing_indicatorShows “typing…” in the chatmessages:send
mark_as_readMarks the unread messages of a chat or group as read, without replyingmessages:send

Each edit tool takes only the fields of its own type, so the agent cannot send the wrong body. Only messages sent through Turbo Notify can be changed. A location, contact card, link button, sticker or audio message cannot be edited: delete_message is the only change for them. See Edit and delete messages.

Reading messages

ToolWhat it doesScope
list_messagesLists the number’s messages, filtered by group, contact and directionmessages:read
get_messageGets a messagemessages:read
get_message_statusGets the delivery statusmessages:read
get_message_receiptsGets the delivery and read receiptsmessages:read
get_message_reactionsGets a message’s reactionsmessages:read
get_message_responsesGets the guests’ answers to a calendar eventmessages:read
get_message_quotaGets the number’s message quotanone

Contacts

ToolWhat it doesScope
list_contactsLists the number’s contactscontacts:read
get_contactGets a contactcontacts:read
get_contact_profile_pictureGets the profile picturecontacts:read
refresh_contactUpdates the contact’s name by looking it up on WhatsAppcontacts:write
get_contact_quotaGets the number’s contact quotanone

Groups

ToolWhat it doesScope
list_groupsLists the number’s groupsgroups:read
get_groupGets a group: name, description, picture, settings and number of participantsgroups:read
get_group_pictureGets the group picturegroups:read

Numbers

ToolWhat it doesScope
list_numbersLists the main number and the extra numbers, with each one’s id (num_…, the fixed identifier Turbo Notify generates, which does not change if the alias is renamed). Accepts the status filter, like the listnumbers:read
get_numberGets a number’s status and its idnumbers:read
connect_numberReconnects a disconnected number, without a QR codenumbers:write
disconnect_numberDisconnects a number without unlinking it from the phone. It does not stop billing: an extra number is billed every cycle until it is removednumbers:write

Usage and events

ToolWhat it doesScope
get_usageGets the account’s aggregated usage in the cyclenone
list_eventsLists events (the same as the webhook) by type, number, period or request_id. This is how the agent learns about incoming messages without a webhookevents:read

Write tools return a request_id when the platform generates one. Keep it to find the outcome of that operation later with list_events.

Connecting and disconnecting do not charge

connect_number and disconnect_number never generate a charge. On an extra number with a scheduled deletion, both work like on any other number and leave the deletion date unchanged. No tool cancels a scheduled deletion.

connect_number does not recover a number with status revoked (unlinked from the phone): that one has to be linked again by a person.

Repeating connect_number or disconnect_number changes nothing. A number that is already in the requested state answers conflict: already_connected, conflict: already_connecting or conflict: already_disconnected, and that means the number is already as you asked. The call did not fail.

What is not available, and why

No tool adds, renames, removes or links a number. The rule is: does it change what your account pays, or does it need someone holding the phone?

OperationWhy it is not a tool
Add an extra numberIt charges the card before provisioning. If the agent repeats the call, the charge repeats.
Remove a numberIt schedules a real deletion and may result in a refund.
Rename a numberIt changes the alias that addresses a number already in use.
Link a number (pairing)It needs a person holding the phone, scanning the QR code or typing the pairing code.
Cancel a scheduled deletionIt may charge the rest of the paid period and keeps a number someone decided to remove. connect_number leaves the deletion in place.

For these operations, use the dashboard (opens in a new tab) or the REST API. An extra number is linked in the dashboard or through the API, in both cases with a QR code or a pairing code.

While a number is pairing, get_number reports the status only. The code or QR code for the link never passes through the agent: it belongs to the person holding the phone.

Sending without duplicates

The send tools and reply_to_message accept idempotency_key: any printable ASCII text of 1 to 255 characters, chosen by you. If the call fails with a timeout and the agent retries with the same key, on the same number, the message is not sent twice. Without it, retrying after a timeout can deliver the message twice, and the recipient sees both.

Use a new key for each new message, derived from something stable on your side (for example the id of the event the agent is answering). A key in the wrong format is rejected with a message that names the field.

Limits

LimitValueCounted per
Requests to the MCP server600 per minuteAPI key or app sign-in
Streaming connections open at the same time8API key or app sign-in
Requests to the MCP server, shared limit3000 per minuteSource address
Streaming connections open at the same time, shared limit64Source address

Requests with no key or sign-in are counted only by source address. All others go through both limits at once, and a request is refused when either one runs out. The per-address limit is higher to accommodate several keys behind the same network egress (for example, a platform that hosts agents for several customers), but together they cannot exceed 3000 requests per minute or 64 streaming connections. The API’s own limits (sending pace, plan quotas) still apply to each tool: see Limits and Quota and usage.

Errors

Inside the tool result

When a tool cannot do what was asked, the agent gets the reason in the result itself, with the API error code intact. That is what the agent uses to decide whether to fix, wait or give up:

SituationThe agent getsWhat to do
Argument rejected before reaching the APIA message that names the fieldFix the indicated argument.
Invalid argumentinvalid_request: <code>Fix the arguments and try again.
Number disconnectedconflict: number_not_connected, with guidance to reconnectReconnect the number with connect_number (or in the dashboard, if the phone needs to read a code again) and try again.
Number already in the requested stateconflict: already_connected, conflict: already_connecting or conflict: already_disconnectedNothing to do: on a repeated connect_number or disconnect_number, the number is already in the requested state.
A key-shaped value that is invalid, revoked or expiredunauthorized: not_authenticated, with guidance to replace the keyCreate a new key in the dashboard.
Key or sign-in without the tool’s scopeunauthorized: insufficient_scopeLook up the scope in the tools table. With a key, use one that has it. If you signed in with your account, disconnect the app in Connected apps and connect again, ticking that permission.
Contact quota, plan or paymentpayment_required: <code> and the full details objectSee Quota and usage.
Not foundnot_found: <code>Check the identifier or the number_alias.
API rate limit, or the cycle’s message quota used uprate_limited: <code> (for example rate_limited: messages_quota_exceeded), with the wait time when there is oneWait for the indicated time. For the message quota, the wait runs until the cycle ends.
Temporary failureservice_unavailableTry again shortly.

The details object is passed in full, not summarized: it is what tells a quota that resets at the end of the cycle apart from a feature your plan does not include.

On the connection

These are answered before any tool runs, so your MCP client sees them, not the agent:

ResponseWhat happenedWhat to do
401 with a sign-in invitationThe call or connection arrived with no sign-in and no key, or with a value that is neither a sign-in nor a key.In a client with sign-in, sign in to your account when it asks. In the others, send the Authorization: Bearer YOUR_KEY header on every request, also on the streaming connection, not only on the calls.
401 invalid_tokenThe app’s sign-in expired or was disconnected.The client signs in or refreshes again on its own. If the app was disconnected, approve access again.
503Sign-in is temporarily unavailable.Try again shortly. API keys keep working in the meantime.
429 too_many_streamsThis key or sign-in already holds 8 open streaming connections, or the source address already holds 64.Close the ones you do not use, or reduce the processes connected with the same key or from the same address.
429 rate_limit_exceededMore than 600 requests in one minute with this key or sign-in, or more than 3000 in one minute from the same address.Honor the response’s Retry-After.

Full example

The turbo-notify/examples (opens in a new tab) repository has a working WhatsApp support agent built on this server: it receives the message through the webhook, decides with a model that writes the answer, and the program itself sends it as a normal message with send_text. It is small, commented code, meant to be read before it is run.

To build your own agent step by step, see the Build an AI agent guide.

Next steps

  • Agent skills: ready-made instructions for your agent to use these tools
  • API key: create the agent’s key with the right scopes
  • Webhook: receive messages for your agent to answer
  • Event polling: an alternative to the webhook, when you do not have a public endpoint
  • Mark as read: what mark_as_read does on WhatsApp