
Automate Whatsapp
Configure Kapso agent flows and nodes to send, route, and automate WhatsApp conversations with sandboxes, webhooks, and button-reply branching.
Install
npx skills add https://github.com/gokapso/agent-skills --skill automate-whatsappWhat is this skill?
- Documents agent_node_config with system prompt, provider model, sandbox network allow_list, and GitHub repo mounts.
- Includes decide-route helper that branches on vars.button_choice from interactive WhatsApp button replies.
- Shows flow_agent_resources (github_repository), webhooks, function tools, and MCP server slots in one config shape.
- Oriented toward inspect-repo-then-change agent loops inside Kapso sandboxes.
Adoption & trust: 1.7k installs on skills.sh; 128 GitHub stars; 2/3 security scanners passed (skills.sh audits).
Recommended Skills
Agent Browservercel-labs/agent-browser
Lark Imlarksuite/cli
Lark Calendarlarksuite/cli
Lark Sheetslarksuite/cli
Lark Vclarksuite/cli
Lark Contactlarksuite/cli
Journey fit
Common Questions / FAQ
Is Automate Whatsapp safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Automate Whatsapp
{ "agent_node_config": { "node_type": "agent", "config": { "system_prompt": "Inspect the mounted repository before answering. Read the README and the most relevant files under /workspace/repos/acme-app, summarize the architecture, and then propose the smallest safe change.", "provider_model_id": "uuid", "max_iterations": 20, "max_tokens": 8192, "temperature": 0.2, "sandbox_enabled": true, "sandbox_network_mode": "allow_list", "sandbox_allowed_outbound_hosts": [ "api.example.com" ], "flow_agent_resources": [ { "resource_type": "github_repository", "repo_url": "https://github.com/acme/acme-app", "branch": "main", "pat": "github_pat_replace_me" } ], "flow_agent_webhooks": [], "flow_agent_function_tools": [], "flow_agent_mcp_servers": [] } } } { "name": "decide-route-interactive-buttons", "description": "Decide node helper: route to the next edge based on vars.button_choice from an interactive button reply.", "code": "async function handler(request, env) {\n const payload = await request.json();\n\n const executionContext = payload && payload.execution_context ? payload.execution_context : {};\n const vars = executionContext && executionContext.vars ? executionContext.vars : {};\n const availableEdges = Array.isArray(payload && payload.available_edges) ? payload.available_edges : [];\n\n const raw = vars.button_choice ?? vars.last_user_input;\n\n function asString(value) {\n if (typeof value === 'string') return value;\n if (typeof value === 'number') return String(value);\n if (value && typeof value === 'object') {\n try {\n return JSON.stringify(value);\n } catch {\n return null;\n }\n }\n return null;\n }\n\n function extractChoice(value) {\n if (!value) return null;\n\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed.length === 0) return null;\n return trimmed;\n }\n\n if (value && typeof value === 'object') {\n const v = value;\n const direct = v.button_id || v.buttonId || v.list_id || v.listId || v.id || v.choice || v.value;\n if (typeof direct === 'string' && direct.trim().length > 0) return direct.trim();\n\n // Common nested shapes.\n const nested =\n (v.interactive && (v.interactive.button_reply || v.interactive.list_reply)) ||\n v.button_reply ||\n v.list_reply;\n\n if (nested && typeof nested === 'object') {\n const nestedId = nested.id || nested.button_id || nested.list_id;\n if (typeof nestedId === 'string' && nestedId.trim().length > 0) return nestedId.trim();\n }\n }\n\n return null;\n }\n\n const extracted = extractChoice(raw);\n const normalized = extracted ? extracted.toLowerCase().trim() : null;\n\n // Map common variants to canonical edge labels.\n const mapped = normalized === 'sale' ? 'sales' : normalized;\n\n if (mapped && availableEdges.includes(mapped)) {\n return new Response(JSON.stringify({ next_edge: mapped }), {\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n const fallback = availableEdges[0] || 'next';\n const reason = {\n extracted: extracted,\n raw_preview: asString(raw),\n available_edges: availableEdges\n };\n\n return new Response(JSON.stringify({ next_edge: fallback, vars: { decision_reason: reason } }), {\n headers: { 'Content-Type': 'application/json' }\n });\n}\n" } { "name": "webhook-handler", "description": "Store webhook payloads", "code": "async function handler(request, env) {\n const payload = await request.json();\n const key = `webhook:${Date.now()}`;\n await env.KV.put(key, JSON.stringify({ event: payload.event, data: payload.data }));\n return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' } });\n}" } { "nodes": [