agentsclimarketplace

Whatsteam webhooks

Skill whatsteamhq/whatsapp-claude-skills/skills/whatsteam-webhooks

Agent Skills for WhatsTeam — automate WhatsApp Business (official API) by chatting with Claude, Cursor, Codex or any Agent Skills client. 23 skills + the WhatsTeam MCP.

Install
npx -y skills add whatsteamhq/whatsapp-claude-skills --skill whatsteam-webhooks

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Subscribe to real-time WhatsApp events from WhatsTeam — inbound messages, delivery and read receipts, status changes, campaign events, chat assignment changes, device state, and more. Use when the user wants live automation triggered by WhatsApp events (auto-replies, CRM sync, alerting, opt-out handling, multi-agent routing). Covers subscription via the REST API, payload shapes, HMAC signature verification, retries, and how to expose a public endpoint locally for development.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

9.2 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it

WhatsTeam Webhooks

Drive automations from real WhatsApp events instead of polling. Webhooks turn "send when X happens" requests into a couple of API calls plus a small HTTP handler.

When to use

  • The user says "trigger something when a message arrives", "notify me when a campaign finishes", "auto-reply", "sync to my CRM", "ping Slack on every reply".
  • Another skill (whatsteam-customer-support, whatsteam-campaigns opt-out, whatsteam-ecommerce order events) needs live event ingestion.
  • The user asks how to receive delivery / read receipts.

For ad-hoc polling ("show me the last 10 messages"), use whatsteam-inbox / whatsteam-messaging instead — webhooks are for production automation.

Prerequisites

  • whatsteam-setup complete.
  • A publicly reachable HTTPS endpoint that accepts POST requests with a JSON body. For local development, expose it via ngrok, cloudflared, bore.pub, or similar.
  • An idempotency strategy on your side — WhatsTeam retries failed deliveries (up to ~24h), so the same event can land twice.

Architecture

WhatsApp ──► WhatsTeam Cloud ──► [Webhook POST] ──► Your endpoint ──► Your logic
                                       │
                                       └── HMAC SHA-256 in X-WhatsTeam-Signature header

The MCP server does not expose subscribing to webhooks — webhook management is a REST-only flow. Call the REST API directly from a small script or curl.

Step 1 — Pick the events to receive

Common event types (full list at https://app.whats.team/docs/#tag/Webhooks):

EventFires when
message:in:newInbound message received
message:out:sentOutbound message sent successfully
message:out:deliveredDelivery receipt from WhatsApp
message:out:readRead receipt
message:out:failedSend failed (number invalid, blocked, quota, …)
chat:assignedChat assigned to a team member
chat:status:changedChat status changed (active/pending/resolved/archived)
device:status:changedDevice connection state changed
campaign:started / campaign:finishedCampaign lifecycle
contact:created / contact:updatedContact synced into WhatsTeam

Subscribe to the least you need — every event you don't filter is bandwidth and a retry-storm risk.

Verify the exact names. Confirm the precise event-name strings (and the signature header name/format used in Step 3) against the live Webhooks reference at https://app.whats.team/docs/#tag/Webhooks before hardcoding them — they can differ from the examples here.

Step 2 — Subscribe

curl -X POST https://api.whats.team/v1/webhooks \
  -H "Token: $WHATSTEAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM sync",
    "url": "https://hooks.example.com/whatsteam",
    "events": ["message:in:new", "message:out:read"],
    "active": true,
    "device": "DEVICE_ID_OR_OMIT_FOR_ACCOUNT_WIDE"
  }'

Response includes the webhook id and a secret. Store the secret — you need it to verify signatures.

To list, update, or delete webhooks: GET / PATCH / DELETE /v1/webhooks/{id}.

Step 3 — Verify the signature

Every delivery includes X-WhatsTeam-Signature: sha256=<hex>. Compute HMAC-SHA-256 over the raw body using the webhook's secret and compare.

Node.js example:

import crypto from 'node:crypto'

function verifyWhatsTeamWebhook (rawBody, signatureHeader, secret) {
  if (!signatureHeader?.startsWith('sha256=')) return false
  const expected = signatureHeader.slice('sha256='.length)
  const actual = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')
  // Constant-time compare
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(actual, 'hex')
  )
}

Always verify. An unverified endpoint is trivially attackable — anyone with the URL can spoof events.

Step 4 — Handle the payload

Typical inbound-message payload:

{
  "id": "evt_01HXXX",
  "event": "message:in:new",
  "createdAt": "2026-05-27T14:32:11.218Z",
  "device": "DEVICE_ID",
  "data": {
    "message": {
      "id": "msg_01HXXX",
      "chat": "[email protected]",
      "from": "34600111222",
      "to": "DEVICE_PHONE",
      "fromMe": false,
      "type": "text",
      "body": "Hi, I need help with order #1234",
      "timestamp": "2026-05-27T14:32:10.000Z"
    },
    "chat": {
      "id": "[email protected]",
      "contact": { "name": "Marta L.", "phone": "+34600111222" },
      "status": "active",
      "assignedTo": null
    }
  }
}

