Back

Wazzap AI API Documentation - Webhooks

Receive your agent's incoming WhatsApp messages in real time on your own server with Wazzap AI webhooks

Last updated :

Wazzap AI API Documentation - Webhooks

Table of contents

  1. Introduction
  2. Webhook setup
  3. Supported event types
  4. Delivery format
  5. Signature verification (HMAC)
  6. Reliability and failure handling
  7. Webhook server examples
  8. Best practices
  9. Troubleshooting

Introduction

Webhooks let you receive your agent's incoming WhatsApp messages in real time on your own server, without polling the API.

Every time your agent receives a message, Wazzap AI sends a signed POST request to the URL you configured.

Key features

  • Real-time delivery of incoming messages
  • Every delivery signed with HMAC-SHA256 (X-Wazzap-Signature header)
  • URL validated with a test ping at registration
  • Automatic disabling after repeated failures
  • One webhook per API key, bound to the key's agent

Typical use cases

  • Sync WhatsApp conversations into your CRM
  • Trigger workflows (Zapier, Make, n8n…) when a message arrives
  • Build your own support interface on top of Wazzap AI
  • Log all incoming messages for analytics

Webhook setup

From the dashboard

  1. Go to Settings → API Keys
  2. On the API key you want, open the Webhook section
  3. Enter your server's HTTPS URL
  4. Wazzap AI immediately sends a validation ping (webhook.test) — your server must reply with a 2xx status for the URL to be saved
  5. Copy the HMAC secret shown: you'll use it to verify each delivery's signature

URL constraints

Constraint Detail
Protocol https:// only (http rejected)
Network Private/local addresses are rejected (localhost, 127.x, 10.x, 192.168.x, 172.16-31.x, 169.254.x)
Validation The URL must answer the test ping with 2xx, otherwise it is not saved
Timeout 10 seconds per delivery

The validation ping

When you register the URL, your server receives this payload:

{
  "event": "webhook.test",
  "apiKeyId": "cm9xxx...",
  "agentId": "cm9yyy...",
  "data": {
    "message": "Wazzap validation ping — reply with a 2xx status to validate this webhook.",
    "timestamp": "2026-07-06T15:00:00.000Z"
  }
}

Simply reply 200 OK (your response body is ignored).


Supported event types

Event Trigger Description
webhook.test URL registration / test from the dashboard Validation ping. Reply 2xx.
message.received Incoming WhatsApp message received by your agent A contact sent a message to your connected WhatsApp number.

⚠️ What does NOT trigger a webhook

  • Your own sends via the /api/wazzap/send-message API
  • Messages typed manually from the connected phone (fromMe messages)
  • AI-generated replies from your agent
  • Read receipts, reactions and status updates

The webhook is intentionally limited to incoming messages: that's the event you need to react to your contacts. More event types may be added later — route on the payload's event field and safely ignore types you don't handle.


Delivery format

Request headers

Every delivery is a POST with these headers:

POST /your-endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Wazzap-Webhook/1.0
X-Wazzap-Signature: sha256=3f5a8c…

message.received payload — text message

{
  "event": "message.received",
  "agentId": "cm9yyy...",
  "organizationId": "cm9zzz...",
  "data": {
    "conversationId": "cmr9aaa...",
    "contactId": "cmr9bbb...",
    "phoneNumber": "33612345678",
    "contactName": "John Doe",
    "message": "Hi, I'd like some information",
    "messageType": "text",
    "fromMe": false,
    "media": null,
    "transcription": null,
    "description": null,
    "messageId": "3EB0A5B4E7CB234BC78A48",
    "timestamp": "2026-07-06T15:18:55.000Z"
  }
}

message.received payload — voice message (audio)

Voice messages are automatically transcribed (Whisper). The transcription lands in message AND transcription; the audio file (hosted on our public CDN) is in media.url.

{
  "event": "message.received",
  "agentId": "cm9yyy...",
  "organizationId": "cm9zzz...",
  "data": {
    "conversationId": "cmr9aaa...",
    "contactId": "cmr9bbb...",
    "phoneNumber": "33612345678",
    "contactName": "John Doe",
    "message": "Hi, is the product still available?",
    "messageType": "audio",
    "fromMe": false,
    "media": {
      "url": "https://pub-xxxx.r2.dev/wazzap-v2.5/audio/1783351406671-xxx.mp3",
      "type": "audio",
      "mimeType": "audio/ogg; codecs=opus"
    },
    "transcription": "Hi, is the product still available?",
    "description": null,
    "messageId": "false_963835624366@lid_3A4355A4022B54D3A211",
    "timestamp": "2026-07-06T15:23:14.000Z"
  }
}

💳 Cost: each successful transcription consumes 1 credit from your organization. If your balance is zero, transcription is skipped (the voice note is still delivered with its media.url, transcription is null). You can disable transcription at any time with the "Transcribe voice notes" switch in the webhook settings (Settings → API Keys → Webhook).

⚠️ If transcription fails (inaudible audio, service unavailable…), no credit is charged and the delivery still happens: transcription is null, message contains any text/caption (usually "" for a voice note), and media.url lets you fetch the audio and process it yourself.

message.received payload — image

Incoming images are analyzed by AI vision: the generated description lands in description. message contains the caption typed by the contact (often empty).

