gokapso/automate-whatsapp
Build WhatsApp automations with Kapso workflows: configure WhatsApp triggers, edit workflow graphs, manage executions, deploy functions, search workflow Logs, and debug automation behavior. Use when automating WhatsApp conversations and event handling.
npx skills add https://github.com/gokapso/agent-skills --skill automate-whatsapp
Use this skill to build and run WhatsApp automations: workflow CRUD, graph edits, WhatsApp and Project Event triggers, Project Event emissions, executions, function management, webhook tools, and MCP tools.
Preferred path:
kapso login)kapso link, kapso pull, kapso build, and kapso push@kapso/workflows and export a Workflow instance from workflow.js or workflow.tsFallback path:
Env vars:
KAPSO_API_BASE_URL (host only, no /platform/v1)KAPSO_API_KEYUse this path first when the user is working in, or can create, a local repo.
npm install -g @kapso/cli
npm install --save-dev @kapso/workflows
kapso login
kapso link --project <project-id>
kapso pull
Edit workflows/<workflow-slug>/workflow.js or workflow.ts with @kapso/workflows:
import { START, Workflow } from "@kapso/workflows";
const workflow = new Workflow("inbound-support", {
name: "Inbound Support",
status: "draft",
});
workflow.addTrigger({
type: "inbound_message",
phoneNumberId: "<phone-number-id>",
});
workflow.addNode(START, {
position: { x: 100, y: 100 },
});
workflow.addNode("reply", {
type: "send_text",
message: "Thanks for reaching out.",
});
workflow.addEdge(START, "reply");
export default workflow;
Build and push:
kapso build
kapso push --dry-run
kapso push workflow <workflow-slug>
Use kapso push to push every local function and workflow. See references/local-workflow-source.md for repo layout, source-file behavior, and JSON-only editing.
Preferred path:
kapso statuskapso whatsapp numbers list --output jsonkapso whatsapp numbers resolve --phone-number "<display-number>" --output jsonFallback path:
node scripts/list-whatsapp-phone-numbers.jsPrefer local source sync for workflow edits. Use these scripts as a fallback for debugging, direct graph inspection, or API-only environments.
node scripts/get-graph.js <workflow_id> (note the lock_version)node scripts/validate-graph.js --definition-file <path>node scripts/update-graph.js <workflow_id> --expected-lock-version <n> --definition-file <path>For small edits, use edit-graph.js with --old-file and --new-file instead.
If you get a lock_version conflict: re-fetch, re-apply changes, retry with new lock_version.
node scripts/list-triggers.js <workflow_id>node scripts/create-trigger.js <workflow_id> --trigger-type <type> --phone-number-id <id>node scripts/update-trigger.js --trigger-id <id> --active true|falsenode scripts/delete-trigger.js --trigger-id <id>For inbound_message triggers, prefer kapso whatsapp numbers resolve --phone-number "<display-number>" --output json to get the exact phone_number_id. Fall back to node scripts/list-whatsapp-phone-numbers.js when the CLI is unavailable.
For project_event triggers, register or update the Project Event definition first when the user is defining an event type/schema:
node scripts/project-event-definitions.js create \
--name conversation.csat_scored \
--description "Customer satisfaction score for a conversation" \
--property-schema '{"score":{"type":"number"},"reason":{"type":"string"}}'
Then create the trigger:
node scripts/create-trigger.js <workflow_id> \
--trigger-type project_event \
--triggerable-attributes '{"event_name":"conversation.csat_scored","property_key":"score","operator":"gte","property_value":4}'
Definitions are metadata only. Do not emit a sample event just to register a name unless the user explicitly accepts that side effect.
Use definitions for event names, descriptions, and flat scalar property schemas. Emitted events are separate records created by POST /platform/v1/events, emit_event nodes, Function node project_events, or Agent node emit_event.
node scripts/project-event-definitions.js listnode scripts/project-event-definitions.js create --name <event.name> [--description <text>] [--property-schema <json>]node scripts/project-event-definitions.js update --definition-id <id> [--name <event.name>] [--description <text>] [--property-schema <json>]Use this checklist when a workflow needs to remember or react to durable business facts:
project_event triggers when a workflow should react to an emitted event.emit_event node for deterministic workflow-step emission.project_events when emission depends on function code output.emit_event only when the agent should decide whether/when to record the fact. Enable the emit_event default tool and configure allowed event definitions before relying on it.Project-event-triggered workflows are observers and cannot emit Project Events. Do not add event emission to a workflow that starts from a Project Event trigger.
kapso logs search --query "<execution-id>" --source flow_event --filter flow_execution_id=<execution-id> --period 7d --limit 20 --output jsonnode scripts/list-executions.js <workflow_id>node scripts/get-execution.js <execution-id>node scripts/get-context-value.js <execution-id> --variable-path vars.foonode scripts/list-execution-events.js <execution-id>node scripts/create-function.js --name <name> --code-file <path> [--public-endpoint true]node scripts/deploy-function.js --function-id <id>node scripts/get-function.js --function-id <id>Use --public-endpoint true when the function should be callable without X-API-Key via the Kapso-hosted invoke URL. This is only supported for Cloudflare functions.
New functions default to invoke_response_mode=passthrough, which returns the function body directly on successful invoke. Legacy wrapped functions can be migrated later with update-function.js.
Use this when the agent needs a remote ephemeral workspace to inspect or modify repository files during a workflow run.
references/agent-remote-sandbox.md for the execution model and field rulesnode scripts/list-provider-models.jsassets/agent-remote-sandbox-github-repo-example.json as a starting point, or edit the agent node under data.configsandbox_enabled: truesandbox_network_mode to allow_all or allow_listallow_list, add extra outbound hosts in sandbox_allowed_outbound_hostsflow_agent_resources with:resource_type: "github_repository"repo_urlbranchpat/workspace/repos/<repo-slug> before making changesNotes:
sandbox_enabled controls whether the remote workspace and sandbox tools are availabletree/... URL/workspace/repos/<repo-slug> inside the remote sandboxreferences/agent-remote-sandbox.md and references/node-types.md for the exact shapeid = start{node_type}_{timestamp_ms} for new node IDsnext edgeconditions[].labelsource/target/label (not from/to)For full schema details, see references/graph-contract.md.
async function handler(request, env) {
// Parse input
const body = await request.json();
// Use env.KV and secrets as needed
return new Response(JSON.stringify({ result: "ok" }));
}
export, export default, or arrow functionsResponse objectAlways use this structure:
vars - user-defined variablessystem - system variablescontext - channel datametadata - request metadata| Script | Purpose |
|--------|---------|
| list-workflows.js | List workflows (metadata only) |
| get-workflow.js | Get workflow metadata |
| create-workflow.js | Create a workflow |
| update-workflow-settings.js | Update workflow settings |
| Script | Purpose |
|--------|---------|
| get-graph.js | Get workflow graph + lock_version |
| edit-graph.js | Patch graph via string replacement |
| update-graph.js | Replace entire graph |
| validate-graph.js | Validate graph structure locally |
| Script | Purpose |
|--------|---------|
| list-triggers.js | List triggers for a workflow |
| create-trigger.js | Create a trigger |
| update-trigger.js | Enable/disable a trigger |
| delete-trigger.js | Delete a trigger |
| list-whatsapp-phone-numbers.js | List phone numbers for trigger setup |
| Script | Purpose |
|--------|---------|
| project-event-definitions.js | List, create, or update Project Event definitions |
| Script | Purpose |
|--------|---------|
| list-executions.js | List executions |
| get-execution.js | Get execution details |
| get-context-value.js | Read value from execution context |
| update-execution-status.js | Force execution state |
| resume-execution.js | Resume waiting execution |
| list-execution-events.js | List execution events |
| Script | Purpose |
|--------|---------|
| list-functions.js | List project functions |
| get-function.js | Get function details + code |
| create-function.js | Create a function, optionally with a public invoke endpoint |
| update-function.js | Update function code, public endpoint setting, or migrate a legacy wrapped function to passthrough |
| deploy-function.js | Deploy function to runtime |
| invoke-function.js | Invoke function with payload |
| list-function-invocations.js | List function invocations |
| Script | Purpose |
|--------|---------|
| openapi-explore.mjs | Explore OpenAPI (search/op/schema/where) |
Install deps (once):
npm i
Examples:
node scripts/openapi-explore.mjs --spec workflows search "variables"
node scripts/openapi-explore.mjs --spec workflows op getWorkflowVariables
--definition-file, --code-file)observe-whatsapp for cross-source Logs search when debugging a workflow alongside API calls, Meta events, or webhook deliveries.variables-set.js, variables-delete.js) is blocked - Platform API doesn't support itRead before editing:
@kapso/workflowsOther references:
| File | Description |
|------|-------------|
| workflow-linear.json | Minimal linear workflow |
| workflow-decision.json | Minimal branching workflow |
| workflow-agent-simple.json | Minimal agent workflow |
| workflow-customer-support-intake-agent.json | Customer support intake |
| workflow-interactive-buttons-decide-function.json | Interactive buttons + decide (function) |
| workflow-interactive-buttons-decide-ai.json | Interactive buttons + decide (AI) |
| workflow-api-template-wait-agent.json | API trigger + template + agent |
| function-decide-route-interactive-buttons.json | Function for button routing |
| agent-remote-sandbox-github-repo-example.json | Agent node with remote sandbox + GitHub repo resource |
integrate-whatsapp - Onboarding, webhooks, messaging, templates, flowsobserve-whatsapp - Debugging, logs, health checks<!-- FILEMAP:BEGIN -->
[automate-whatsapp file map]|root: .
|.:{package.json,SKILL.md}
|assets:{agent-remote-sandbox-github-repo-example.json,function-decide-route-interactive-buttons.json,functions-example.json,workflow-agent-simple.json,workflow-api-template-wait-agent.json,workflow-customer-support-intake-agent.json,workflow-decision.json,workflow-interactive-buttons-decide-ai.json,workflow-interactive-buttons-decide-function.json,workflow-linear.json}
|references:{agent-remote-sandbox.md,execution-context.md,function-contracts.md,functions-payloads.md,functions-reference.md,graph-contract.md,local-workflow-source.md,node-types.md,triggers.md,workflow-overview.md,workflow-reference.md}
|scripts:{create-function.js,create-trigger.js,create-workflow.js,delete-trigger.js,deploy-function.js,edit-graph.js,get-context-value.js,get-execution-event.js,get-execution.js,get-function.js,get-graph.js,get-workflow.js,invoke-function.js,list-execution-events.js,list-executions.js,list-function-invocations.js,list-functions.js,list-provider-models.js,list-triggers.js,list-whatsapp-phone-numbers.js,list-workflows.js,openapi-explore.mjs,project-event-definitions.js,resume-execution.js,update-execution-status.js,update-function.js,update-graph.js,update-trigger.js,update-workflow-settings.js,validate-graph.js,variables-delete.js,variables-list.js,variables-set.js}
|scripts/lib/functions:{args.js,kapso-api.js}
|scripts/lib/workflows:{args.js,kapso-api.js,result.js}
<!-- FILEMAP:END -->
Take gokapso/automate-whatsapp from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm.
Without those the skill loads but fails at the first command.