theQuickAssist.cotheQuickAssist.co

Getting Started

  • Overview
  • Quickstart Guide
  • API Keys & Authentication

WhatsApp Templates

  • Module Overview

Appointment Booking

  • Module Overview

Webhooks

  • Webhooks

Create intelligent AI assistants for your website in minutes without coding.

TheQuickAssist is a product of Prominno Labs Pvt. Ltd.

Solutions

  • Real Estate
  • Healthcare
  • Education
  • E-Commerce
  • IT Companies
  • Marketing Agencies
  • Hotels & Restaurants
  • Travel Agencies
  • Salons & Spas
  • Banking & Fintech

Free Tools

  • All Free Tools
  • WhatsApp Chat Button
  • WhatsApp QR Code Generator
  • Free WhatsApp Link Generator

Quick Links

  • AI Chatbot
  • Pricing
  • Blogs
  • Contact Us
  • Documentation

Company

  • Affiliate Program
  • Cancellation & Refunds
  • Shipping
Trustpilot
InstagramFacebookTwitter
theQuickAssist.co

2026 TheQuickAssist.co. All rights reserved.

Privacy PolicyTerms of ServiceCookie Policy
Documentation/Webhooks

Template Delivery Webhooks

Webhooks deliver real-time notifications when WhatsApp template messages dispatched from the API change status. Receive instant updates when messages are sent, delivered, read, or failed.

Delivery Lifecycle

When you dispatch an approved template via POST /v1/templates/send, WhatsApp delivers event notifications through each stage of the message lifecycle:

1. Sent

Dispatched from Meta Cloud API and en route

2. Delivered

Delivered to recipient device (Double grey ticks)

3. Read

Opened & read by recipient (Double blue ticks)

Failed

Delivery failed (includes error reason)

Webhook Event Format

Every delivery notification is sent with the event name messages.status:

FieldTypeDescription
eventstringAlways messages.status.
timestampstringISO 8601 UTC timestamp of the webhook event.
data.messageIdstringWhatsApp Message ID (wamid.HBgL...) returned in the send template API response.
data.statusstringCurrent delivery status: sent | delivered | read | failed.
data.recipientPhonestringRecipient phone number in E.164 format (91XXXXXXXXXX).
data.timestampnumberUnix epoch timestamp in seconds.
data.errorobject | nullIncluded when status is failed. Contains error code, title, and clear failure explanation.

Event Payload Examples

Delivered to Phone
{
  "event": "messages.status",
  "timestamp": "2026-08-19T10:30:05.000Z",
  "data": {
    "messageId": "wamid.HBgLMzE5NDU...",
    "status": "delivered",
    "recipientPhone": "91XXXXXXXXXX",
    "timestamp": 1724063405
  }
}
Read by Recipient (Blue Ticks)
{
  "event": "messages.status",
  "timestamp": "2026-08-19T10:30:15.000Z",
  "data": {
    "messageId": "wamid.HBgLMzE5NDU...",
    "status": "read",
    "recipientPhone": "91XXXXXXXXXX",
    "timestamp": 1724063415
  }
}
Delivery Failed (With Reason)
{
  "event": "messages.status",
  "timestamp": "2026-08-19T10:30:05.000Z",
  "data": {
    "messageId": "wamid.HBgLMzE5NDU...",
    "status": "failed",
    "recipientPhone": "91XXXXXXXXXX",
    "timestamp": 1724063405,
    "error": {
      "code": 131026,
      "title": "Delivery Failed",
      "message": "Delivery blocked by WhatsApp quality policy for this recipient."
    }
  }
}

Receiver Implementation & Signature Verification

All webhook requests sent to your server include the x-webhook-signature header (HMAC SHA-256). Verify this header to ensure incoming payloads are authentic.

const express = require('express');
const crypto = require('crypto');

const app = express();

// IMPORTANT: Capture exact raw body Buffer for HMAC verification
app.use(express.json({
  verify: (req, _res, buf) => {
    req.rawBody = buf;
  }
}));

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

app.post('/webhook/whatsapp', (req, res) => {
  const signature = req.headers['x-webhook-signature'];

  // 1. Enforce signature header requirement
  if (!signature || typeof signature !== 'string') {
    return res.status(401).json({ error: 'Missing x-webhook-signature header' });
  }

  if (!WEBHOOK_SECRET) {
    console.error('WEBHOOK_SECRET environment variable is not configured');
    return res.status(500).json({ error: 'Server webhook secret unconfigured' });
  }

  // 2. Strip optional 'sha256=' prefix
  const cleanSignature = signature.startsWith('sha256=')
    ? signature.slice(7)
    : signature;

  if (!/^[a-f0-9]{64}$/i.test(cleanSignature)) {
    return res.status(401).json({ error: 'Malformed signature format' });
  }

  // 3. Compute HMAC SHA-256 over exact raw bytes
  const rawBody = req.rawBody || Buffer.from('');
  const expectedHash = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(rawBody)
    .digest('hex');

  // 4. Constant-time comparison to prevent timing attacks
  const sigBuffer = Buffer.from(cleanSignature, 'hex');
  const expectedBuffer = Buffer.from(expectedHash, 'hex');

  if (
    sigBuffer.length !== expectedBuffer.length ||
    !crypto.timingSafeEqual(sigBuffer, expectedBuffer)
  ) {
    return res.status(401).json({ error: 'Invalid webhook signature' });
  }

  // 5. Process authenticated event payload
  const { event, data } = req.body;

  if (event === 'messages.status') {
    const { messageId, status, recipientPhone, error } = data;

    if (status === 'delivered') {
      console.log(`✅ Message ${messageId} delivered to ${recipientPhone}`);
    } else if (status === 'read') {
      console.log(`👁️ Message ${messageId} read by ${recipientPhone}`);
    } else if (status === 'failed') {
      console.error(`❌ Message ${messageId} failed: ${error?.message}`);
    }
  }

  // Always return 200 OK promptly
  res.status(200).json({ received: true });
});

app.listen(3000, () => console.log('Webhook server running on port 3000'));

Webhook Security Best Practices

  • Always host your webhook URL over secure HTTPS.
  • Verify the HMAC SHA-256 signature using your Webhook Secret.
  • Respond immediately with 200 OK before doing long asynchronous tasks.