Guides

WhatsApp API Webhook Integration Guide: Events, Payloads & Setup (2026)

Published on
Updated on
18 min read
By Kapil Nagar
WhatsApp API webhook integration with signature verification and async event processing

What URL format does WhatsApp webhook require?

Complete WhatsApp API webhook guide — configure inbound events, verify signatures, parse payloads, async processing patterns, and connect your backend to Ominiflow and Meta Cloud API. A publicly accessible HTTPS URL that accepts POST requests and returns HTTP 200. Meta does not support plain HTTP endpoints.

Last updated: 6 July 2026

WhatsApp API webhooks are HTTP callbacks that notify your server when customers send messages, when delivery status changes, or when errors occur. They are the backbone of real-time WhatsApp integrations — connecting your CRM, e-commerce platform, or custom app to conversational events without polling.

For the full integration mental model (outbound + inbound pipes, BSP layer, security), read the WhatsApp API integration guide first. New to Meta’s developer console? Start with WhatsApp Cloud API get started. This document goes deep on webhook events, payloads, signature verification, async patterns, and production failures we've debugged at 2 AM.

Start here if you're in a hurry

  • Webhooks push events from Meta → Ominiflow → your endpoint (or directly to your server)
  • Key events: messages (inbound), statuses (sent/delivered/read/failed), errors
  • Your endpoint must return HTTP 200 in under 5 seconds; process async for heavy logic
  • Verify X-Hub-Signature-256 on every POST — HMAC-SHA256 of raw body vs app secret
  • Use HTTPS with valid SSL — Meta rejects HTTP endpoints
  • Deduplicate on message.id — Meta retries on non-200 responses

What is a WhatsApp API webhook?

A webhook is a POST request Meta sends to a URL you configure when something happens on your WhatsApp Business account. Instead of asking "any new messages?" every few seconds, your system receives instant notifications with JSON payloads containing message text, sender phone, media URLs, and delivery receipts.

Outbound (you send) uses REST API. Inbound (customer replies, status updates) uses webhooks. Production integrations need both. Send guide: send messages programmatically.

Webhook event types

EventJSON pathWhen it firesTypical use
messagesentry[].changes[].value.messages[]Customer sends text, image, button reply, locationChatbot routing, CRM logging, auto-replies
statusesentry[].changes[].value.statuses[]sent, delivered, read, failedCampaign analytics, retry logic, SLA tracking
errorsentry[].changes[].value.errors[]API or policy errorsAlerting, template debugging
contactsentry[].changes[].value.contacts[]With inbound messagesSender profile name, wa_id

Setup checklist — seven steps

  1. Get API access — Complete WhatsApp Business API onboarding on Ominiflow.
  2. Create HTTPS endpoint — Public URL accepting POST (e.g. https://api.yourcompany.com/whatsapp/webhook).
  3. Handle GET verification — Echo hub.challenge when hub.verify_token matches your secret.
  4. Subscribe WABA to app — POST /{WABA_ID}/subscribed_apps — dashboard test button skips this.
  5. Configure webhook fields — Subscribe to messages and message_template_status_update as needed.
  6. Verify signatures — Validate every POST before parsing JSON.
  7. Test with real phone — Send message from personal WhatsApp; confirm payload hits your server.

Printable: onboarding checklist

Webhook verification (GET challenge)

Meta sends a GET request during setup:

GET /whatsapp/webhook?hub.mode=subscribe
  &hub.verify_token=YOUR_VERIFY_TOKEN
  &hub.challenge=CHALLENGE_STRING

Your endpoint must return hub.challenge as plain text if hub.verify_token matches. Otherwise return 403.

// Express.js example
app.get('/whatsapp/webhook', (req, res) => {
  const mode = req.query['hub.mode'];
  const token = req.query['hub.verify_token'];
  const challenge = req.query['hub.challenge'];

  if (mode === 'subscribe' && token === process.env.VERIFY_TOKEN) {
    return res.status(200).send(challenge);
  }
  return res.sendStatus(403);
});

Signature verification (critical)

Every POST includes X-Hub-Signature-256. Compute HMAC-SHA256 of the raw request body using your app secret. Compare with constant-time equality.

const crypto = require('crypto');

function verifySignature(rawBody, signatureHeader, appSecret) {
  if (!signatureHeader?.startsWith('sha256=')) return false;
  const expected = crypto
    .createHmac('sha256', appSecret)
    .update(rawBody)  // raw bytes, NOT parsed JSON
    .digest('hex');
  const received = signatureHeader.slice(7);
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(received, 'hex')
  );
}

// Express: use express.raw() or capture rawBody before JSON parse
app.post('/whatsapp/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySignature(req.body, req.headers['x-hub-signature-256'], APP_SECRET)) {
    return res.sendStatus(401);
  }
  // Acknowledge immediately
  res.sendStatus(200);
  // Queue async processing
  queue.add('process-wa-webhook', JSON.parse(req.body.toString()));
});

Common bug: Parsing JSON before signature verification invalidates the HMAC. Always verify against the raw body bytes Meta sent.

Sample inbound message payload

