Webhooks

Overview

Webhooks allow Muchaw DEV to push real-time event notifications to your server. When a message is received, a delivery status changes, or a number is activated, Muchaw DEV sends an HTTP POST request to your configured webhook URLs.

This page covers your webhooks — the URLs Muchaw DEV calls on your server. This is separate from the internal Meta webhook that Muchaw DEV uses to receive events from Meta.

Available events

EventTriggered when
message.receivedAn inbound WhatsApp message arrives
message.sentA message is accepted by Meta’s servers
message.deliveredA message is delivered to the recipient’s device
message.readA message is read by the recipient
message.failedA message delivery fails
number.activatedA WhatsApp number finishes provisioning and is ready
number.disconnectedA number is disconnected
number.bannedA number is banned by Meta

Creating a webhook

$curl -X POST https://dev.muchau.com.br/api/webhooks \
> -H "Authorization: Bearer $MUCHAW_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "url": "https://your-server.com/webhooks/muchaw",
> "events": ["message.received", "number.activated"]
> }'

Response (save the secret — it is shown only once):

1{
2 "id": "wh_01j...",
3 "url": "https://your-server.com/webhooks/muchaw",
4 "secret": "whsec_...",
5 "events": ["message.received", "number.activated"],
6 "numberIds": [],
7 "active": true,
8 "createdAt": "2024-01-15T10:00:00.000Z"
9}

Filter by number

To only receive events for specific numbers, pass numberIds:

1{
2 "url": "https://your-server.com/webhooks/muchaw",
3 "events": ["message.received"],
4 "numberIds": ["clxyz123..."]
5}

Verifying webhook signatures

Every webhook request from Muchaw DEV includes an X-Muchaw-Signature header with an HMAC-SHA256 signature.

Always verify this signature before processing the event.

1import crypto from "node:crypto";
2
3function verifySignature(rawBody, signature, secret) {
4 const expected = crypto
5 .createHmac("sha256", secret)
6 .update(rawBody)
7 .digest("hex");
8 return crypto.timingSafeEqual(
9 Buffer.from(signature),
10 Buffer.from(`sha256=${expected}`)
11 );
12}
13
14// Express example
15app.post("/webhooks/muchaw", express.raw({ type: "application/json" }), (req, res) => {
16 const sig = req.headers["x-muchaw-signature"];
17 if (!verifySignature(req.body, sig, process.env.MUCHAW_WEBHOOK_SECRET)) {
18 return res.status(401).send("Invalid signature");
19 }
20 const event = JSON.parse(req.body.toString());
21 // process event...
22 res.sendStatus(200);
23});

Webhook payload structure

1{
2 "event": "message.received",
3 "timestamp": "2024-01-15T10:05:00.000Z",
4 "data": {
5 "id": "msg_01j...",
6 "numberId": "clxyz123...",
7 "direction": "INBOUND",
8 "fromNumber": "5511888880000",
9 "toNumber": "5511999990000",
10 "type": "text",
11 "body": { "body": "Hello!" },
12 "createdAt": "2024-01-15T10:05:00.000Z"
13 }
14}

Retry policy

If your server returns a non-2xx response or times out, Muchaw DEV retries with exponential backoff:

AttemptDelay
1st retry~30 seconds
2nd retry~5 minutes
3rd retry~30 minutes

After 3 failed attempts, the event is dropped. If a webhook accumulates 10 consecutive failures, it is automatically deactivated.

Rotating the secret

$curl -X POST https://dev.muchau.com.br/api/webhooks/<id>/rotate-secret \
> -H "Authorization: Bearer $MUCHAW_API_KEY"

Update the secret in your server configuration before the old one expires.