Respond with HTTP 2xx within 10 seconds. WhatsTeam considers anything else (including timeouts) a failure and will retry with exponential backoff up to ~24h.

Recipes

Recipe 1 — Auto-reply to first contact

on event: message:in:new
  if first contact (this chat WID not seen before in your store):
    send_whatsapp_message
      - device: event.device
      - action: "text"
      - chat: data.message.chat        # the WID, e.g. "[email protected]"
      - message: "Hi! Thanks for reaching out. An agent will reply within 1 hour."
    mark chat WID as seen in your store
  ack 200

Decide "first contact" from your own store (track which chat WIDs you've greeted), and dedupe on data.message.id so a webhook retry can't double-fire. Don't infer it from a per-chat message count — that field isn't a reliable first-contact signal.

Recipe 2 — Slack alert on every reply

on event: message:in:new
  body = data.message.body
  contact = data.chat.contact.name || data.message.from
  POST https://hooks.slack.com/...  body: {
    text: `📩 *${contact}*: ${body}`
  }
  ack 200

Recipe 3 — Opt-out handling (STOP keyword)

on event: message:in:new
  if data.message.body matches /^(STOP|UNSUBSCRIBE|BAJA)$/i:
    send_whatsapp_message
      - action: "agent"
      - chat: data.message.chat
      - agentId: <userId>
      - message: "You've been unsubscribed. Reply START to opt back in."
      - actions: [{ action: "labels:add", params: { labels: ["opted-out"] } }]
    add to suppression list in your DB
  ack 200

(Applying a label is an agent-action side effect on the send — manage_whatsapp_labels is CRUD only and can't attach a label to a chat.)

Recipe 4 — Sync delivery receipts to a CRM

on event in [message:out:delivered, message:out:read, message:out:failed]:
  PATCH /crm/messages/<message.reference> with status=event.type
  ack 200

message.reference is the custom tracking ID you set on the send (send_whatsapp_message's reference parameter), echoed back on the message; store it to bridge WhatsTeam IDs to your CRM IDs.

Recipe 5 — Local dev with ngrok

# Terminal 1
node webhook-handler.js   # listens on :3000

# Terminal 2
ngrok http 3000
# → https://abcd-12-34-56-78.ngrok-free.app

# Terminal 3 — subscribe
curl -X POST https://api.whats.team/v1/webhooks \
  -H "Token: $WHATSTEAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Local dev",
    "url": "https://abcd-12-34-56-78.ngrok-free.app/whatsteam",
    "events": ["message:in:new"]
  }'

Send yourself a WhatsApp message — the event should arrive in ngrok's inspector. Delete the webhook (DELETE /v1/webhooks/{id}) when done; ngrok URLs rotate.

Common pitfalls

  • Forgetting to verify signatures. Public URLs leak. Without HMAC verification, anyone can POST fake events and trigger your automations.
  • Returning 4xx/5xx for normal cases. If the event isn't relevant to you, still return 2004xx triggers retries for ~24h.
  • Reading the body twice. If you parse JSON before signature verification, you've already discarded the raw bytes you need. Capture the raw body first (express.raw(), bodyParser.raw()), verify, then parse.
  • Subscribing to everything. Each event you receive but don't handle is wasted bandwidth and potential retries. Subscribe narrowly.
  • No idempotency. Retries deliver the same event.id twice. Dedupe on event.id in your DB or use idempotent operations.
  • Localhost as the URL. WhatsTeam sends from the public internet — http://localhost:3000 will never reach you. Always tunnel for dev.

See also

  • whatsteam-messaging — for the actions your handler triggers (sending replies).
  • whatsteam-inbox — for the chat / label / assignment side effects.
  • whatsteam-customer-support — opinionated SLA + auto-reply playbook on top of webhooks.
  • whatsteam-campaigns — opt-out webhook pattern in detail.
  • Full event reference: https://app.whats.team/docs/#tag/Webhooks

Gives 0 of the 12 instructions most apis services skills give in ~2.3k tokens

Counted across 424 of the 426 authors here whose files we hold, read 2026-08-06

  • use plural nouns for resource namesin 41 of 424, across 32 files
  • use cursor-based pagination for large datasetsin 35 of 424, across 20 files
  • include rate limit headers in responsesin 25 of 424, across 13 files
  • Use kebab-case for multi-word resourcesin 23 of 424, across 13 files
  • version APIs in the URL pathin 19 of 424, across 9 files
  • use semantic HTTP status codesin 18 of 424, across 8 files
  • verify webhook signaturesin 18 of 424, across 11 files
  • use query parameters for filteringin 17 of 424, across 6 files
  • use async database operationsin 14 of 424, across 7 files
  • wrap successful responses in a data fieldin 13 of 424, across 3 files
  • prefix sorting parameters with a hyphen for descending orderin 13 of 424, across 3 files
  • set appropriate HTTP status codesin 13 of 424, across 6 files

Said here and by no other author read

  • expose a public HTTPS endpoint for POST requests
  • respond with HTTP 2xx within 10 seconds
  • implement idempotency using the event ID
  • dedupe messages using the message ID

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.