{
  "object": "whatsapp_business_account",
  "entry": [{
    "id": "WABA_ID",
    "changes": [{
      "value": {
        "messaging_product": "whatsapp",
        "metadata": {
          "display_phone_number": "919876543210",
          "phone_number_id": "PHONE_NUMBER_ID"
        },
        "contacts": [{
          "profile": { "name": "Priya Sharma" },
          "wa_id": "919988776655"
        }],
        "messages": [{
          "from": "919988776655",
          "id": "wamid.HBgNOTE...",
          "timestamp": "1720000000",
          "type": "text",
          "text": { "body": "Where is my order ORD-88421?" }
        }]
      },
      "field": "messages"
    }]
  }]
}

Button reply payload

"messages": [{
  "from": "919988776655",
  "id": "wamid.HBgNOTE...",
  "type": "interactive",
  "interactive": {
    "type": "button_reply",
    "button_reply": { "id": "confirm_yes", "title": "YES" }
  }
}]

COD flows branch on button_reply.id. Guide: COD confirmation

Sample delivery status payload

{
  "entry": [{
    "changes": [{
      "value": {
        "statuses": [{
          "id": "wamid.HBgNOTE...",
          "status": "delivered",
          "timestamp": "1720000060",
          "recipient_id": "919988776655",
          "conversation": {
            "id": "CONVERSATION_ID",
            "origin": { "type": "utility" }
          },
          "pricing": { "billable": true, "category": "utility" }
        }]
      },
      "field": "messages"
    }]
  }]
}

Status values: sent → delivered → read (or failed with errors array). Store message.id from your send response to correlate.

Async processing pattern

Meta retries on non-200 or slow responses. Duplicate events without deduplication = duplicate CRM entries.

  1. Receive POST → verify signature
  2. Return HTTP 200 immediately (under 5 seconds, ideally under 500ms)
  3. Enqueue job — Redis, SQS, RabbitMQ, or database outbox
  4. Worker processes — parse payload, dedupe on message.id, update CRM, trigger bot
  5. Idempotent handlers — same message.id processed twice = no-op second time
Anti-patternWhat breaksFix
CRM API call inside webhook handlerTimeout → Meta retry → duplicate leadQueue + dedupe
Shared hosting with 30s cold startIntermittent missed eventsHost on GCP/AWS with warm instances
No signature checkSpoofed payloadsHMAC verification on raw body
Logging full payloadsPII in plaintext logsRedact phone and message body

Integration patterns

1. CRM sync (inbound → CRM)

Customer replies → webhook → create/update CRM lead, log conversation on timeline. Native: Zoho CRM integration.

2. E-commerce order lookup

Customer sends order ID → webhook triggers lookup → chatbot responds with shipping status. Shopify guide

3. Outbound triggers (your app → WhatsApp)

Your backend POSTs to Ominiflow API on business events. Use approved message templates for business-initiated sends.

4. Chatbot API nodes

Ominiflow flow builder HTTP nodes call your APIs during conversations — complementing external webhooks for bidirectional automation.

Security best practices

  • Validate X-Hub-Signature-256 on every POST — primary guard, not IP allowlists alone
  • Return 200 immediately; never block on downstream API calls
  • Store app secret in secrets manager — not in git
  • Redact PII in logs — hash phone numbers if you must log identifiers
  • Rotate verify tokens and app secrets when team members leave
  • HTTPS only — valid certificate chain; Meta rejects self-signed

Troubleshooting webhooks

SymptomRoot causeFix
Dashboard test works; real messages don't arriveWABA not subscribed to appPOST /{WABA_ID}/subscribed_apps
Outbound OK; inbound deadPhone PENDING or messages not subscribedRe-verify number; check subscription fields
Duplicate CRM entriesWebhook retries, no idempotencyDedupe on message.id
401 on signature checkParsed JSON before HMACVerify raw body bytes
Events arrive hours lateSlow endpoint or cold hostingReturn 200 fast; queue processing

More: common API mistakes

Next steps

Talk to API Team →

Read: integration guide · send messages · Ominiflow pricing

FAQ

Do I need a developer to set up webhooks? expand_more

Basic no-code flows work inside Ominiflow's chatbot builder. Custom CRM or backend sync requires a developer to build and host an HTTPS endpoint.

What URL format does WhatsApp webhook require? expand_more

Publicly accessible HTTPS URL accepting POST requests, returning HTTP 200. Meta does not support plain HTTP.

How fast must my webhook respond? expand_more

Return HTTP 200 within 20 seconds (Meta's limit). Best practice: under 5 seconds. Queue heavy processing asynchronously.

How do I verify webhook signatures? expand_more

Compute HMAC-SHA256 of the raw request body with your app secret. Compare to X-Hub-Signature-256 header using constant-time equality.

Can webhooks work with Shopify or WooCommerce? expand_more

Yes — via Ominiflow native connectors or middleware like Make. Shopify integration guide

What is the difference between webhooks and REST API? expand_more

Webhooks are inbound (Meta pushes to you). REST API is outbound (you push messages to Meta). Production integrations use both.

Why do I get duplicate webhook events? expand_more

Meta retries on non-200 responses or timeouts. Deduplicate on message.id in your database before processing.

Can Ominiflow forward webhooks to my server? expand_more

Yes — configure primary webhooks in the dashboard. Ominiflow receives from Meta and forwards normalized payloads to your endpoint. Last updated July 2026. Webhook integration guide for developers — verified against Meta Cloud API documentation.

Need help? Chat with AI