Skip to main content
ClaudeWave
Skill522 estrellas del repoactualizado today

kapso-whatsapp

The kapso-whatsapp skill provides comprehensive REST recipes and reference documentation for advanced WhatsApp interactions through the Kapso platform beyond the basic MCP send tools. Use it for template messages, media delivery, reactions, typing indicators, mark-as-read, webhook signature verification, contact resolution to swarm users, and conversation history retrieval when standard MCP tools cannot handle the task.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/desplega-ai/agent-swarm /tmp/kapso-whatsapp && cp -r /tmp/kapso-whatsapp/templates/skills/kapso-whatsapp ~/.claude/skills/kapso-whatsapp
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Kapso WhatsApp

Kapso (https://kapso.ai) is a WhatsApp platform vendor that fronts the Meta Cloud API. A swarm provisions one or more WhatsApp phone numbers and wires each one to either a native inbound handler (PR #560) or a workflow that dispatches a task per inbound message.

## When to use MCP tools vs this skill's REST recipes

PR #560 ships **thin MCP-tool wrappers for the common case only**:

| Tool | Use for |
|---|---|
| `send-whatsapp-message` | Free-form text within the 24h session window. |
| `reply-whatsapp-message` | Same as above but quote-threads to an inbound WAMID. |
| `register-kapso-number` / `unregister-kapso-number` | Provisioning a phone number's webhook + KV mapping. |

**For ANYTHING else, drop to the REST recipes in this skill** — these are the canonical reference, and the MCP tools deliberately do NOT duplicate them:

- **Template messages** (outside 24h window) → §"Send a template" below.
- **Media** (image / document / audio / video, including wide-image padding and PTT voice notes) → §"Sending media".
- **Reactions** (👀 / ✅ / clear) → §"Send a reaction".
- **Typing indicator + mark-as-read** → §"Mark as read + typing indicator".
- **Signature verify (manual)** → §"Webhook signature verification".
- **Contact resolution → swarm user** → §"Resolve a contact to a swarm user".
- **Conversation history / message detail / templates list** → §"Read conversation context".

If the MCP-tool send returns a 24h-window error (`sessionWindowExpired: true`), fall through to the template path in §"Send a template" — this is exactly what the tool's structured-error points at.

## Setup

Swarm config keys (resolve with `get-config key:<NAME> includeSecrets:true` — Lead-only for secrets; workers should ask Lead if they need a value injected):

| Key | Value |
|---|---|
| `KAPSO_API_BASE_URL` | `https://api.kapso.ai` (host only, no `/platform/v1`) |
| `KAPSO_API_KEY` | API key (`X-API-Key` header) |
| `KAPSO_PHONE_NUMBER_ID` | The swarm's provisioned number's Meta phone-number ID |
| `KAPSO_WEBHOOK_HMAC_SECRET` | Shared HMAC secret. Kapso signs every webhook request with `X-Webhook-Signature: <hex>` |

The curl recipes below assume `$KAPSO_API_KEY`, `$KAPSO_API_BASE_URL`, and `$KAPSO_PHONE_NUMBER_ID` are resolved into your shell, e.g.:

```bash
API_BASE=$(get-config KAPSO_API_BASE_URL)        # https://api.kapso.ai
API_KEY=$(get-config KAPSO_API_KEY)
PHONE_NUMBER_ID=$(get-config KAPSO_PHONE_NUMBER_ID)
```

The Kapso CLI is NOT installed in worker containers. Use direct HTTP or clone the `gokapso/agent-skills` repo for fallback scripts.

```bash
git clone --depth=1 https://github.com/gokapso/agent-skills /tmp/kapso-skills
cd /tmp/kapso-skills/skills/integrate-whatsapp && npm i  # or observe-whatsapp / automate-whatsapp
```

The Meta Cloud API is proxied at `$KAPSO_API_BASE_URL/meta/whatsapp/v24.0/...` (auth: `X-API-Key`). Kapso's own platform endpoints live at `$KAPSO_API_BASE_URL/platform/v1/...`.

## Inbound webhook payload (v2)

When inbound events are routed through a workflow, the workflow's webhook trigger receives `whatsapp.message.*` and `whatsapp.conversation.*` events at `POST https://<your-swarm-host>/api/webhooks/<workflow-id>`.

Shape (top-level keys):

```json
{
  "message": {
    "id": "wamid.HBgL...",            // Meta message id (WAMID)
    "from": "15551234567",            // E.164 without + (sender)
    "from_user_id": "XX.00000...",    // Meta-internal user id
    "timestamp": "1700000000",        // unix seconds (string)
    "type": "text",                   // text | image | audio | video | document | sticker | location | contacts | reaction | ...
    "text": { "body": "hi" },         // only for type=text
    "context": null,                  // present when the user quote-replied another message
    "kapso": {
      "direction": "inbound|outbound",
      "status": "received|delivered|read|sent|failed",
      "processing_status": "pending|completed",
      "origin": "cloud_api",
      "has_media": false,
      "content": "hi"                 // text representation (caption / filename / body)
    }
  },
  "conversation": {
    "id": "<conversation-uuid>",
    "phone_number": "15551234567",
    "phone_number_id": "<PHONE_NUMBER_ID>",
    "contact_name": "Jane Doe",
    "status": "active",
    "last_active_at": "...",
    "created_at": "...",
    "kapso": {
      "messages_count": 10,
      "last_message_id": "wamid...",
      "last_message_text": "hi",
      "last_inbound_at": "...",
      "last_outbound_at": "..."
    }
  },
  "is_new_conversation": false,
  "phone_number_id": "<PHONE_NUMBER_ID>"
}
```

**ALWAYS filter on `message.kapso.direction == "inbound"`** — Kapso fires the webhook for the swarm's own outbound sends, deliveries, reads, and failures too. Only inbound events from real humans warrant a task.

Test payloads include `"test": true` and `wamid.TEST_...` ids — handle gracefully (treat as a real inbound but mark it test in your reply; do not send a real WhatsApp reply to test payloads).

## Non-text message types

`message.type` can be `text`, `image`, `audio`, `video`, `document`, `sticker`, `location`, `contacts`, `reaction`, `button`, or `interactive`. Non-text inbound messages carry a type-specific object:

| type | object | key fields |
|---|---|---|
| `image` | `message.image` | `id` (media id), `mime_type`, `sha256`, `caption?` |
| `audio` | `message.audio` | `id`, `mime_type`, `voice` (true = voice note), `sha256` |
| `video` | `message.video` | `id`, `mime_type`, `sha256`, `caption?` |
| `document` | `message.document` | `id`, `mime_type`, `filename`, `sha256`, `caption?` |
| `sticker` | `message.sticker` | `id`, `mime_type`, `animated`, `sha256` |
| `location` | `message.location` | `latitude`, `longitude`, `name?`, `address?` |
| `contacts` | `message.contacts[]` | `name`, `phones[]`, `emails[]`, ... |
| `reaction` | `message.reaction` | `message_id` (wamid being reacted to), `emoji` |

`message.kapso.has_media` is `true` for i