Whats91
Developers

Practical webhook receiver examples and use cases for CRM, delivery tracking, and template operations.

Webhook Examples

Summary

Practical webhook receiver examples and use cases for CRM, delivery tracking, and template operations.

Prerequisites

  • A Whats91 account
  • A generated public API token

These examples show how teams usually connect Whats91 Webhooks v2 to application workflows after creating a destination.

Use Cases

message.inbound.text

CRM handoff

Create or update a CRM conversation when a customer sends a new WhatsApp message.

message.status.delivered

Delivery status sync

Update order, invoice, or campaign timelines when Whats91 receives message status events.

template.status_update

Template review alerts

Notify operations teams when a template is approved, rejected, paused, or needs review.

Node.js receiver

This receiver validates the custom verification header, parses the event, shows how to respond with 200 quickly, and then routes the work asynchronously.

Node.js receiver
import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.raw({ type: 'application/json' }));

const signingSecret = process.env.WHATS91_WEBHOOK_SIGNING_SECRET;
const verificationToken = process.env.WHATS91_WEBHOOK_VERIFICATION_TOKEN;

function verifySignature(rawBody, signature) {
  if (!signingSecret || !signature) return false;

  const expected = crypto
    .createHmac('sha256', signingSecret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

app.post('/webhooks/whats91', async (req, res) => {
  const customToken = req.header('X-CRM-Webhook-Token');
  if (verificationToken && customToken !== verificationToken) {
    return res.sendStatus(401);
  }

  const signature = req.header('X-Whats91-Signature');
  if (!verifySignature(req.body, signature)) {
    return res.sendStatus(401);
  }

  const payload = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(200);

  queueWebhookWork(payload);
});

Keep the request handler fast. Acknowledge the webhook first, then process CRM writes, notifications, and analytics updates in a queue or background job.

CRM handoff example

CRM inbound handler
async function handleWebhook(payload) {
  if (payload.event !== 'message.inbound.text') return;

  await crm.conversations.upsert({
    phone: payload.data.from,
    source: 'whats91',
    lastMessage: payload.data.text,
    externalMessageId: payload.data.messageId,
    receivedAt: payload.data.timestamp,
  });
}

Delivery status sync example

Status sync handler
async function syncDeliveryStatus(payload) {
  if (!payload.event.startsWith('message.status.')) return;

  await messageReports.updateByMetaMessageId(payload.data.messageId, {
    status: payload.data.status,
    recipient: payload.data.recipient,
    updatedAt: payload.data.timestamp,
  });
}

Template review alerts example

Template status handler
async function notifyTemplateReview(payload) {
  if (payload.event !== 'template.status_update') return;

  await notifications.send({
    channel: 'template-ops',
    title: 'Template status changed',
    message: payload.data.templateName + ' is now ' + payload.data.status,
  });
}

Operational checklist

  • Create the webhook with status ACTIVE only after the receiver endpoint is deployed.
  • Store WHATS91_WEBHOOK_SIGNING_SECRET from the create response immediately.
  • Use X-CRM-Webhook-Token or another verificationHeaderKey when your receiver expects a shared custom token.
  • Subscribe only to events your application actually processes.
  • Return HTTP 200 before long-running CRM, ERP, reporting, or notification work.
  • Use status INACTIVE to disable a webhook; there is no public delete endpoint in this phase.

Related Documentation