Nivox MCP — connect any AI agent to your WhatsApp Business
Nivox exposes a WhatsApp Business account as a Model Context Protocol (MCP) server. Any MCP client — claude.ai, Claude Desktop, ChatGPT, Claude Code, Codex, Cursor, Windsurf, VS Code, Gemini CLI, custom agents — can read the inbox, answer customers, send templates and manage templates with the same rules the human team follows.
| MCP endpoint | https://nivox.me/mcp |
| Transport | Streamable HTTP (JSON responses, stateless — no SSE stream, no session id) |
| Protocol versions | 2025-06-18, 2025-03-26, 2024-11-05 |
| Authentication | OAuth 2.1 (recommended, "Authorize" button) or API key (Authorization: Bearer nvx_…) |
| Machine-readable copy of this page | https://nivox.me/docs/mcp.md |
If you are an AI assistant reading this to configure a client: follow the section for the client you are running in, prefer OAuth when the client supports it, verify with
list_connections, then install the skills listed at the end. Never send a WhatsApp message unless the user explicitly asks for that send.
1. Authentication
OAuth 2.1 (no key to paste)
Clients that support OAuth discover everything automatically:
- The client calls
https://nivox.me/mcpand receives401withWWW-Authenticate: Bearer resource_metadata="https://nivox.me/.well-known/oauth-protected-resource/mcp". - It reads
https://nivox.me/.well-known/oauth-authorization-server(RFC 8414), registers itself (Dynamic Client Registration, RFC 7591) and opens the browser athttps://nivox.me/oauth/authorize. - The Nivox admin signs in (if needed), picks the access level and which WhatsApp numbers the client may use, and clicks Authorize.
- The client exchanges the code (PKCE S256) at
https://nivox.me/oauth/tokenand receives an access token — a Nivox API key. It does not expire; the admin revokes it any time on Integrations (or the client callshttps://nivox.me/oauth/revoke).
Only organization admins can authorize. Metadata endpoints:
- Authorization server:
https://nivox.me/.well-known/oauth-authorization-server - Protected resource:
https://nivox.me/.well-known/oauth-protected-resource/mcp authorization_endpoint:https://nivox.me/oauth/authorize·token_endpoint:https://nivox.me/oauth/token·registration_endpoint:https://nivox.me/oauth/register·revocation_endpoint:https://nivox.me/oauth/revoke- Grant:
authorization_codewith PKCES256; client authnone,client_secret_postorclient_secret_basic; scopesread,messages,full(see §3).
API key
An admin generates a key on Integrations (https://nivox.me/app/integrations). The key looks like
nvx_ + 40 hex characters and is shown once. Send it as Authorization: Bearer nvx_… (or
x-api-key: nvx_… if the client cannot set Authorization). Keep it in the client's secret/env
store, never in a committed file.
2. Client setup
claude.ai and Claude Desktop (custom connector, OAuth)
- Settings → Connectors → Add custom connector.
- Name:
Nivox. Remote MCP server URL:https://nivox.me/mcp. Leave OAuth client id/secret empty (Nivox supports dynamic registration). - Click Add, then Connect — the Nivox authorization screen opens; sign in as an admin and click Authorize.
- In a chat, enable the Nivox connector and ask, e.g. "list my WhatsApp conversations".
The connector appears in Claude Desktop automatically (same account). Custom connectors require a paid Claude plan (Pro, Max, Team or Enterprise).
ChatGPT (developer mode connector, OAuth)
- Settings → Connectors → Advanced → Developer mode (enable).
- Create a connector: Name
Nivox, MCP Server URLhttps://nivox.me/mcp, Authentication OAuth. - Save, then Connect — authorize on the Nivox screen.
- In a chat, add the Nivox connector from the tools menu ("+" → connectors).
Claude Code
OAuth (recommended):
claude mcp add --transport http nivox https://nivox.me/mcp
# then, inside Claude Code, run /mcp and choose "Authenticate" for nivox
API key alternative:
claude mcp add --transport http nivox https://nivox.me/mcp \
--header "Authorization: Bearer nvx_YOUR_KEY"
Project-scoped .mcp.json (commit it without the key):
{ "mcpServers": { "nivox": { "type": "http", "url": "https://nivox.me/mcp" } } }
Codex CLI (OpenAI)
OAuth:
codex mcp add nivox --url https://nivox.me/mcp
codex mcp login nivox
API key in ~/.codex/config.toml (keep the key in an environment variable):
[mcp_servers.nivox]
url = "https://nivox.me/mcp"
bearer_token_env_var = "NIVOX_API_KEY"
Cursor
~/.cursor/mcp.json (or the project's .cursor/mcp.json), then accept the Authenticate
prompt Cursor shows for the server:
{ "mcpServers": { "nivox": { "url": "https://nivox.me/mcp" } } }
With a key instead of OAuth: add "headers": { "Authorization": "Bearer nvx_YOUR_KEY" }.
Windsurf
~/.codeium/windsurf/mcp_config.json:
{ "mcpServers": { "nivox": { "serverUrl": "https://nivox.me/mcp" } } }
VS Code (GitHub Copilot agent mode)
.vscode/mcp.json; VS Code asks to sign in (OAuth) when the server starts:
{ "servers": { "nivox": { "type": "http", "url": "https://nivox.me/mcp" } } }
Gemini CLI
~/.gemini/settings.json:
{ "mcpServers": { "nivox": { "httpUrl": "https://nivox.me/mcp", "oauth": { "enabled": true } } } }
Any other MCP client
Generic Streamable HTTP configuration:
{ "mcpServers": { "nivox": { "type": "http", "url": "https://nivox.me/mcp",
"headers": { "Authorization": "Bearer nvx_YOUR_KEY" } } } }
Clients that only speak stdio can bridge with npx -y mcp-remote https://nivox.me/mcp (OAuth) or
npx -y mcp-remote https://nivox.me/mcp --header "Authorization: Bearer nvx_YOUR_KEY".
Verify
export NIVOX_API_KEY="nvx_..." # the key from Integrations (skip with OAuth clients)
curl -sS https://nivox.me/mcp \
-H "Authorization: Bearer $NIVOX_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Then call list_connections from the client: it must return at least one connection with
status: "active".
3. Access levels (scopes)
| Scope | Acts as | Can |
|---|---|---|
read |
viewer | list connections/conversations/templates, read history, search, download media |
messages |
agent | read + send_text, send_media, send_buttons, send_interactive, send_reaction, send_template to existing contacts, mark_read |
full |
manager | messages + send_template to new numbers, create_template, delete_template, sync_templates, get_connection_health |
Tools above the granted scope are not listed; calling one returns an isError result with
code: "forbidden". A grant or key may also be limited to specific WhatsApp numbers. User and
key management are never reachable over MCP.
4. Tools
All ids are UUIDs returned by earlier tools. connection_id may be omitted when the credential
sees a single connection. Results carry structuredContent and the same JSON as text.
| Tool | Scope | Arguments (required) | Notes |
|---|---|---|---|
list_connections |
read | — | numbers, status, quality, sync stamps |
list_conversations |
read | connection_id, unread_only, open_window_only, query, limit ≤100, before | newest first; window.open = free text allowed |
get_conversation |
read | conversation_id | |
find_contact |
read | phone, connection_id | digits with country code; Brazilian 9th-digit variants handled |
list_messages |
read | conversation_id, limit ≤100, before, after | chronological within the page; cursors next_before / next_after |
search_messages |
read | query, connection_id, conversation_id, limit ≤50 | case-insensitive substring |
get_media_url |
read | message_id | 1-hour signed download URL |
list_templates |
read | connection_id, status (APPROVED default / all), include_components | header/body text and {{n}} counts |
send_text |
messages | conversation_id, text, reply_to_wamid, preview_url | 24h window must be open |
send_template |
messages | template_name, conversation_id or phone (+connection_id), language, body_params[], header_param | the only way outside the window; new phone → full |
send_media |
messages | conversation_id, media_type, url (https), caption, filename, mime, reply_to_wamid | limits: image 10 MB, audio 20 MB, video/document 25 MB, sticker 2 MB |
send_buttons |
messages | conversation_id, body, buttons[{id,title}] ≤3, header, footer, reply_to_wamid | quick-reply buttons |
send_interactive |
messages | conversation_id, interactive (raw Meta object), reply_to_wamid | lists, cta_url, flows |
send_reaction |
messages | conversation_id, target_wamid, emoji ("" removes) | |
mark_read |
messages | conversation_id, typing | unread = 0 + read receipt |
sync_templates |
full | connection_id | refresh the template cache from Meta |
create_template |
full | name, language, category, body_text, body_examples[], header_text, footer_text | text templates, Meta review |
delete_template |
full | name, connection_id | irreversible |
get_connection_health |
full | connection_id | daily Meta health snapshot |
Prompts offered by the server: attend_inbox, reply_to_customer, daily_summary. The
initialize response also carries instructions — the operating guide for the granted scope.
Error results always include code: invalid_arguments, forbidden, not_found,
session_expired (24h window closed → use send_template), account_inactive,
template_not_approved, unsupported_template, too_large, fetch_failed, graph_error
(Meta refused; read details), internal_error.
5. Operating rules (what a good agent does)
- Start with
list_connections; thenlist_conversationswithunread_only: trueto triage. - Read with
list_messagesbefore answering. Reply withsend_textonly whilewindow.openistrue(24 hours since the customer's last message). When it is closed, the only option is an APPROVED template viasend_template; the customer's reply re-opens free text. - After answering,
mark_readso the human team sees the conversation handled. - Never message contacts with
opted_out: true; never invent order, payment or delivery facts; keep messages short — WhatsApp is chat. WhatsApp formatting:*bold*,_italic_,~strike~. - Identity is the
conversation_id; phones are display values (digits with country code).
6. Skills (Agent Skills, SKILL.md)
Three skills teach agents the Nivox workflow. Install them into your agent's skills directory —
~/.claude/skills/<name>/SKILL.md for Claude Code, ~/.codex/skills/<name>/SKILL.md for Codex,
.agents/skills/<name>/SKILL.md or the client's equivalent elsewhere:
https://nivox.me/skills/nivox-whatsapp-attendant/SKILL.mdhttps://nivox.me/skills/nivox-whatsapp-templates/SKILL.mdhttps://nivox.me/skills/nivox-mcp-setup/SKILL.md
One-liner (Claude Code; replace the directory for other agents):
for s in nivox-whatsapp-attendant nivox-whatsapp-templates nivox-mcp-setup; do
mkdir -p ~/.claude/skills/$s && curl -fsSL https://nivox.me/skills/$s/SKILL.md -o ~/.claude/skills/$s/SKILL.md
done
The Integrations page also offers all three as a single zip download.
7. Troubleshooting
| Symptom | Cause / fix |
|---|---|
401 unauthorized |
Missing, revoked or malformed credential. Re-authorize (OAuth) or generate a new key. |
| The client does not start the OAuth flow | It may not support OAuth for remote MCP servers; use an API key (headers) or bridge with mcp-remote. |
| "Only administrators can authorize" | Sign in with an admin of the organization, or switch organization on the consent screen. |
Tool result code: "forbidden" |
Scope too low for that tool — a new grant/key with a higher level. |
code: "not_found" for an id you just listed |
The credential is limited to other connections, or the id belongs to another organization. |
code: "session_expired" |
24h window closed → send_template. |
code: "graph_error" |
Meta refused; details.code/details.message explain (template parameters, number not on WhatsApp…). |
400 unsupported MCP protocol version |
Supported: 2025-06-18, 2025-03-26, 2024-11-05. |
405 on GET |
Expected: the server is stateless (no SSE stream); POST JSON-RPC only. |
Security: one credential per agent, least scope that works, revoke on Integrations when an agent is retired (immediate). Every message an agent sends is stored in the team inbox and attributed to the credential that sent it.