Skip to content

Webhooks

Configure webhooks to receive inbound messages and delivery updates

View .md

Webhooks allow your application to receive real-time notifications when events occur, such as incoming messages, delivery status changes, or partner invitation updates.

Zavu supports two types of webhooks:

  • Sender Webhooks: Receive events for a specific sender (messages, conversations)
  • Project Webhooks: Receive project-level events (partner invitations)

Project Webhooks

Project webhooks are ideal for receiving notifications about project-level events, such as when a partner invitation is completed. This is particularly useful if you're using the Partner Invitations API to onboard clients.

A project webhook receives events for its own project only. If you onboard each client in a sub-account, configure the webhook with that sub-account's API key: the parent project's webhook does not receive events from its sub-accounts.

Via Dashboard

  1. Go to Dashboard and select your project
  2. Navigate to Webhooks in the sidebar
  3. In the "Project Webhook" section, click Configure
  4. Enter your webhook URL and select the events you want to receive
  5. Save - you'll receive a signing secret

Via API

Configure a project webhook using the invitations API:

cURL
curl -X POST https://api.zavu.dev/v1/invitations/webhook \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.com/webhooks/zavu", "events": ["invitation.status_changed"] }'

Response:

JSON
{ "webhook": { "url": "https://your-app.com/webhooks/zavu", "events": ["invitation.status_changed"], "secret": "whsec_abc123def456...", "active": true } }

Managing Project Webhooks

Get current configuration:

cURL
curl https://api.zavu.dev/v1/invitations/webhook \ -H "Authorization: Bearer zv_live_xxx"

Update webhook: only works on a webhook created with POST first. Updating one that does not exist returns 400.

cURL
curl -X PATCH https://api.zavu.dev/v1/invitations/webhook \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://new-url.com/webhooks", "active": true }'

Remove webhook:

cURL
curl -X DELETE https://api.zavu.dev/v1/invitations/webhook \ -H "Authorization: Bearer zv_live_xxx"

Project Webhook Events

EventDescription
invitation.status_changedA partner invitation status changed (in_progress, completed, failed, cancelled, or back to pending on resend). See the event reference

Project Webhook Payload

JSON
{ "id": "evt_1234567890_abc123", "type": "invitation.status_changed", "timestamp": 1705312200000, "projectId": "prj_xyz789", "data": { "invitationId": "inv_abc123", "clientName": "John Doe", "clientEmail": "john@example.com", "connectionType": "whatsapp_waba", "previousStatus": "in_progress", "currentStatus": "completed", "senderId": "snd_new123", "wabaAccountId": "waba_123456", "connectedAccount": { "channel": "whatsapp", "id": "109876543210", "name": "John Doe Store" } } }

Sender Webhooks

Each sender can have one webhook configured. Webhooks are managed as part of the sender resource.

Via Dashboard

  1. Go to Dashboard and select your project
  2. Navigate to your Sender's settings
  3. Click Add Webhook
  4. Enter your webhook URL and select the events you want to receive
  5. Save - you'll receive a signing secret

Via API

Configure a webhook when creating a new sender:

cURL
curl -X POST https://api.zavu.dev/v1/senders \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "My Sender", "phoneNumber": "+15551234567", "webhookUrl": "https://your-app.com/webhooks/zavu", "webhookEvents": ["message.inbound", "message.delivered", "message.failed"] }'

Response:

JSON
{ "id": "snd_abc123", "name": "My Sender", "phoneNumber": "+15551234567", "isDefault": false, "webhook": { "url": "https://your-app.com/webhooks/zavu", "events": ["message.inbound", "message.delivered", "message.failed"], "secret": "whsec_abc123def456...", "active": true }, "createdAt": "2024-01-15T10:30:00.000Z" }

Available Events

Inbound (Receiving Messages)

EventDescription
conversation.newFirst message from a new contact (new conversation started)
message.inboundA customer sent you a message

Outbound (Delivery Tracking)

EventDescription
message.queuedYour message was queued for delivery
message.sentYour message was sent to the carrier
message.deliveredYour message was delivered to the recipient
message.readYour message was read by the recipient (WhatsApp only)
message.failedMessage delivery failed

Templates

EventDescription
template.status_changedA WhatsApp template status changed (draft, pending, approved, rejected)

Partner Invitations (Project Webhook)

EventDescription
invitation.status_changedA partner invitation status changed (in_progress, completed, failed, cancelled, or back to pending on resend). See the event reference

Webhook Payload

All webhook payloads follow the same structure:

JSON
{ "id": "evt_1234567890_abc123", "type": "message.inbound", "timestamp": 1705312200000, "senderId": "snd_abc123", "projectId": "prj_xyz789", "data": { // Event-specific data } }
FieldDescription
idUnique event identifier
typeEvent type (e.g., message.inbound)
timestampUnix timestamp (ms) when Zavu dispatched the webhook — not when the message was sent. For inbound messages, compare against data.providerTimestamp (the channel's original receive time) to detect delayed deliveries.
senderIdThe Sender that received this event
projectIdYour project ID
dataEvent-specific payload

Handling Webhooks

Your webhook endpoint must:

  1. Respond with 2xx status within 30 seconds
  2. Verify the signature to ensure the request is from Zavu
  3. Process asynchronously for long-running tasks
import express from "express"; const app = express(); app.post("/webhooks/zavu", express.json(), (req, res) => { // 1. Verify signature (see Security guide) const signature = req.headers["x-zavu-signature"]; if (!verifySignature(signature, req.body, process.env.WEBHOOK_SECRET)) { return res.status(401).send("Invalid signature"); } // 2. Acknowledge receipt immediately res.status(200).send("OK"); // 3. Process the event asynchronously const event = req.body; processEvent(event).catch(console.error); }); async function processEvent(event) { switch (event.type) { case "message.inbound": await handleInboundMessage(event.data); break; case "message.delivered": await handleDeliveryConfirmation(event.data); break; case "message.failed": await handleDeliveryFailure(event.data); break; } }

Retry Policy

If your endpoint returns an error or doesn't respond within 30 seconds, Zavu will retry the delivery:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry15 minutes
4th retry1 hour
5th retry4 hours

After 5 failed attempts, the webhook delivery is marked as failed. You can view failed deliveries in the Dashboard.

Managing Webhooks

Update Webhook Configuration

Update your sender's webhook settings:

cURL
curl -X PATCH https://api.zavu.dev/v1/senders/snd_abc123 \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "webhookUrl": "https://new-url.com/webhooks", "webhookEvents": ["message.inbound"], "webhookActive": true }'

Disable Webhook

cURL
curl -X PATCH https://api.zavu.dev/v1/senders/snd_abc123 \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "webhookActive": false }'

Remove Webhook

Set webhookUrl to null to remove the webhook:

cURL
curl -X PATCH https://api.zavu.dev/v1/senders/snd_abc123 \ -H "Authorization: Bearer zv_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "webhookUrl": null }'

Regenerate Secret

If your webhook secret is compromised:

cURL
curl -X POST https://api.zavu.dev/v1/senders/snd_abc123/webhook/secret \ -H "Authorization: Bearer zv_live_xxx"

Response:

JSON
{ "secret": "whsec_new_secret_here..." }

Next Steps