{"agent_handoff":{"api_handoff":{"behavior":"If the number is already registered/live: returns status=handoff_confirmation_required with request_id + poll_token only (NO api_key, NO mcp token, NO tenant_key). Owner must /connect approve \u003ccode\u003e in self-chat. Agent then polls GET /api/session/connect/handoff once to receive a newly minted named MCP credential.","breaking_change":"session_handoff with immediate secrets is removed for public connect. Integrators must implement poll + owner approve.","response_fields_approved_once":["status","api_key","mcp_connection","nickname","message"],"response_fields_pending":["status","request_id","poll_token","poll_url","mfa","expires_at","message"],"triggers":["POST /api/session/connect/code","POST /api/session/connect/qr","POST /api/session/connect/kit","MCP whatsapp_connect_code / whatsapp_connect_qr / whatsapp_connect_kit"]},"description":"When WhatsApp is already **live** and the caller does not hold phone-bound credentials, additional agents need owner MFA before any credentials are issued. Secrets are never returned from unauthenticated connect calls. Offline registered numbers do **not** use MFA (see session_lifecycle).","fields":{"code_delivered":"Whether the 6-digit code was pushed to self-chat","session_active":"true for this path","suggested_action":"approve_mfa | approve_mfa_or_force_pair_if_code_missing"},"self_chat_mfa":{"approve":"/connect approve \u003c6-digit-code\u003e","bot_replies":["On /connect: MCP endpoint + token for owner","On approve: confirmation only — agent polls once for secrets"],"command":"/connect","cooldown_seconds":30,"deny":"/connect deny \u003c6-digit-code\u003e","how":"User opens self-chat (message yourself). For API requests they receive an approve code; for owner self-service they can send /connect alone.","mfa_proof":"Only the account owner can send live IsFromMe messages in self-chat — this verifies identity."},"status":"handoff_confirmation_required"},"alternative_flows":{"agent_connect_kit":{"description":"Best for AI agents: one call returns a shareable pairing_page_url plus QR, pairing code, and hosted PNG.","mcp":"whatsapp_connect_kit","poll":"GET /api/session/connect/status?uuid=\u003cuuid\u003e","render":"GET /api/session/connect/kit-image?uuid=\u003cuuid\u003e","returns":["pairing_page_url","pairing_code","qr_data","qr_ascii","image.base64","image.url","tenant"],"start":"POST /api/session/connect/kit"},"connect_page":{"description":"Self-service UI: pairing code or QR, auto-poll, API key, MCP config copy.","query_params":{"tenant":"Pre-fills tenant key (tnt_...)"},"url":"https://zap.zaptdev.com/connect"},"mcp_only_lifecycle":{"description":"After MCP is enabled with allow_provision=true, provision phones without REST.","tools":["whatsapp_connect_code","whatsapp_connect_qr","whatsapp_connect_kit","whatsapp_connect_status","whatsapp_reconnect","whatsapp_enable_mcp"]},"qr_pairing":{"poll":"GET /api/session/connect/status?uuid=\u003cuuid\u003e","render":"GET /api/session/connect/qr-image?uuid=\u003cuuid\u003e","start":"POST /api/session/connect/qr"},"reconnect":{"mcp":"whatsapp_reconnect","rest":"POST /api/session/reconnect with wbot_ key"}},"authentication":{"mcp":{"format":"Bearer mcp_... or Bearer oat_... (OAuth access)","header":"Authorization","usage":"All MCP tool calls at POST /mcp with MCP-Protocol-Version: 2025-11-25"},"oauth":{"access_token_prefix":"oat_","discovery":{"authorization_server_metadata":"https://zap.zaptdev.com/.well-known/oauth-authorization-server","protected_resource_metadata":"https://zap.zaptdev.com/.well-known/oauth-protected-resource"},"endpoints":{"authorize":"https://zap.zaptdev.com/oauth/authorize","register":"https://zap.zaptdev.com/oauth/register","revoke":"https://zap.zaptdev.com/oauth/revoke","token":"https://zap.zaptdev.com/oauth/token"},"refresh_token_prefix":"ort_","resource":"https://zap.zaptdev.com/mcp","scopes":["mcp:read","mcp:send","mcp:provision","offline_access"],"spec":"OAuth 2.1 + PKCE S256 (RFC 9728 PRM, RFC 8414 AS metadata, RFC 7591 DCR)"},"platform_admin":{"alt":"Authorization: Bearer \u003cADMIN_MASTER_TOKEN\u003e","format":"ADMIN_MASTER_TOKEN env value","header":"X-Admin-Token","usage":"Admin tenant list/create, inventory, docs when REQUIRE_ADMIN_FOR_DOCS=true"},"session_api":{"alt":"Authorization: Bearer wbot_...","format":"wbot_...","header":"X-API-Key","usage":"Send messages, session status, optional MCP enable with custom permissions"},"tenant_provisioner":{"alt":"Authorization: Bearer tnt_...","format":"tnt_...","header":"X-Tenant-Key","usage":"Start connect flows for a white-label tenant; add more numbers under the same org"}},"docs":{"agent_playbook":"https://zap.zaptdev.com/docs/agent-playbook","agents_guide":"https://zap.zaptdev.com/guides/agents","api_guide":"https://zap.zaptdev.com/guides/api","connect_page":"https://zap.zaptdev.com/connect","connect_prefilled":"https://zap.zaptdev.com/connect?tenant=tnt_\u003ctenant_key\u003e","health":"https://zap.zaptdev.com/health","landing":"https://zap.zaptdev.com/","llms_full_txt":"https://zap.zaptdev.com/llms-full.txt","llms_txt":"https://zap.zaptdev.com/llms.txt","mcp_endpoint":"https://zap.zaptdev.com/mcp","mcp_guide":"https://zap.zaptdev.com/guides/mcp","openapi_json":"https://zap.zaptdev.com/docs/openapi.json","openapi_yaml":"https://zap.zaptdev.com/docs/openapi.yaml","owner_settings":"https://zap.zaptdev.com/portal","portal_guide":"https://zap.zaptdev.com/guides/portal","skills_guide":"https://zap.zaptdev.com/guides/skills","swagger_ui":"https://zap.zaptdev.com/docs"},"error_handling":{"401":"Missing or invalid auth — check tenant key, wbot_ key, or mcp_ token. Try whatsapp_rotate_mcp_token.","403":"Connect UUID belongs to another tenant.","408":"Pairing code not ready — poll connect status.","409":"WhatsApp offline — whatsapp_reconnect or wait for user pairing.","rate_limit":"MCP default 120/min per token — whatsapp_get_audit_log"},"human_onboarding":{"agent_first_action":"QR no phone: POST /api/session/connect/qr. Phone dual: POST /api/session/connect/kit with phone_number. SaaS: X-Tenant-Key or /connect?tenant=tnt_.... Poll status until success.","human_steps":["Option A: open homepage, scan auto QR (no phone typed)","Option B: open /connect, enter number, generate code \u0026 QR","Confirm on phone via Linked Devices","Copy the AI connection (MCP config) and/or API key (wbot_...) for full REST access","Optional: owner portal via self-chat /pwa to name agents and set permissions"],"prerequisites":["WhatsApp app open on the user's phone","For /connect code flow: phone number with country code","Optional for multi-number/SaaS: workspace key (tnt_...) from POST /api/tenants or POST /api/admin/tenants"],"recommended_url":"https://zap.zaptdev.com/connect","share_with_clients":"Fastest: send homepage URL for QR scan. Phone+code: /connect. SaaS: /connect?tenant=tnt_....","ui_modes":{"connect_phone":"https://zap.zaptdev.com/connect — phone number generates pairing code + QR together","landing_qr":"https://zap.zaptdev.com/#pair-now — QR without phone number, auto-starts on homepage","prefilled":"https://zap.zaptdev.com/connect?tenant=tnt_... — pre-fills workspace key for client pairing","saas":"https://zap.zaptdev.com/connect?mode=saas — create/paste workspace key (tnt_...) for multi-number orgs"}},"important_notes":["SECURITY: Connect endpoints never return credentials for an already-registered number without owner self-chat MFA.","Already paired? Agent gets handoff_confirmation_required → owner sends /connect approve \u003ccode\u003e → agent polls /api/session/connect/handoff with poll_token (one-time secrets).","Owner self-service: send /connect in WhatsApp self-chat to view MCP details (IsFromMe MFA).","tenant_key is returned only when a tenant is freshly auto-created — never re-exported for existing tenants.","api_key and mcp_connection appear on connect status only after pairing status=success (WhatsApp ownership proof).","Approved handoffs mint a NEW named MCP credential (send_mode=approval) — they do not re-export the primary token.","Call POST /api/mcp/enable (or whatsapp_enable_mcp) only to set allow_provision=true or rotate permissions.","api_key (wbot_...) and mcp_ token are sensitive — store securely.","Use whatsapp_resolve_recipient / whatsapp_resolve_entity before send — phone, JID, contact, chat, or group name (fuzzy). On AMBIGUOUS_RECIPIENT pick jid from candidates[].","list_chats/list_groups/list_contacts all accept query= fuzzy search. list_messages/search_messages accept chat names and natural time (yesterday, 12h, this_week).","whatsapp_inbox = unread + needs_reply. whatsapp_find_message when message_id unknown. reply accepts content_snippet.","send_message supports client_message_id for idempotent retries. Errors are JSON with code/hint/candidates.","When the owner replies in the WhatsApp app (is_from_me), inbound message_in notifications for that chat are auto-marked read.","All MCP tool calls are audited in whatsmeow_mcp_audit.","Full per-tool docs: mcp.tools[] in this playbook — descriptions match live MCP tool schemas."],"mcp":{"auto_enable_defaults":{"allow_provision":false,"allow_read":true,"allow_send":true},"auto_enable_on_connect":true,"claude_integrations":{"desktop_custom_connector":{"auth":"Interactive OAuth PKCE (DCR). Owner consents in browser; Claude stores tokens.","how":"Claude Settings → Connectors → Add custom connector → URL https://zap.zaptdev.com/mcp"},"messages_api_mcp_connector":{"authorization":"authorization_token = mcp_… or oat_… (obtain ahead of time; refresh as needed)","beta":"mcp-client-2025-11-20","connection_field":"mcp_connection.anthropic_mcp_connector from GET /api/mcp/connection or whatsapp_get_mcp_connection","docs":"https://docs.anthropic.com/en/docs/agents-and-tools/mcp-connector","how":"Pass mcp_servers + tools mcp_toolset on beta.messages.create; no local MCP client needed.","url":"https://zap.zaptdev.com/mcp"}},"endpoint":"https://zap.zaptdev.com/mcp","operating_guide":{"audit_and_control":["Every tool call appears in owner's /portal audit and notifications.","Use get_audit_log frequently so the owner sees transparency.","Owners can rotate/revoke tokens instantly in the portal."],"first_calls_after_connect":["whatsapp_health_check","whatsapp_get_mcp_connection","whatsapp_wait_for_sync (if just paired or long offline)"],"patterns":["Always use Approval Gate before writes (show exact call, wait for confirmation)","Always call get_audit_log after mutating actions and report to user","Use since/until + search_messages for time-bounded recall","Use list_groups + get_group_* for group work","Use schedule_message for future delivery / recurring digests"],"reactive_message_flow":["whatsapp_watch_notifications (preferred long-poll; or list_notifications)","whatsapp_get_chat","whatsapp_resolve_recipient (if only phone/name)","whatsapp_reply_to_message (preferred) or send_message","whatsapp_mark_chat_read (after handling)","whatsapp_get_audit_log (transparency)"],"recipient_tips":["Call whatsapp_resolve_recipient (or resolve_entity) before send — accepts phone, JID, contact name, chat name, or group name (fuzzy/phonetic).","Self-chat: pass your own phone digits — resolver maps to @s.whatsapp.net.","Groups: list_groups query= or resolve with kind=group; send_message accepts group names.","On AMBIGUOUS_RECIPIENT: pick jid from candidates[] — do not invent numbers.","Time filters accept yesterday/today/this_week/12h/7d and RFC3339 (America/Sao_Paulo).","Triaging: whatsapp_inbox for unread + needs_reply; find_message when message_id unknown."],"sync_tips":["Call whatsapp_wait_for_sync after pairing before heavy operations.","sync_protected=true means the session will not be deactivated mid-sync.","Large histories may show 'syncing' in phone even after progress=100."],"troubleshooting_flow":["whatsapp_health_check","whatsapp_get_sync_status","whatsapp_reconnect","whatsapp_rotate_mcp_token (on 401)","whatsapp_get_audit_log (to see recent failures)"]},"protocol_version":"2025-11-25","resources":[{"description":"Live connection status and nested sync object. Same as whatsapp_get_session_status.","mime_type":"application/json","name":"WhatsApp Session","requires_allow_read":true,"uri":"whatsapp://session"},{"description":"Persisted history/offline sync progress (phase, progress_percent, sync_protected). Same as whatsapp_get_sync_status.","mime_type":"application/json","name":"WhatsApp Sync Status","requires_allow_read":true,"uri":"whatsapp://sync"},{"description":"Unread rows from whatsmeow_notifications (message_in, sync_progress, sync_completed, etc.).","mime_type":"application/json","name":"Unread Agent Notifications","requires_allow_read":true,"uri":"whatsapp://notifications/unread"},{"description":"Last 50 stored messages for a chat JID. Substitute {chat_jid} with e.g. 5511999999999@s.whatsapp.net.","mime_type":"application/json","name":"WhatsApp Messages","requires_allow_read":true,"uri":"whatsapp://messages/{chat_jid}"}],"token_sources":["GET /api/session/connect/status → mcp_connection.authentication.token (on status=success)","POST /api/mcp/enable or whatsapp_enable_mcp (custom permissions / allow_provision)","GET /api/mcp/connection or whatsapp_get_mcp_connection","whatsapp_rotate_mcp_token when MCP returns 401","OAuth: POST /oauth/token → access_token oat_… (or MCP Inspector Quick OAuth Flow)"],"tool_count":66,"tools":[{"category":"messaging","mcp_description":"Send a text or media message through the connected WhatsApp account. When to use: Outbound to user or group. recipient accepts phone, JID, or fuzzy name. Use client_message_id for retries. Parameters: recipient (required) — Phone, JID, contact/chat/group name e.g. Iasmin; content — Text body or media caption; media_url — Public URL of image/video/audio/document to send; media_type — Hint when using media_url: image, video, audio, document, sticker; client_message_id — Idempotency key for safe retries; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_message","parameters":[{"description":"Phone, JID, contact/chat/group name","example":"Iasmin","name":"recipient","required":true,"type":"string"},{"description":"Text body or media caption","name":"content","type":"string"},{"description":"Public URL of image/video/audio/document to send","name":"media_url","type":"string"},{"description":"Hint when using media_url: image, video, audio, document, sticker","name":"media_type","type":"string"},{"description":"Idempotency key for safe retries","name":"client_message_id","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Send a text or media message through the connected WhatsApp account.","when_to_use":"Outbound to user or group. recipient accepts phone, JID, or fuzzy name. Use client_message_id for retries."},{"category":"messaging","mcp_description":"Send a quoted reply. Prefer message_id; or content_snippet (+ optional chat_jid). When to use: Respond in-thread after list/search/find_message or a message_in notification. Parameters: message_id — Stored message_id to quote (preferred); content_snippet — Find message by content if id unknown; chat_jid — Scope snippet search (name or JID); content (required) — Reply text; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_reply_to_message","parameters":[{"description":"Stored message_id to quote (preferred)","name":"message_id","type":"string"},{"description":"Find message by content if id unknown","name":"content_snippet","type":"string"},{"description":"Scope snippet search (name or JID)","name":"chat_jid","type":"string"},{"description":"Reply text","name":"content","required":true,"type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Send a quoted reply. Prefer message_id; or content_snippet (+ optional chat_jid).","when_to_use":"Respond in-thread after list/search/find_message or a message_in notification."},{"category":"messaging","mcp_description":"Find messages by snippet, chat name, from_name, and time window. When to use: When message_id is unknown before reply. Parameters: query — Content tokens or contact/chat name (fuzzy); chat_jid — Chat JID or fuzzy name; from_name — Fuzzy sender filter; since — yesterday|12h|RFC3339|…; limit — Max results; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_find_message","parameters":[{"description":"Content tokens or contact/chat name (fuzzy)","name":"query","type":"string"},{"description":"Chat JID or fuzzy name","name":"chat_jid","type":"string"},{"description":"Fuzzy sender filter","name":"from_name","type":"string"},{"description":"yesterday|12h|RFC3339|…","name":"since","type":"string"},{"description":"Max results","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Find messages by snippet, chat name, from_name, and time window.","when_to_use":"When message_id is unknown before reply."},{"category":"messaging","mcp_description":"Unread notifications + chats waiting for your reply. When to use: First call for 'what needs attention?' / voice triage. Parameters: limit — Max items (default 20); since_hours — needs_reply window hours (default 48); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_inbox","parameters":[{"description":"Max items (default 20)","name":"limit","type":"number"},{"description":"needs_reply window hours (default 48)","name":"since_hours","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Unread notifications + chats waiting for your reply.","when_to_use":"First call for 'what needs attention?' / voice triage."},{"category":"session","mcp_description":"Unified resolve of contact/chat/group with ranked candidates. When to use: Before any send when the user gave a spoken name. Parameters: query (required) — Name, phone, or JID; kind — any|contact|chat|group; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_resolve_entity","parameters":[{"description":"Name, phone, or JID","name":"query","required":true,"type":"string"},{"description":"any|contact|chat|group","name":"kind","type":"string"}],"requires_allow_read":true,"requires_session":true,"summary":"Unified resolve of contact/chat/group with ranked candidates.","when_to_use":"Before any send when the user gave a spoken name."},{"category":"messaging","mcp_description":"Schedule a message for future delivery. When to use: When the user wants to send a message at a specific future time. Parameters: recipient (required) — Phone or JID; content (required) — ; schedule_at (required) — RFC3339 future time; media_url — ; media_type — ; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_schedule_message","parameters":[{"description":"Phone or JID","name":"recipient","required":true,"type":"string"},{"description":"","name":"content","required":true,"type":"string"},{"description":"RFC3339 future time","name":"schedule_at","required":true,"type":"string"},{"description":"","name":"media_url","type":"string"},{"description":"","name":"media_type","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Schedule a message for future delivery.","when_to_use":"When the user wants to send a message at a specific future time."},{"category":"messaging","mcp_description":"React to a stored message with an emoji. When to use: Lightweight ack without a full reply. Parameters: message_id (required) — Target message_id; emoji — Reaction emoji e.g. 👍; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_reaction","parameters":[{"description":"Target message_id","name":"message_id","required":true,"type":"string"},{"description":"Reaction emoji","example":"👍","name":"emoji","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"React to a stored message with an emoji.","when_to_use":"Lightweight ack without a full reply."},{"category":"messaging","mcp_description":"Show or clear typing indicator in a chat. When to use: Before composing a long reply for natural UX. Parameters: chat_jid (required) — Chat JID or phone; state — composing or paused e.g. composing; media — Optional: audio for voice-note typing; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_typing","parameters":[{"description":"Chat JID or phone","name":"chat_jid","required":true,"type":"string"},{"description":"composing or paused","example":"composing","name":"state","type":"string"},{"description":"Optional: audio for voice-note typing","name":"media","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Show or clear typing indicator in a chat.","when_to_use":"Before composing a long reply for natural UX."},{"category":"messaging","mcp_description":"List stored messages for the account, optionally filtered by chat, author, or time window. When to use: Paginated or incremental history. Use since/until for a window, direction for author, after_message_id to fetch only new messages since a cursor. Parameters: chat_jid — Optional chat filter; limit — Max rows (default 25, max 100); offset — Pagination offset; direction — Author filter: 'out' (from me) or 'in' (from others); since — Lower time bound (RFC3339 or YYYY-MM-DD); until — Upper time bound (RFC3339 or YYYY-MM-DD); after_message_id — Incremental cursor — only messages newer than this id (oldest-first); oldest_first — 'true' for oldest-first ordering; last_hours — Relative window: only messages from the last N hours (ignored if since is set); interacted_only — Only chats where you sent at least one message; exclude_status — Exclude status@broadcast; exclude_announce_groups — Exclude large announce (broadcast) groups; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_messages","parameters":[{"description":"Optional chat filter","name":"chat_jid","type":"string"},{"description":"Max rows (default 25, max 100)","name":"limit","type":"number"},{"description":"Pagination offset","name":"offset","type":"number"},{"description":"Author filter: 'out' (from me) or 'in' (from others)","name":"direction","type":"string"},{"description":"Lower time bound (RFC3339 or YYYY-MM-DD)","name":"since","type":"string"},{"description":"Upper time bound (RFC3339 or YYYY-MM-DD)","name":"until","type":"string"},{"description":"Incremental cursor — only messages newer than this id (oldest-first)","name":"after_message_id","type":"string"},{"description":"'true' for oldest-first ordering","name":"oldest_first","type":"string"},{"description":"Relative window: only messages from the last N hours (ignored if since is set)","name":"last_hours","type":"number"},{"description":"Only chats where you sent at least one message","name":"interacted_only","type":"boolean"},{"description":"Exclude status@broadcast","name":"exclude_status","type":"boolean"},{"description":"Exclude large announce (broadcast) groups","name":"exclude_announce_groups","type":"boolean"}],"requires_allow_read":true,"requires_session":true,"summary":"List stored messages for the account, optionally filtered by chat, author, or time window.","when_to_use":"Paginated or incremental history. Use since/until for a window, direction for author, after_message_id to fetch only new messages since a cursor."},{"category":"messaging","mcp_description":"Per-chat aggregate statistics (counts, my-share, first/last, media) without fetching messages. Optionally windowed with since/until. When to use: Prioritize chats or size a window before pulling content — one cheap call instead of paging history. Parameters: chat_jid (required) — Chat JID or phone; since — Window start (RFC3339, YYYY-MM-DD, or relative '12h'/'7d'); default all-time; until — Window end; default now; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_chat_stats","parameters":[{"description":"Chat JID or phone","name":"chat_jid","required":true,"type":"string"},{"description":"Window start (RFC3339, YYYY-MM-DD, or relative '12h'/'7d'); default all-time","name":"since","type":"string"},{"description":"Window end; default now","name":"until","type":"string"}],"requires_allow_read":true,"requires_session":true,"summary":"Per-chat aggregate statistics (counts, my-share, first/last, media) without fetching messages. Optionally windowed with since/until.","when_to_use":"Prioritize chats or size a window before pulling content — one cheap call instead of paging history."},{"category":"messaging","mcp_description":"Search messages by tokens (content + document filename/title/caption), chat name, from_name, media type, and natural time. When to use: Recall by keyword/date/sender. For PDFs/orçamentos prefer message_type=document + has_media=true + chat_jid. Time: yesterday, 12h, this_week. Parameters: query — Tokens matched against content, document fileName/title/caption, and media_url; chat_jid — Chat JID or fuzzy name; from_name — Fuzzy sender/chat name; message_type — text|image|document|…; has_media — Only media messages; since — yesterday|12h|RFC3339|…; until — Upper bound; sync_type — e.g. live_sync, initial_sync; limit — Max results (default 25); offset — Pagination offset; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_search_messages","parameters":[{"description":"Tokens matched against content, document fileName/title/caption, and media_url","name":"query","type":"string"},{"description":"Chat JID or fuzzy name","name":"chat_jid","type":"string"},{"description":"Fuzzy sender/chat name","name":"from_name","type":"string"},{"description":"text|image|document|…","name":"message_type","type":"string"},{"description":"Only media messages","name":"has_media","type":"boolean"},{"description":"yesterday|12h|RFC3339|…","name":"since","type":"string"},{"description":"Upper bound","name":"until","type":"string"},{"description":"e.g. live_sync, initial_sync","name":"sync_type","type":"string"},{"description":"Max results (default 25)","name":"limit","type":"number"},{"description":"Pagination offset","name":"offset","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Search messages by tokens (content + document filename/title/caption), chat name, from_name, media type, and natural time.","when_to_use":"Recall by keyword/date/sender. For PDFs/orçamentos prefer message_type=document + has_media=true + chat_jid. Time: yesterday, 12h, this_week."},{"category":"messaging","mcp_description":"List attachments/media in one chat (documents by default), with filename/title and neighbor message context. When to use: Find PDFs/orçamentos in a group. Ranks by filename AND messages before/after the attachment (topic often lives there). Prefer over list_messages. Then download_media or share media_url. Parameters: chat_jid (required) — Chat/group JID or fuzzy name; chat — Alias for chat_jid; message_type — document (default)|image|video|audio|any; query — Keywords matched against filename/title AND neighbor chat text; from_name — Fuzzy sender filter; context_before — Neighbor messages before each media (default 3); context_after — Neighbor messages after each media (default 3); limit — Max items (default 30); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_chat_media","parameters":[{"description":"Chat/group JID or fuzzy name","name":"chat_jid","required":true,"type":"string"},{"description":"Alias for chat_jid","name":"chat","type":"string"},{"description":"document (default)|image|video|audio|any","name":"message_type","type":"string"},{"description":"Keywords matched against filename/title AND neighbor chat text","name":"query","type":"string"},{"description":"Fuzzy sender filter","name":"from_name","type":"string"},{"description":"Neighbor messages before each media (default 3)","name":"context_before","type":"number"},{"description":"Neighbor messages after each media (default 3)","name":"context_after","type":"number"},{"description":"Max items (default 30)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"List attachments/media in one chat (documents by default), with filename/title and neighbor message context.","when_to_use":"Find PDFs/orçamentos in a group. Ranks by filename AND messages before/after the attachment (topic often lives there). Prefer over list_messages. Then download_media or share media_url."},{"category":"messaging","mcp_description":"Get one chat summary with contact names and last message. When to use: Triage a conversation before replying. Parameters: chat_jid (required) — Chat JID, phone, or fuzzy name; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_chat","parameters":[{"description":"Chat JID, phone, or fuzzy name","name":"chat_jid","required":true,"type":"string"}],"requires_allow_read":true,"requires_session":true,"summary":"Get one chat summary with contact names and last message.","when_to_use":"Triage a conversation before replying."},{"category":"messaging","mcp_description":"Mark inbound messages read on WhatsApp and zero unread_count in bot_chats. When to use: After handling a conversation. Parameters: chat_jid (required) — Chat JID or phone; limit — Max inbound messages to mark (default 30); Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_mark_chat_read","parameters":[{"description":"Chat JID or phone","name":"chat_jid","required":true,"type":"string"},{"description":"Max inbound messages to mark (default 30)","name":"limit","type":"number"}],"requires_allow_send":true,"requires_session":true,"summary":"Mark inbound messages read on WhatsApp and zero unread_count in bot_chats.","when_to_use":"After handling a conversation."},{"category":"messaging","mcp_description":"Download a message's media and return it as attachable content (image/blob), extracted document text, plus access URLs. When to use: When a message has media (image, PDF, document, audio) and you need to read or forward its actual content, not just a link. Content is returned inline (extracted_text for PDFs + the file attached as MCP content); read it from the response. Do not fetch media_url over HTTP or web-search the document — the returned content is authoritative. Parameters: message_id (required) — Stored message_id; include_base64 — Also inline raw base64 in JSON (default false; file is already attached as MCP content); extract_text — Return extracted plain text for documents like PDFs (default true); max_bytes — Max bytes to embed as attachment/base64 (default 20MB); larger files served via download_url; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_download_media","parameters":[{"description":"Stored message_id","name":"message_id","required":true,"type":"string"},{"description":"Also inline raw base64 in JSON (default false; file is already attached as MCP content)","name":"include_base64","type":"boolean"},{"description":"Return extracted plain text for documents like PDFs (default true)","name":"extract_text","type":"boolean"},{"description":"Max bytes to embed as attachment/base64 (default 20MB); larger files served via download_url","name":"max_bytes","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Download a message's media and return it as attachable content (image/blob), extracted document text, plus access URLs.","when_to_use":"When a message has media (image, PDF, document, audio) and you need to read or forward its actual content, not just a link. Content is returned inline (extracted_text for PDFs + the file attached as MCP content); read it from the response. Do not fetch media_url over HTTP or web-search the document — the returned content is authoritative."},{"category":"messaging","mcp_description":"List synced chats (or fuzzy-search by name). Each row has a resolved name. When to use: Discover conversations. Pass query for voice names. Prefer list_interacted_chats for windowed digests. Parameters: query — Fuzzy chat/contact/group name search; limit — Max chats (default 50); chat_type — Filter: private | group | status; interacted_since — Only chats with activity at/after this time (RFC3339 or relative '12h'); has_outbound — Only chats where you sent at least one message; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_chats","parameters":[{"description":"Fuzzy chat/contact/group name search","name":"query","type":"string"},{"description":"Max chats (default 50)","name":"limit","type":"number"},{"description":"Filter: private | group | status","name":"chat_type","type":"string"},{"description":"Only chats with activity at/after this time (RFC3339 or relative '12h')","name":"interacted_since","type":"string"},{"description":"Only chats where you sent at least one message","name":"has_outbound","type":"boolean"}],"requires_allow_read":true,"requires_session":true,"summary":"List synced chats (or fuzzy-search by name). Each row has a resolved name.","when_to_use":"Discover conversations. Pass query for voice names. Prefer list_interacted_chats for windowed digests."},{"category":"messaging","mcp_description":"Windowed activity digest: the conversations active in a time window, each with resolved name, in/out/media counts, last-message preview, and a needs_reply flag. One call answers 'who did I talk to' / 'what needs my attention'. When to use: The primary entrypoint for 'what happened' questions. Prefer over paging whatsapp_list_messages when you want an overview across chats. Defaults to the last 24h and hides status + announce-group noise. Parameters: since — Window start (RFC3339 or relative '12h'/'2d'); default 24h ago; until — Window end; default now; min_messages — Only chats with at least N messages (default 1); only_outbound — Only chats where you sent at least one message; exclude_status — Exclude status@broadcast (default true); exclude_announce_groups — Exclude large announce groups (default true); limit — Max chats (default 50, max 200); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_interacted_chats","parameters":[{"description":"Window start (RFC3339 or relative '12h'/'2d'); default 24h ago","name":"since","type":"string"},{"description":"Window end; default now","name":"until","type":"string"},{"description":"Only chats with at least N messages (default 1)","name":"min_messages","type":"number"},{"description":"Only chats where you sent at least one message","name":"only_outbound","type":"boolean"},{"description":"Exclude status@broadcast (default true)","name":"exclude_status","type":"boolean"},{"description":"Exclude large announce groups (default true)","name":"exclude_announce_groups","type":"boolean"},{"description":"Max chats (default 50, max 200)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Windowed activity digest: the conversations active in a time window, each with resolved name, in/out/media counts, last-message preview, and a needs_reply flag. One call answers 'who did I talk to' / 'what needs my attention'.","when_to_use":"The primary entrypoint for 'what happened' questions. Prefer over paging whatsapp_list_messages when you want an overview across chats. Defaults to the last 24h and hides status + announce-group noise."},{"category":"messaging","mcp_description":"List or fuzzy-search synced contacts with interaction metadata, ordered by last activity. When to use: Find contacts and DM JIDs by name or recent interaction. Pass query for fuzzy/phonetic match (handles accents like André↔Andre \u0026 ASR variants: Yasmin↔Iasmin). Pass sort_by='recent' or 'name'. Pass chat_type='private' for contacts with 1-on-1 DM chats. Parameters: query — Fuzzy name search (optional). Handles accents \u0026 ASR variants.; search — Alias for query; sort_by — recent (default; order by last message/contact time) or name (alphabetical); chat_type — private (only contacts with active 1-on-1 DM chats) or all; limit — Max contacts (default 50); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_contacts","parameters":[{"description":"Fuzzy name search (optional). Handles accents \u0026 ASR variants.","name":"query","type":"string"},{"description":"Alias for query","name":"search","type":"string"},{"description":"recent (default; order by last message/contact time) or name (alphabetical)","name":"sort_by","type":"string"},{"description":"private (only contacts with active 1-on-1 DM chats) or all","name":"chat_type","type":"string"},{"description":"Max contacts (default 50)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"List or fuzzy-search synced contacts with interaction metadata, ordered by last activity.","when_to_use":"Find contacts and DM JIDs by name or recent interaction. Pass query for fuzzy/phonetic match (handles accents like André↔Andre \u0026 ASR variants: Yasmin↔Iasmin). Pass sort_by='recent' or 'name'. Pass chat_type='private' for contacts with 1-on-1 DM chats."},{"category":"messaging","mcp_description":"List synced WhatsApp groups from bot_groups. When to use: Find group_jid values for group messaging. Use query to match current or past group names. Parameters: query — Search current and historical group names (e.g. old rename like XYZ - Notes); limit — Max groups (default 50); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_groups","parameters":[{"description":"Search current and historical group names (e.g. old rename like XYZ - Notes)","name":"query","type":"string"},{"description":"Max groups (default 50)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"List synced WhatsApp groups from bot_groups.","when_to_use":"Find group_jid values for group messaging. Use query to match current or past group names."},{"category":"messaging","mcp_description":"Return rename history for a group from bot_group_subject_history. When to use: Resolve old group names or audit rename events after whatsapp_list_groups. Parameters: group_jid (required) — Group JID e.g. 120363...@g.us; limit — Max history entries (default 25); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_group_subject_history","parameters":[{"description":"Group JID","example":"120363...@g.us","name":"group_jid","required":true,"type":"string"},{"description":"Max history entries (default 25)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Return rename history for a group from bot_group_subject_history.","when_to_use":"Resolve old group names or audit rename events after whatsapp_list_groups."},{"category":"messaging","mcp_description":"List members of a synced group. When to use: After whatsapp_list_groups when you need participants. Parameters: group_jid (required) — Group JID e.g. 120363...@g.us; limit — Max members (default 100); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_group_members","parameters":[{"description":"Group JID","example":"120363...@g.us","name":"group_jid","required":true,"type":"string"},{"description":"Max members (default 100)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"List members of a synced group.","when_to_use":"After whatsapp_list_groups when you need participants."},{"category":"session","mcp_description":"Return live connection status and nested sync summary. When to use: Quick check if messaging tools will work.","name":"whatsapp_get_session_status","requires_session":false,"summary":"Return live connection status and nested sync summary.","when_to_use":"Quick check if messaging tools will work."},{"category":"session","mcp_description":"Return detailed sync progress from Postgres (phase, progress_percent). When to use: Monitor history sync after pairing or reconnect.","name":"whatsapp_get_sync_status","requires_session":false,"summary":"Return detailed sync progress from Postgres (phase, progress_percent).","when_to_use":"Monitor history sync after pairing or reconnect."},{"category":"session","mcp_description":"Block until sync reaches target phase/progress or timeout. When to use: Immediately after pairing — wait before sending bulk messages. Parameters: wait_seconds — Max wait (default 120, max 600); min_progress — Required progress_percent 0-100; target_phase — Target phase e.g. completed;","name":"whatsapp_wait_for_sync","parameters":[{"description":"Max wait (default 120, max 600)","name":"wait_seconds","type":"number"},{"description":"Required progress_percent 0-100","name":"min_progress","type":"number"},{"description":"Target phase","example":"completed","name":"target_phase","type":"string"}],"requires_session":false,"summary":"Block until sync reaches target phase/progress or timeout.","when_to_use":"Immediately after pairing — wait before sending bulk messages."},{"category":"session","mcp_description":"Resolve phone, JID, contact/chat/group name into a JID (unified fuzzy resolve). When to use: Before send when recipient is spoken name or bare phone. Parameters: recipient — Phone, JID, or name e.g. Iasmin; kind — any|contact|chat|group;","name":"whatsapp_resolve_recipient","parameters":[{"description":"Phone, JID, or name","example":"Iasmin","name":"recipient","type":"string"},{"description":"any|contact|chat|group","name":"kind","type":"string"}],"requires_session":false,"summary":"Resolve phone, JID, contact/chat/group name into a JID (unified fuzzy resolve).","when_to_use":"Before send when recipient is spoken name or bare phone."},{"category":"session","mcp_description":"Single-call diagnostics: connection, sync, notifications, MCP audit. When to use: First tool when anything feels broken.","name":"whatsapp_health_check","requires_session":false,"summary":"Single-call diagnostics: connection, sync, notifications, MCP audit.","when_to_use":"First tool when anything feels broken."},{"category":"session","mcp_description":"Return MCP endpoint, token, headers, and cursor_mcp_example for agent hosts. When to use: Configure Grok/Cursor/Claude after pairing or to verify MCP config.","name":"whatsapp_get_mcp_connection","requires_session":false,"summary":"Return MCP endpoint, token, headers, and cursor_mcp_example for agent hosts.","when_to_use":"Configure Grok/Cursor/Claude after pairing or to verify MCP config."},{"category":"session","mcp_description":"Rotate the mcp_ bearer token and return fresh connection details. When to use: When MCP returns 401 Unauthorized — update agent host config with new token. Parameters: phone_number — Defaults to bound session phone;","name":"whatsapp_rotate_mcp_token","parameters":[{"description":"Defaults to bound session phone","name":"phone_number","type":"string"}],"requires_session":false,"summary":"Rotate the mcp_ bearer token and return fresh connection details.","when_to_use":"When MCP returns 401 Unauthorized — update agent host config with new token."},{"category":"session","mcp_description":"List recent MCP tool audit entries for this phone. When to use: Debug failed tool calls or rate limits. Parameters: limit — Max entries (default 50); offset — Pagination offset; Requires: allow_read=true.","name":"whatsapp_get_audit_log","parameters":[{"description":"Max entries (default 50)","name":"limit","type":"number"},{"description":"Pagination offset","name":"offset","type":"number"}],"requires_allow_read":true,"requires_session":false,"summary":"List recent MCP tool audit entries for this phone.","when_to_use":"Debug failed tool calls or rate limits."},{"category":"notifications","mcp_description":"List agent notifications (message_in, sync_progress, etc.). Results include sender_name/chat_name when available. When to use: Poll for events; prefer whatsapp_watch_notifications for blocking wait. Use whatsapp_get_notification_summary for grouped overviews. Parameters: limit — Max rows (default 50); offset — Pagination offset; unread_only — Only unread rows (default true); chat_jid — Filter to a single chat; Requires: allow_read=true.","name":"whatsapp_list_notifications","parameters":[{"description":"Max rows (default 50)","name":"limit","type":"number"},{"description":"Pagination offset","name":"offset","type":"number"},{"description":"Only unread rows (default true)","name":"unread_only","type":"boolean"},{"description":"Filter to a single chat","name":"chat_jid","type":"string"}],"requires_allow_read":true,"requires_session":false,"summary":"List agent notifications (message_in, sync_progress, etc.). Results include sender_name/chat_name when available.","when_to_use":"Poll for events; prefer whatsapp_watch_notifications for blocking wait. Use whatsapp_get_notification_summary for grouped overviews."},{"category":"notifications","mcp_description":"Long-poll until new notifications arrive or timeout. When to use: Event-driven agent loop instead of tight polling. Parameters: wait_seconds — Max wait (default 30, max 120); since_id — Only ids greater than this; unread_only — Default true; Requires: allow_read=true.","name":"whatsapp_watch_notifications","parameters":[{"description":"Max wait (default 30, max 120)","name":"wait_seconds","type":"number"},{"description":"Only ids greater than this","name":"since_id","type":"number"},{"description":"Default true","name":"unread_only","type":"boolean"}],"requires_allow_read":true,"requires_session":false,"summary":"Long-poll until new notifications arrive or timeout.","when_to_use":"Event-driven agent loop instead of tight polling."},{"category":"notifications","mcp_description":"Return unread notification count. When to use: Badge/summary without listing full rows. Requires: allow_read=true.","name":"whatsapp_get_unread_notification_count","requires_allow_read":true,"requires_session":false,"summary":"Return unread notification count.","when_to_use":"Badge/summary without listing full rows."},{"category":"notifications","mcp_description":"High-signal grouped summary of recent/unread notifications with resolved names and per-chat activity. Preferred over raw list for understanding what happened. When to use: Get a concise digest ('3 from Iasmin: latest text + stickers'). Use after watch or when you want an overview instead of paging raw rows. Parameters: limit — How many recent notifications to analyze (default 100); unread_only — Default true; chat_jid — Optional filter; exclude_status — Exclude status@broadcast (default true); exclude_announce — Exclude announce (broadcast) groups (default false); Requires: allow_read=true.","name":"whatsapp_get_notification_summary","parameters":[{"description":"How many recent notifications to analyze (default 100)","name":"limit","type":"number"},{"description":"Default true","name":"unread_only","type":"boolean"},{"description":"Optional filter","name":"chat_jid","type":"string"},{"description":"Exclude status@broadcast (default true)","name":"exclude_status","type":"boolean"},{"description":"Exclude announce (broadcast) groups (default false)","name":"exclude_announce","type":"boolean"}],"requires_allow_read":true,"requires_session":false,"summary":"High-signal grouped summary of recent/unread notifications with resolved names and per-chat activity. Preferred over raw list for understanding what happened.","when_to_use":"Get a concise digest ('3 from Iasmin: latest text + stickers'). Use after watch or when you want an overview instead of paging raw rows."},{"category":"notifications","mcp_description":"Mark one agent notification row as read. When to use: After processing a message_in or sync event. Parameters: notification_id (required) — Row id from list_notifications; read_by — Agent id for audit; Requires: allow_read=true.","name":"whatsapp_mark_notification_read","parameters":[{"description":"Row id from list_notifications","name":"notification_id","required":true,"type":"number"},{"description":"Agent id for audit","name":"read_by","type":"string"}],"requires_allow_read":true,"requires_session":false,"summary":"Mark one agent notification row as read.","when_to_use":"After processing a message_in or sync event."},{"category":"notifications","mcp_description":"Mark all unread notifications read for this phone. Parameters: read_by — Agent id for audit; Requires: allow_read=true.","name":"whatsapp_mark_all_notifications_read","parameters":[{"description":"Agent id for audit","name":"read_by","type":"string"}],"requires_allow_read":true,"requires_session":false,"summary":"Mark all unread notifications read for this phone.","when_to_use":""},{"category":"notifications","mcp_description":"Bulk mark-read or delete notifications by filter (chat, event type, age) — clear a backlog in one call. When to use: Drain a large unread backlog, or prune noisy rows (e.g. old message_in from a busy chat). Use delete=true to remove rows, otherwise they are marked read. Parameters: chat_jid — Optional: restrict to one chat; event_type — Optional: restrict to one event_type e.g. message_in; older_than_days — Optional: only rows older than N days; only_read — When delete=true, restrict to already-read rows; delete — false = mark read (default), true = delete rows; Requires: allow_read=true.","name":"whatsapp_cleanup_notifications","parameters":[{"description":"Optional: restrict to one chat","name":"chat_jid","type":"string"},{"description":"Optional: restrict to one event_type","example":"message_in","name":"event_type","type":"string"},{"description":"Optional: only rows older than N days","name":"older_than_days","type":"number"},{"description":"When delete=true, restrict to already-read rows","name":"only_read","type":"boolean"},{"description":"false = mark read (default), true = delete rows","name":"delete","type":"boolean"}],"requires_allow_read":true,"requires_session":false,"summary":"Bulk mark-read or delete notifications by filter (chat, event type, age) — clear a backlog in one call.","when_to_use":"Drain a large unread backlog, or prune noisy rows (e.g. old message_in from a busy chat). Use delete=true to remove rows, otherwise they are marked read."},{"category":"lifecycle","mcp_description":"Reconnect stored session or start pairing-code re-auth when logged out. When to use: Session offline (409) or health_check shows disconnected. Parameters: phone_number — Defaults to bound phone; wait_seconds — Wait for pairing code (max 60);","name":"whatsapp_reconnect","parameters":[{"description":"Defaults to bound phone","name":"phone_number","type":"string"},{"description":"Wait for pairing code (max 60)","name":"wait_seconds","type":"number"}],"requires_session":false,"summary":"Reconnect stored session or start pairing-code re-auth when logged out.","when_to_use":"Session offline (409) or health_check shows disconnected."},{"category":"lifecycle","mcp_description":"Logout and unlink (default) or soft-deactivate the WhatsApp session. When to use: User requests logout / full re-pair; blocked while sync_protected. Parameters: phone_number — Defaults to bound phone; unlink — Default true: clear device credentials. false: soft deactivate only;","name":"whatsapp_disconnect","parameters":[{"description":"Defaults to bound phone","name":"phone_number","type":"string"},{"description":"Default true: clear device credentials. false: soft deactivate only","name":"unlink","type":"boolean"}],"requires_session":false,"summary":"Logout and unlink (default) or soft-deactivate the WhatsApp session.","when_to_use":"User requests logout / full re-pair; blocked while sync_protected."},{"category":"lifecycle","mcp_description":"Start 8-digit pairing for a new or logged-out phone. Offline owner sessions re-pair without MFA; live unowned sessions return handoff_confirmation_required. When to use: Provision another phone when allow_provision=true; force=true for owned number re-pair. Parameters: phone_number (required) — Digits only e.g. 5511999999999; wait_seconds — Wait for code generation; force — Unlink + fresh pair when caller owns the number; device_name — Linked Devices label (e.g. MeoChat), max 32 chars; Requires: allow_provision=true.","name":"whatsapp_connect_code","parameters":[{"description":"Digits only","example":"5511999999999","name":"phone_number","required":true,"type":"string"},{"description":"Wait for code generation","name":"wait_seconds","type":"number"},{"description":"Unlink + fresh pair when caller owns the number","name":"force","type":"boolean"},{"description":"Linked Devices label (e.g. MeoChat), max 32 chars","name":"device_name","type":"string"}],"requires_allow_provision":true,"requires_session":false,"summary":"Start 8-digit pairing for a new or logged-out phone. Offline owner sessions re-pair without MFA; live unowned sessions return handoff_confirmation_required.","when_to_use":"Provision another phone when allow_provision=true; force=true for owned number re-pair."},{"category":"lifecycle","mcp_description":"Start QR pairing for a new session. When to use: User prefers QR scan; requires allow_provision. Parameters: phone_number — Optional phone hint; wait_seconds — Wait for QR data; force — Unlink + fresh QR when caller owns the number; device_name — Linked Devices label (e.g. MeoChat); Requires: allow_provision=true.","name":"whatsapp_connect_qr","parameters":[{"description":"Optional phone hint","name":"phone_number","type":"string"},{"description":"Wait for QR data","name":"wait_seconds","type":"number"},{"description":"Unlink + fresh QR when caller owns the number","name":"force","type":"boolean"},{"description":"Linked Devices label (e.g. MeoChat)","name":"device_name","type":"string"}],"requires_allow_provision":true,"requires_session":false,"summary":"Start QR pairing for a new session.","when_to_use":"User prefers QR scan; requires allow_provision."},{"category":"lifecycle","mcp_description":"Start dual QR + pairing-code flow with ASCII QR, image URL, and pairing code. When to use: Best single-call onboarding for agents provisioning a new phone. Parameters: phone_number (required) — Digits only; wait_seconds — Wait for kit assets; force — Unlink + fresh kit when caller owns the number; device_name — Linked Devices label (e.g. MeoChat); Requires: allow_provision=true.","name":"whatsapp_connect_kit","parameters":[{"description":"Digits only","name":"phone_number","required":true,"type":"string"},{"description":"Wait for kit assets","name":"wait_seconds","type":"number"},{"description":"Unlink + fresh kit when caller owns the number","name":"force","type":"boolean"},{"description":"Linked Devices label (e.g. MeoChat)","name":"device_name","type":"string"}],"requires_allow_provision":true,"requires_session":false,"summary":"Start dual QR + pairing-code flow with ASCII QR, image URL, and pairing code.","when_to_use":"Best single-call onboarding for agents provisioning a new phone."},{"category":"lifecycle","mcp_description":"Poll connect flow status by uuid from connect_code, connect_qr, or connect_kit. When to use: After starting any connect flow until status=success. Parameters: uuid (required) — Connect flow UUID;","name":"whatsapp_connect_status","parameters":[{"description":"Connect flow UUID","name":"uuid","required":true,"type":"string"}],"requires_session":false,"summary":"Poll connect flow status by uuid from connect_code, connect_qr, or connect_kit.","when_to_use":"After starting any connect flow until status=success."},{"category":"lifecycle","mcp_description":"Enable MCP or rotate token; set allow_provision and permissions. When to use: After pairing if mcp_connection missing, or to enable allow_provision=true. Parameters: phone_number — Defaults to bound phone; rotate_token — Issue new mcp_ token; allow_provision — Allow lifecycle connect tools; Requires: active WhatsApp session (connected + logged_in).","name":"whatsapp_enable_mcp","parameters":[{"description":"Defaults to bound phone","name":"phone_number","type":"string"},{"description":"Issue new mcp_ token","name":"rotate_token","type":"boolean"},{"description":"Allow lifecycle connect tools","name":"allow_provision","type":"boolean"}],"requires_session":true,"summary":"Enable MCP or rotate token; set allow_provision and permissions.","when_to_use":"After pairing if mcp_connection missing, or to enable allow_provision=true."},{"category":"multi_account","mcp_description":"List every WhatsApp number under this token's tenant, each with its label, live status, and an accessible flag showing whether this token may operate it. When to use: FIRST call when a token may control more than one number. Discover which numbers you can send/read from and their labels, then pass phone_number (digits or label) to messaging/read tools.","name":"whatsapp_list_tenant_numbers","requires_session":false,"summary":"List every WhatsApp number under this token's tenant, each with its label, live status, and an accessible flag showing whether this token may operate it.","when_to_use":"FIRST call when a token may control more than one number. Discover which numbers you can send/read from and their labels, then pass phone_number (digits or label) to messaging/read tools."},{"category":"multi_account","mcp_description":"Set or clear a friendly alias (label) for a connected number, e.g. 'sales'. The label can then be passed as phone_number to any tool. When to use: To name numbers so agents can target them by alias instead of raw digits. Parameters: phone_number — Number to label (digits or existing label); defaults to bound session; label — Alias, e.g. 'sales'. Empty string clears it. e.g. sales;","name":"whatsapp_set_number_label","parameters":[{"description":"Number to label (digits or existing label); defaults to bound session","name":"phone_number","type":"string"},{"description":"Alias, e.g. 'sales'. Empty string clears it.","example":"sales","name":"label","type":"string"}],"requires_session":false,"summary":"Set or clear a friendly alias (label) for a connected number, e.g. 'sales'. The label can then be passed as phone_number to any tool.","when_to_use":"To name numbers so agents can target them by alias instead of raw digits."},{"category":"multi_account","mcp_description":"Return the token's tenant (id, name, slug) and tenant_key. The tenant_key is the consent secret used to group numbers together. When to use: To read the tenant_key before grouping another number via whatsapp_transfer_number.","name":"whatsapp_get_tenant","requires_session":false,"summary":"Return the token's tenant (id, name, slug) and tenant_key. The tenant_key is the consent secret used to group numbers together.","when_to_use":"To read the tenant_key before grouping another number via whatsapp_transfer_number."},{"category":"multi_account","mcp_description":"Move a number into the tenant identified by a tenant_key, so one token (access_mode=tenant_all) can control both numbers. When to use: To group two numbers under one tenant. Get the destination tenant_key from whatsapp_get_tenant on the target tenant. Parameters: tenant_key (required) — Destination tenant's tnt_ key; phone_number — Number to move (digits or label); defaults to bound session; Requires: allow_provision=true.","name":"whatsapp_transfer_number","parameters":[{"description":"Destination tenant's tnt_ key","name":"tenant_key","required":true,"type":"string"},{"description":"Number to move (digits or label); defaults to bound session","name":"phone_number","type":"string"}],"requires_allow_provision":true,"requires_session":false,"summary":"Move a number into the tenant identified by a tenant_key, so one token (access_mode=tenant_all) can control both numbers.","when_to_use":"To group two numbers under one tenant. Get the destination tenant_key from whatsapp_get_tenant on the target tenant."},{"category":"messaging","mcp_description":"Delete (revoke for everyone) a previously sent message. When to use: Retract a message you sent by mistake. Parameters: message_id (required) — message_id to revoke; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_delete_message","parameters":[{"description":"message_id to revoke","name":"message_id","required":true,"type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Delete (revoke for everyone) a previously sent message.","when_to_use":"Retract a message you sent by mistake."},{"category":"messaging","mcp_description":"Edit the text of a message you previously sent. When to use: Fix a typo or update content without sending a new message. Parameters: message_id (required) — message_id to edit; content (required) — New text content; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_edit_message","parameters":[{"description":"message_id to edit","name":"message_id","required":true,"type":"string"},{"description":"New text content","name":"content","required":true,"type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Edit the text of a message you previously sent.","when_to_use":"Fix a typo or update content without sending a new message."},{"category":"contacts","mcp_description":"Check whether phone numbers are registered on WhatsApp. When to use: Validate recipients before sending, or resolve a phone to its JID. Parameters: phones (required) — Comma-separated phone numbers e.g. 5511999999999,5511888888888; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_check_number","parameters":[{"description":"Comma-separated phone numbers","example":"5511999999999,5511888888888","name":"phones","required":true,"type":"string"}],"requires_allow_read":true,"requires_session":true,"summary":"Check whether phone numbers are registered on WhatsApp.","when_to_use":"Validate recipients before sending, or resolve a phone to its JID."},{"category":"groups","mcp_description":"Create a new WhatsApp group with the given participants. When to use: Spin up a group programmatically. Parameters: name (required) — Group name (max 25 chars); participants (required) — Comma-separated phone numbers; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_create_group","parameters":[{"description":"Group name (max 25 chars)","name":"name","required":true,"type":"string"},{"description":"Comma-separated phone numbers","name":"participants","required":true,"type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Create a new WhatsApp group with the given participants.","when_to_use":"Spin up a group programmatically."},{"category":"groups","mcp_description":"Add, remove, promote or demote group participants. When to use: Manage group membership and admins. Parameters: group_jid (required) — Target group JID; action (required) — add | remove | promote | demote; participants (required) — Comma-separated phone numbers; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_update_group_participants","parameters":[{"description":"Target group JID","name":"group_jid","required":true,"type":"string"},{"description":"add | remove | promote | demote","name":"action","required":true,"type":"string"},{"description":"Comma-separated phone numbers","name":"participants","required":true,"type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Add, remove, promote or demote group participants.","when_to_use":"Manage group membership and admins."},{"category":"groups","mcp_description":"Get (or reset) a group's invite link. When to use: Share a join link, or revoke and regenerate it with reset=true. Parameters: group_jid (required) — Target group JID; reset — Reset (revoke + regenerate) the link; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_group_invite_link","parameters":[{"description":"Target group JID","name":"group_jid","required":true,"type":"string"},{"description":"Reset (revoke + regenerate) the link","name":"reset","type":"boolean"}],"requires_allow_read":true,"requires_session":true,"summary":"Get (or reset) a group's invite link.","when_to_use":"Share a join link, or revoke and regenerate it with reset=true."},{"category":"reports","mcp_description":"Cross-chat analytics: totals, per-day message volume, and top chats. When to use: Build a usage/activity report across all chats over a time window. Parameters: days — Window in days (1-90, default 14); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_overview_stats","parameters":[{"description":"Window in days (1-90, default 14)","name":"days","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Cross-chat analytics: totals, per-day message volume, and top chats.","when_to_use":"Build a usage/activity report across all chats over a time window."},{"category":"session","mcp_description":"Get the per-phone webhook URL, enabled state, and event filter settings. When to use: Read the current outbound webhook configuration for this phone number. Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_webhook_config","requires_allow_read":true,"requires_session":true,"summary":"Get the per-phone webhook URL, enabled state, and event filter settings.","when_to_use":"Read the current outbound webhook configuration for this phone number."},{"category":"session","mcp_description":"Configure or enable/disable per-phone outbound webhooks and filter event types (message_received, session_disconnected, sync_completed, etc.). When to use: Enable/disable webhook delivery, change webhook URL, set HMAC secret, or pick specific event types. Parameters: url — Target HTTP(S) webhook URL; enabled — Enable or disable webhook delivery; events — Comma-separated event types: message_received, message_sent, session_paired, session_disconnected, sync_completed, sync_progress, or * (default all); secret — Optional HMAC secret key for X-WAMCP-Signature verification; Requires: active WhatsApp session (connected + logged_in), allow_provision=true.","name":"whatsapp_set_webhook_config","parameters":[{"description":"Target HTTP(S) webhook URL","name":"url","type":"string"},{"description":"Enable or disable webhook delivery","name":"enabled","type":"boolean"},{"description":"Comma-separated event types: message_received, message_sent, session_paired, session_disconnected, sync_completed, sync_progress, or * (default all)","name":"events","type":"string"},{"description":"Optional HMAC secret key for X-WAMCP-Signature verification","name":"secret","type":"string"}],"requires_allow_provision":true,"requires_session":true,"summary":"Configure or enable/disable per-phone outbound webhooks and filter event types (message_received, session_disconnected, sync_completed, etc.).","when_to_use":"Enable/disable webhook delivery, change webhook URL, set HMAC secret, or pick specific event types."},{"category":"groups","mcp_description":"Configure group permissions: announce-only (admins send msgs), locked (admins edit info), or join approval mode. When to use: Toggle group operational settings. Parameters: group_jid (required) — Target group JID; announce — True = only admins can send messages; locked — True = only admins can edit group info; join_approval — True = admin approval required for new members; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_set_group_settings","parameters":[{"description":"Target group JID","name":"group_jid","required":true,"type":"string"},{"description":"True = only admins can send messages","name":"announce","type":"boolean"},{"description":"True = only admins can edit group info","name":"locked","type":"boolean"},{"description":"True = admin approval required for new members","name":"join_approval","type":"boolean"}],"requires_allow_send":true,"requires_session":true,"summary":"Configure group permissions: announce-only (admins send msgs), locked (admins edit info), or join approval mode.","when_to_use":"Toggle group operational settings."},{"category":"groups","mcp_description":"Update group subject/name or group description/topic. When to use: Change group title or description. Parameters: group_jid (required) — Target group JID; name — New group name (max 25 chars); description — New group description; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_update_group_info","parameters":[{"description":"Target group JID","name":"group_jid","required":true,"type":"string"},{"description":"New group name (max 25 chars)","name":"name","type":"string"},{"description":"New group description","name":"description","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Update group subject/name or group description/topic.","when_to_use":"Change group title or description."},{"category":"messaging","mcp_description":"Send an interactive poll with multiple options. When to use: Ask users or group members to vote on a question. Parameters: recipient (required) — Phone, JID, contact name, or group name; question (required) — Poll question text; options (required) — Comma-separated list of poll options (min 2); selectable_count — Max selectable options per voter (default 1); Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_poll","parameters":[{"description":"Phone, JID, contact name, or group name","name":"recipient","required":true,"type":"string"},{"description":"Poll question text","name":"question","required":true,"type":"string"},{"description":"Comma-separated list of poll options (min 2)","name":"options","required":true,"type":"string"},{"description":"Max selectable options per voter (default 1)","name":"selectable_count","type":"number"}],"requires_allow_send":true,"requires_session":true,"summary":"Send an interactive poll with multiple options.","when_to_use":"Ask users or group members to vote on a question."},{"category":"messaging","mcp_description":"Send an audio file formatted as a native push-to-talk (PTT) voice note. When to use: Send a voice message with the blue microphone indicator. Parameters: recipient (required) — Phone, JID, or chat name; audio_url (required) — Public HTTP(S) URL of audio file (ogg/mp3/m4a); Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_voice_note","parameters":[{"description":"Phone, JID, or chat name","name":"recipient","required":true,"type":"string"},{"description":"Public HTTP(S) URL of audio file (ogg/mp3/m4a)","name":"audio_url","required":true,"type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Send an audio file formatted as a native push-to-talk (PTT) voice note.","when_to_use":"Send a voice message with the blue microphone indicator."},{"category":"messaging","mcp_description":"Send GPS coordinates and optional location name/address. When to use: Share a pin or business address location with a recipient. Parameters: recipient (required) — Phone, JID, or chat name; latitude (required) — Latitude float coordinate; longitude (required) — Longitude float coordinate; name — Optional place name (e.g. Headquarters); address — Optional street address; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_location","parameters":[{"description":"Phone, JID, or chat name","name":"recipient","required":true,"type":"string"},{"description":"Latitude float coordinate","name":"latitude","required":true,"type":"number"},{"description":"Longitude float coordinate","name":"longitude","required":true,"type":"number"},{"description":"Optional place name (e.g. Headquarters)","name":"name","type":"string"},{"description":"Optional street address","name":"address","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Send GPS coordinates and optional location name/address.","when_to_use":"Share a pin or business address location with a recipient."},{"category":"messaging","mcp_description":"Send a contact card (vCard) to a WhatsApp recipient. When to use: Share someone's contact details (name and phone) in chat. Parameters: recipient (required) — Phone, JID, or chat name; contact_name (required) — Full name of contact being shared; contact_phone (required) — Phone number of contact being shared; organization — Optional company/organization name; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_vcard","parameters":[{"description":"Phone, JID, or chat name","name":"recipient","required":true,"type":"string"},{"description":"Full name of contact being shared","name":"contact_name","required":true,"type":"string"},{"description":"Phone number of contact being shared","name":"contact_phone","required":true,"type":"string"},{"description":"Optional company/organization name","name":"organization","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Send a contact card (vCard) to a WhatsApp recipient.","when_to_use":"Share someone's contact details (name and phone) in chat."},{"category":"messaging","mcp_description":"Set disappearing messages expiration timer for a chat. When to use: Enable or disable ephemeral messages in a chat. Parameters: chat_jid (required) — Target chat JID or recipient name; duration_seconds (required) — 0 to disable, 86400 (24h), 604800 (7d), or 7776000 (90d); Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_set_disappearing_messages","parameters":[{"description":"Target chat JID or recipient name","name":"chat_jid","required":true,"type":"string"},{"description":"0 to disable, 86400 (24h), 604800 (7d), or 7776000 (90d)","name":"duration_seconds","required":true,"type":"number"}],"requires_allow_send":true,"requires_session":true,"summary":"Set disappearing messages expiration timer for a chat.","when_to_use":"Enable or disable ephemeral messages in a chat."},{"category":"messaging","mcp_description":"Pin or unpin a chat conversation to top of chat list. When to use: Pin an important conversation. Parameters: chat_jid (required) — Target chat JID or recipient name; pin — True to pin, False to unpin (default True); Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_pin_chat","parameters":[{"description":"Target chat JID or recipient name","name":"chat_jid","required":true,"type":"string"},{"description":"True to pin, False to unpin (default True)","name":"pin","type":"boolean"}],"requires_allow_send":true,"requires_session":true,"summary":"Pin or unpin a chat conversation to top of chat list.","when_to_use":"Pin an important conversation."},{"category":"messaging","mcp_description":"Star (favorite) or unstar a specific message. When to use: Mark an important message with a star. Parameters: message_id (required) — Target message ID; star — True to star, False to unstar (default True); chat_jid — Optional chat JID fallback if message_id not in local store; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_star_message","parameters":[{"description":"Target message ID","name":"message_id","required":true,"type":"string"},{"description":"True to star, False to unstar (default True)","name":"star","type":"boolean"},{"description":"Optional chat JID fallback if message_id not in local store","name":"chat_jid","type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Star (favorite) or unstar a specific message.","when_to_use":"Mark an important message with a star."},{"category":"messaging","mcp_description":"Mute or unmute notifications for a chat conversation. When to use: Silence notifications from noisy chats. Parameters: chat_jid (required) — Target chat JID or recipient name; mute — True to mute, False to unmute (default True); duration_hours — Duration in hours to mute (default 8); Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_mute_chat","parameters":[{"description":"Target chat JID or recipient name","name":"chat_jid","required":true,"type":"string"},{"description":"True to mute, False to unmute (default True)","name":"mute","type":"boolean"},{"description":"Duration in hours to mute (default 8)","name":"duration_hours","type":"number"}],"requires_allow_send":true,"requires_session":true,"summary":"Mute or unmute notifications for a chat conversation.","when_to_use":"Silence notifications from noisy chats."}],"transport":"streamable-http"},"product_name":"WAMCP","product_tagline":"Production WhatsApp infrastructure for AI agents","purpose":"Canonical machine-readable guide for AI agents to pair WhatsApp, obtain dedicated MCP credentials (named per-agent), wait for sync, operate via MCP tools, and direct account owners to the minimal settings portal for access control.","rest_api":{"authentication":"X-API-Key: wbot_... (or Authorization: Bearer wbot_...). Tenant-scoped; all calls are audited in the owner portal.","description":"Every MCP tool has a REST equivalent under /api/*. Build WhatsApp-Web-style clients, dashboards, and enterprise tools directly over HTTP — no MCP client required.","forward_note":"POST /api/messages/forward re-sends a stored message to the same chat, or to a different chat via the optional 'to' field.","openapi":"https://zap.zaptdev.com/docs/openapi.yaml","swagger_ui":"https://zap.zaptdev.com/docs","tool_to_rest":{"whatsapp_check_number":"GET /api/contacts/check","whatsapp_cleanup_notifications":"POST /api/notifications/cleanup","whatsapp_create_group":"POST /api/groups/create","whatsapp_delete_message":"POST /api/messages/delete","whatsapp_download_media":"GET /api/messages/media","whatsapp_edit_message":"POST /api/messages/edit","whatsapp_get_audit_log":"GET /api/audit","whatsapp_get_chat":"GET /api/chats/detail","whatsapp_get_chat_stats":"GET /api/chats/stats","whatsapp_get_group_invite_link":"GET /api/groups/invite","whatsapp_get_group_members":"GET /api/groups/members","whatsapp_get_group_subject_history":"GET /api/groups/subject-history","whatsapp_get_overview_stats":"GET /api/stats/overview","whatsapp_get_session_status":"GET /api/session","whatsapp_get_sync_status":"GET /api/session/sync","whatsapp_list_chats":"GET /api/chats","whatsapp_list_contacts":"GET /api/contacts","whatsapp_list_groups":"GET /api/groups","whatsapp_list_interacted_chats":"GET /api/chats/interacted","whatsapp_list_messages":"GET /api/messages","whatsapp_list_notifications":"GET /api/notifications","whatsapp_mark_all_notifications_read":"POST /api/notifications/read-all","whatsapp_mark_chat_read":"POST /api/chats/read","whatsapp_mark_notification_read":"POST /api/notifications/read","whatsapp_reply_to_message":"POST /api/send/reply","whatsapp_resolve_recipient":"GET /api/contacts/resolve","whatsapp_schedule_message":"POST /api/messages/schedule","whatsapp_search_messages":"GET /api/messages/search","whatsapp_send_message":"POST /api/send (text or media_url)","whatsapp_send_reaction":"POST /api/messages/reaction","whatsapp_send_typing":"POST /api/send/typing","whatsapp_update_group_participants":"POST /api/groups/participants","whatsapp_wait_for_sync":"GET /api/session/wait-sync","whatsapp_watch_notifications":"GET /api/notifications/stream (SSE)"},"version_alias":"Prefix any path with /api/v1/ to pin a version."},"search_keywords":["WhatsApp MCP","WhatsApp API for AI agents","Model Context Protocol WhatsApp","WhatsApp automation API","white-label WhatsApp","Cursor WhatsApp MCP","Claude WhatsApp integration","Grok MCP WhatsApp","named MCP credentials"],"session_lifecycle":{"disconnect":{"auth":["X-API-Key wbot_...","X-Tenant-Key tnt_... + phone_number"],"mcp":"whatsapp_disconnect","rest":"POST /api/session/disconnect","soft":"unlink:false — deactivate only; number may still look registered","unlink":"true (default on MCP) — full logout + clear device store so next connect can pair"},"offline_vs_live":{"cross_tenant":"error phone_owned_by_other_tenant","live_unowned":"handoff_confirmation_required — MFA self-chat","offline_owned":"fall through to pairing code (no MFA)","offline_unowned":"registered_offline + suggested_action force_pair_requires_owner"},"re_pair":{"alt":"POST disconnect unlink:true then connect without force","prefer":"POST connect/* with force:true using owning tenant key","when":"Session offline (zombie), user wants new connection, or health_check shows logged_in=false"}},"skills_guide_note":"The /guides/skills page contains production-grade recipes (Reactive Loop, Inbox Triage, Scheduled Digest, Memory, etc.) with exact sequences, approval gates, audit transparency, example traces, and host-specific instructions. The page is deliberately simple at the top for owners and deeply technical below for agents.","starter_skills":[{"description":"Summarizes recent activity (last 24h). Best first read-only skill. Uses watch + search + get_chat + optional mark read.","host_examples":"Claude Projects / Cursor / Grok — full blocks and config at /guides/skills","key_tools":["whatsapp_health_check","whatsapp_watch_notifications","whatsapp_get_chat","whatsapp_search_messages","whatsapp_mark_chat_read","whatsapp_get_audit_log"],"name":"daily_brief","recommended_permissions":{"allow_read":true,"allow_send":false},"sample_prompt":"See full upgraded prompt + patterns at /guides/skills (Reactive Loop + Daily Brief).","sequence":["health_check","watch_notifications or search (since 24h)","get_chat for active","compile markdown","optional mark_chat_read","get_audit_log report"],"title":"Daily Brief"},{"description":"The primary agentic pattern: long-poll watch_notifications instead of polling. Combine with approval + audit.","key_tools":["whatsapp_watch_notifications","whatsapp_get_chat","whatsapp_resolve_recipient","whatsapp_reply_to_message","whatsapp_mark_chat_read","whatsapp_get_audit_log"],"name":"reactive_loop","recommended_permissions":{"allow_read":true,"allow_send":true},"sequence":["watch_notifications (loop)","get_chat","resolve if needed","approval gate","reply or send","mark_chat_read","audit report"],"title":"Reactive Event Loop (core pattern)"},{"description":"Context-aware drafts with strict approval gate before any send.","key_tools":["whatsapp_resolve_recipient","whatsapp_get_chat","whatsapp_search_messages","whatsapp_reply_to_message","whatsapp_get_audit_log"],"name":"smart_reply_assistant","recommended_permissions":{"allow_read":true,"allow_send":true},"title":"Smart Reply Assistant"},{"description":"Watches specific senders via watch_notifications and forwards summaries to a hub.","key_tools":["whatsapp_watch_notifications","whatsapp_get_chat","whatsapp_send_message","whatsapp_get_audit_log"],"name":"notification_forwarder","recommended_permissions":{"allow_read":true,"allow_send":true},"title":"Selective Forwarder / Priority Alert"},{"description":"Time-bounded search + get_chat compiled and delivered via schedule_message.","key_tools":["whatsapp_search_messages","whatsapp_get_chat","whatsapp_schedule_message"],"name":"scheduled_digest","recommended_permissions":{"allow_read":true,"allow_send":true},"title":"Scheduled Digest + Delivery"},{"description":"High-fidelity recall using search + filters + group history. Read-only friendly.","key_tools":["whatsapp_resolve_recipient","whatsapp_search_messages","whatsapp_get_chat","whatsapp_get_group_subject_history"],"name":"memory_recall","recommended_permissions":{"allow_read":true,"allow_send":false},"title":"Memory \u0026 Context Recall"},{"description":"Wrap low-level tools + guardrails (approval, audit, resolve) into named commands. Full recipes + Tier-1 skills + patterns at /guides/skills.","details_url":"https://zap.zaptdev.com/guides/skills#custom-functions","name":"custom_function_recipe","title":"Building higher-level custom functions"}],"tenant_bootstrap":{"body_example":{"name":"Acme Corp","slug":"acme-corp"},"note":"Personal /connect does not require this step; the server auto-provisions a workspace when X-Tenant-Key is omitted.","optional":true,"public_create":"POST https://zap.zaptdev.com/api/tenants","store_in_env":["WAMCP_TENANT_ID=\u003cid\u003e","WAMCP_TENANT_KEY=tnt_..."],"then_connect":"POST /api/session/connect/kit with header X-Tenant-Key: $WAMCP_TENANT_KEY","ui":"https://zap.zaptdev.com/connect?mode=saas (workspace panel) or /connect?tenant=tnt_... (prefilled)","verify":"GET /api/tenants with X-Tenant-Key returns id/slug/name","when":"Multi-number org, white-label product, or you need a stable tnt_... in .env before pairing"},"title":"WAMCP — WhatsApp MCP Platform — AI Agent Playbook","trust_signals":["Tenant-scoped connect flows (tnt_...) with UUID enumeration protection","All MCP tool calls audited in whatsmeow_mcp_audit","Self-chat MFA for /connect credential delivery (IsFromMe verification)","Sync-aware session protection during history import","Minimal owner settings portal with named per-MCP credentials (nicknames + independent send/read/provision + send_mode: auto|approval|read_only). Approvals UI shows collapsible history/reasoning/proposed for review."],"version":"2.4.0","workflow":[{"description":"Load this playbook or OpenAPI. Humans use /connect; agents can use REST or MCP lifecycle tools.","endpoints":["GET /docs/agent-playbook","GET /docs/openapi.yaml","GET /connect","GET /health"],"name":"discover_api","step":1},{"agent_action":"Do not start connect until phone is known. For multi-number/SaaS, also collect or create tnt_... (see tenant_bootstrap).","description":"Collect WhatsApp number (digits + country code). Workspace key (tnt_...) is optional for personal connects.","human_alt":"Send personal users to /connect. Send SaaS clients to /connect?tenant=tnt_... or /connect?mode=saas.","name":"ask_user_phone","step":2},{"body_flags":{"force":"true — unlink + fresh pair when caller owns the number (tenant key of owner, phone-bound credentials, or admin)"},"description":"Start pairing via REST or MCP. Prefer connect/kit for agents (QR + code in one response). Omit X-Tenant-Key for personal auto-workspace. Branch on body.status: pairing code | handoff_confirmation_required (live MFA) | registered_offline (use force or unlink). For re-pair from scratch pass force:true when the tenant owns the number.","headers":{"X-Tenant-Key":"optional tnt_\u003ctenant_key\u003e for SaaS / multi-number"},"name":"start_pairing","options":[{"rest_code":"POST /api/session/connect/code"},{"rest_kit":"POST /api/session/connect/kit"},{"mcp_kit":"whatsapp_connect_kit (requires allow_provision)"},{"mcp_code":"whatsapp_connect_code (requires allow_provision)"}],"status_branching":[{"action":"Show pairing_code; poll status","status":"pending_user_verification"},{"action":"Owner /connect approve in self-chat; poll handoff; check code_delivered","status":"handoff_confirmation_required"},{"action":"Do not wait for MFA. force:true if owner, or POST /api/session/disconnect unlink:true then connect","status":"registered_offline"},{"action":"Use existing mcp_/wbot_ token; secrets not re-exported","status":"already_connected"},{"action":"Transfer number or use the owning tenant key","error":"phone_owned_by_other_tenant"}],"step":3},{"description":"Poll until status=success. Save api_key (wbot_...) and mcp_connection if present.","mcp_alt":"whatsapp_connect_status","method":"GET","name":"poll_connect_status","path":"/api/session/connect/status","polling":{"interval_seconds":3,"max_wait_seconds":120,"success_status":"success","terminal_failures":["expired","not_found","pairing_timeout"]},"query":{"uuid":"\u003cuuid from step 3\u003e"},"response_fields":["api_key","mcp_connection","tenant_key","phone_number"],"step":4},{"description":"MCP is auto-enabled on success — copy mcp_connection.cursor_mcp_example into the agent host. Only call enable/rotate if you need allow_provision=true or a new token.","mcp_tools":["whatsapp_get_mcp_connection","whatsapp_enable_mcp","whatsapp_rotate_mcp_token"],"name":"configure_agent_mcp","optional":"POST /api/mcp/enable or whatsapp_enable_mcp with allow_provision:true for provisioning other phones","step":5},{"alt":"whatsapp_get_sync_status / resource whatsapp://sync","description":"After pairing, wait for history sync before bulk operations.","mcp_tool":"whatsapp_wait_for_sync","name":"wait_for_sync","note":"Phone may still show 'syncing' in UI after server progress hits 100% — normal for large accounts.","step":6},{"description":"Confirm session is ready.","expect":{"connection.logged_in":true,"healthy":true},"mcp_tool":"whatsapp_health_check","name":"verify_health","step":7},{"description":"Operate via MCP tools at POST /mcp. Use whatsapp_watch_notifications for event-driven loops.","endpoint":"https://zap.zaptdev.com/mcp","first_tools":["whatsapp_watch_notifications","whatsapp_get_chat","whatsapp_resolve_recipient","whatsapp_reply_to_message"],"name":"listen_and_manage","step":8},{"description":"After sync, the owner receives a self-chat link to the minimal settings portal for managing named per-agent MCP credentials (nicknames + toggles).","features":["Session health + reconnect/unlink","Named MCP accesses with per-credential toggles + send_mode (auto/approval/read_only) and custom_instructions. Pending approvals with context in PWA.","Live sessions shown per nickname","Revoke/rotate individual named credentials","Infra alerts (MCP, session, sync) + security (passkeys, devices)"],"name":"owner_settings","portal_url":"https://zap.zaptdev.com/portal","step":9,"trigger":"sync_completed → self-chat /portal/install link (or /pwa on demand).","user_flow":["Open link → add to home screen → enter pairing code → enable passkey (recommended)","Install as PWA to home screen","Create named MCP access (e.g. nickname + custom toggles)","Copy handoff/token for the specific agent"]}]}