{
  "event": "message.received",
  "agentId": "cm9yyy...",
  "organizationId": "cm9zzz...",
  "data": {
    "conversationId": "cmr9aaa...",
    "contactId": "cmr9bbb...",
    "phoneNumber": "33612345678",
    "contactName": "John Doe",
    "message": "Is this the right model?",
    "messageType": "image",
    "fromMe": false,
    "media": {
      "url": "https://pub-xxxx.r2.dev/wazzap-v2.5/images/1783351406671-xxx.jpg",
      "type": "image",
      "mimeType": "image/jpeg"
    },
    "transcription": null,
    "description": "Photo of a pair of white sneakers with red laces…",
    "messageId": "false_963835624366@lid_3A4355A40BB54D3A211",
    "timestamp": "2026-07-06T15:25:02.000Z"
  }
}

Payload fields

Field Type Description
event string Event type (message.received)
agentId string ID of the agent that received the message
organizationId string Your organization ID
data.conversationId string Conversation ID in Wazzap AI
data.contactId string Contact ID in Wazzap AI
data.phoneNumber string Sender's number (without the +)
data.contactName string | null Contact name (if known)
data.message string Clean message text: text or caption; transcription for a voice note when it succeeded; "" for a silent media
data.messageType string text | image | audio | video | document
data.fromMe boolean Message direction: false = sent by the contact (incoming). Always false for message.received — explicit field, ready for future event types
data.media object | null Attached media: { url, type, mimeType }. null for a text message. url points to our public CDN (or the source URL if the CDN upload failed)
data.transcription string | null Audio only: automatic transcription of the voice note, null if it failed or wasn't attempted
data.description string | null Image only: AI-vision generated description, null if not analyzed
data.messageId string | null WhatsApp message ID (use it as an idempotency key)
data.timestamp string Message send time (ISO-8601)

Your response

Reply with a 2xx status (ideally 200) as fast as possible — process the message asynchronously if your logic is slow. Any non-2xx response or a timeout (>10s) counts as a delivery failure.


Signature verification (HMAC)

Every delivery is signed with your webhook's HMAC secret:

X-Wazzap-Signature: sha256=<hex(HMAC-SHA256(secret, raw_request_body))>

⚠️ Always verify the signature before processing a delivery — it's your guarantee the request really comes from Wazzap AI. Compute the HMAC over the raw body (the exact bytes received, before any JSON parsing).


Reliability and failure handling

Rule Value Detail
Delivery timeout 10s Beyond that, the delivery counts as failed
Tolerated consecutive failures 20 The counter (webhookFailCount) increments on each failure
After 20 consecutive failures Webhook disabled Re-enable it from the dashboard once your endpoint is fixed
After one success Counter reset to zero A single success clears the failure streak

⚠️ No automatic redelivery: a failed delivery is not replayed. If your endpoint was down, recover missed messages from your Wazzap AI dashboard.


Webhook server examples

Test quickly without writing code

To validate your setup without a server, use a webhook inspection service (webhook.site, webhooklens.cloud, requestbin…): paste the generated URL into the dashboard, send a WhatsApp message to your agent from another phone, and watch the delivery arrive.

Forward to Slack

app.post('/wazzap-webhook', async (req, res) => {
  if (!verifySignature(req)) return res.status(401).end();
  res.status(200).json({ ok: true });

  const { event, data } = req.body;
  if (event !== 'message.received') return;

  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `💬 *${data.contactName || data.phoneNumber}* : ${data.message}`
    })
  });
});

Auto-reply via the API

app.post('/wazzap-webhook', async (req, res) => {
  if (!verifySignature(req)) return res.status(401).end();
  res.status(200).json({ ok: true });

  const { event, data } = req.body;
  if (event !== 'message.received') return;

  // Example: acknowledgement outside business hours
  const hour = new Date().getHours();
  if (hour < 9 || hour >= 18) {
    await fetch('https://api21.wazzap.ai/api/wazzap/send-message', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.WAZZAP_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        phoneNumber: '+' + data.phoneNumber,
        message: 'Thanks for your message! We will reply from 9am.',
        disableAiResponse: true
      })
    });
  }
});

Best practices

1. Reply fast, process later

Your endpoint has 10 seconds to respond. Acknowledge immediately (200) then process asynchronously (queue, worker…). Slow synchronous processing = timeouts = webhook disabled after 20 failures.

2. Always verify the signature

Without HMAC verification, anyone who knows your URL can inject fake messages.

3. Be idempotent

Use data.messageId as an idempotency key: if you receive the same messageId twice, process it only once.

4. Ignore unknown events

Route on the event field and reply 200 to types you don't handle — new types may appear.

5. Monitor your endpoint

An endpoint failing silently = webhook disabled after 20 failures. Monitor its availability and check the webhook status in the dashboard regularly.


Troubleshooting

I'm not receiving any webhooks:

  1. Check the webhook is enabled in the dashboard (it may have been disabled after 20 failures)
  2. Remember only incoming messages trigger the webhook — not your API sends nor messages typed from the phone
  3. Check your WhatsApp agent is connected
  4. Test with an inspection service (webhook.site) to isolate whether the issue is on Wazzap's side or your server's

The URL is rejected at registration:

  1. The URL must be https:// and publicly reachable (no localhost/private IP)
  2. Your server must answer the webhook.test ping with 2xx within 10s

Signature verification fails:

  1. Compute the HMAC over the request's raw body, not re-serialized JSON
  2. Check you're using the right secret (viewable again in the dashboard)
  3. The header format is sha256=<hex> — compare the full string

Contact

For any question:


APIWebhooksWhatsAppIntegrationDevelopment

Create your AI assistant for free

Transform your WhatsApp conversations into business opportunities, qualify your leads and schedule appointments automatically

Amadou Fall
Kaaramoo
Bestilia
Joel Toulan
4.8 / 5
Used by + 40 125 users

No credit card required ·

Wazzap AI API Documentation - Webhooks | Wazzap AI - Le 1er Setter IA pour les agences.