{"agent_handoff":{"api_handoff":{"behavior":"Returns status=session_handoff with mcp_connection instead of starting pairing. Also posts a notice in the user's self-chat.","response_fields":["status","handoff_method","mcp_connection","api_key","tenant_key","docs","self_chat_hint"],"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","MCP whatsapp_reconnect when already_connected"]},"description":"When WhatsApp is already paired, additional agents receive credentials without re-pairing.","self_chat_mfa":{"bot_replies":["MCP endpoint + token","API key","tenant key","cursor_mcp_example JSON","doc URLs"],"command":"/connect","cooldown_seconds":30,"how":"User opens self-chat (message yourself) and sends /connect from their phone.","mfa_proof":"Only the account owner can send live IsFromMe messages in self-chat — this verifies identity."},"status":"session_handoff"},"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://wamcp.up.railway.app/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_...","header":"Authorization","usage":"All MCP tool calls at POST /mcp with MCP-Protocol-Version: 2025-11-25"},"platform_admin":{"alt":"Authorization: Bearer \u003cADMIN_MASTER_TOKEN\u003e","format":"ADMIN_MASTER_TOKEN env value","header":"X-Admin-Token","usage":"Tenant provisioning, admin 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"}},"docs":{"agent_playbook":"https://wamcp.up.railway.app/docs/agent-playbook","agents_guide":"https://wamcp.up.railway.app/guides/agents","api_guide":"https://wamcp.up.railway.app/guides/api","connect_page":"https://wamcp.up.railway.app/connect","connect_prefilled":"https://wamcp.up.railway.app/connect?tenant=tnt_\u003ctenant_key\u003e","health":"https://wamcp.up.railway.app/health","landing":"https://wamcp.up.railway.app/","llms_full_txt":"https://wamcp.up.railway.app/llms-full.txt","llms_txt":"https://wamcp.up.railway.app/llms.txt","mcp_endpoint":"https://wamcp.up.railway.app/mcp","mcp_guide":"https://wamcp.up.railway.app/guides/mcp","openapi_json":"https://wamcp.up.railway.app/docs/openapi.json","openapi_yaml":"https://wamcp.up.railway.app/docs/openapi.yaml","owner_settings":"https://wamcp.up.railway.app/portal","portal_guide":"https://wamcp.up.railway.app/guides/portal","skills_guide":"https://wamcp.up.railway.app/guides/skills","swagger_ui":"https://wamcp.up.railway.app/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":"Ask for phone_number + tenant key, then POST /api/session/connect/code or /api/session/connect/kit, or send the user to /connect.","prerequisites":["tenant key (tnt_...) from platform admin POST /api/admin/tenants","WhatsApp phone number with country code, digits only","WhatsApp app open on the user's phone"],"recommended_url":"https://wamcp.up.railway.app/connect","share_with_clients":"Send /connect?tenant=tnt_... so the tenant key is pre-filled."},"important_notes":["Already paired? Send /connect to yourself in WhatsApp for instant MCP credentials (MFA via active session).","REST/MCP connect endpoints return session_handoff (not pairing) when the WhatsApp session is already live.","MCP is auto-enabled when connect status=success; mcp_connection is returned in the poll response.","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 before send when recipient is phone digits only (self-chat needs JID resolution).","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,"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":["Use full JID or call whatsapp_resolve_recipient before any send/reply/schedule.","Self-chat: pass your own phone digits — resolver maps to @s.whatsapp.net.","Groups: @g.us JIDs come from whatsapp_list_groups."],"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)"]},"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"],"tool_count":37,"tools":[{"category":"messaging","mcp_description":"Send a text or media message through the connected WhatsApp account. When to use: Outbound messages to a user or group. Call whatsapp_resolve_recipient first if you only have digits. Parameters: recipient (required) — Phone digits or full JID e.g. 5511999999999; 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; Requires: active WhatsApp session (connected + logged_in), allow_send=true.","name":"whatsapp_send_message","parameters":[{"description":"Phone digits or full JID","example":"5511999999999","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","name":"media_type","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 messages to a user or group. Call whatsapp_resolve_recipient first if you only have digits."},{"category":"messaging","mcp_description":"Send a quoted reply to an existing stored message. When to use: Respond in-thread after whatsapp_list_messages, whatsapp_search_messages, or a message_in notification. Parameters: message_id (required) — Stored message_id to quote; 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","name":"message_id","required":true,"type":"string"},{"description":"Reply text","name":"content","required":true,"type":"string"}],"requires_allow_send":true,"requires_session":true,"summary":"Send a quoted reply to an existing stored message.","when_to_use":"Respond in-thread after whatsapp_list_messages, whatsapp_search_messages, or a message_in notification."},{"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; 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"}],"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. 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; 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"}],"requires_allow_read":true,"requires_session":true,"summary":"Per-chat aggregate statistics (counts, my-share, first/last, media) without fetching messages.","when_to_use":"Prioritize chats or size a window before pulling content — one cheap call instead of paging history."},{"category":"messaging","mcp_description":"Search stored messages by text and optional filters. When to use: Find messages by keyword, date, or sync_type across chats. Parameters: query — Case-insensitive content search; chat_jid — Optional chat filter; since — ISO timestamp lower bound; until — ISO timestamp 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":"Case-insensitive content search","name":"query","type":"string"},{"description":"Optional chat filter","name":"chat_jid","type":"string"},{"description":"ISO timestamp lower bound","name":"since","type":"string"},{"description":"ISO timestamp 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 stored messages by text and optional filters.","when_to_use":"Find messages by keyword, date, or sync_type across chats."},{"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 or phone; Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_get_chat","parameters":[{"description":"Chat JID or phone","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":"Return stored media_url or download bytes from WhatsApp for a message. When to use: When a message has media and you need the URL or base64 content. Parameters: message_id (required) — Stored message_id; include_base64 — Embed base64 if under max_bytes; max_bytes — Base64 size cap (default 5MB); 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":"Embed base64 if under max_bytes","name":"include_base64","type":"boolean"},{"description":"Base64 size cap (default 5MB)","name":"max_bytes","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"Return stored media_url or download bytes from WhatsApp for a message.","when_to_use":"When a message has media and you need the URL or base64 content."},{"category":"messaging","mcp_description":"List synced chats ordered by recent activity. When to use: Discover conversations and unread counts. Parameters: limit — Max chats (default 50); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_chats","parameters":[{"description":"Max chats (default 50)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"List synced chats ordered by recent activity.","when_to_use":"Discover conversations and unread counts."},{"category":"messaging","mcp_description":"List synced contacts with display and push names. When to use: Resolve human-readable names or find JIDs. Parameters: limit — Max contacts (default 50); Requires: active WhatsApp session (connected + logged_in), allow_read=true.","name":"whatsapp_list_contacts","parameters":[{"description":"Max contacts (default 50)","name":"limit","type":"number"}],"requires_allow_read":true,"requires_session":true,"summary":"List synced contacts with display and push names.","when_to_use":"Resolve human-readable names or find JIDs."},{"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 Supabase (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 Supabase (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":"Normalize phone digits or partial id into a WhatsApp JID. When to use: Before send when recipient format is uncertain; handles self-chat. Parameters: recipient (required) — Phone or JID e.g. 5511999999999;","name":"whatsapp_resolve_recipient","parameters":[{"description":"Phone or JID","example":"5511999999999","name":"recipient","required":true,"type":"string"}],"requires_session":false,"summary":"Normalize phone digits or partial id into a WhatsApp JID.","when_to_use":"Before send when recipient format is uncertain; handles self-chat."},{"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 Supabase 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 Supabase 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; 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"}],"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":"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":"Gracefully disconnect and deactivate the WhatsApp session. When to use: User requests logout; blocked while sync_protected. Parameters: phone_number — Defaults to bound phone;","name":"whatsapp_disconnect","parameters":[{"description":"Defaults to bound phone","name":"phone_number","type":"string"}],"requires_session":false,"summary":"Gracefully disconnect and deactivate the WhatsApp session.","when_to_use":"User requests logout; blocked while sync_protected."},{"category":"lifecycle","mcp_description":"Start 8-digit pairing for a new or logged-out phone. When to use: Provision another phone when allow_provision=true. Parameters: phone_number (required) — Digits only e.g. 5511999999999; wait_seconds — Wait for code generation; 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"}],"requires_allow_provision":true,"requires_session":false,"summary":"Start 8-digit pairing for a new or logged-out phone.","when_to_use":"Provision another phone when allow_provision=true."},{"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; 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"}],"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; 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"}],"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."}]},"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 33+ tools, and direct account owners to the minimal settings portal for access control.","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"],"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://wamcp.up.railway.app/guides/skills#custom-functions","name":"custom_function_recipe","title":"Building higher-level custom functions"}],"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.3.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 both are known.","description":"Collect WhatsApp number (digits + country code) and tenant key (tnt_...).","human_alt":"Send user to /connect?tenant=tnt_...","name":"ask_user_phone","step":2},{"description":"Start pairing via REST or MCP. Prefer connect/kit for agents (QR + code in one response).","headers":{"X-Tenant-Key":"tnt_\u003ctenant_key\u003e"},"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)"}],"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://wamcp.up.railway.app/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://wamcp.up.railway.app/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"]}]}
