Guides

Webhooks

Get data out of MYWA in real time. When something happens — a new lead, an incoming message, a delivery update — MYWA POSTs it to an HTTPS endpoint you control, signed so you can trust it.

Configured in the app, not the API

Add an Outbound Webhook in Settings → Integrations. Set your endpoint URL, choose which events to send, and MYWA delivers each one as JSON with retries and a delivery log.

Events

An Outbound Webhook can send any of these events. You pick which ones when you set it up, so your endpoint only receives what it needs.

FieldTypeDescription
lead.created
eventA new lead — from an ad, a campaign, or a first message.
message.received
eventA customer sent you a message.
message.status
eventA message you sent was sent, delivered, read, or failed.
contact.created
eventA new contact was added.
contact.opted_out
eventA contact unsubscribed from messages.
conversation.status_changed
eventA conversation moved to another status, e.g. open to resolved.
conversation.quality_changed
eventA conversation’s quality score changed.

The payload

Each delivery is a POST with the same JSON envelope: event, lead (filled for lead.created, null otherwise), data with the event’s own fields, and occurredAt. A new lead looks like this:

json
{
  "event": "lead.created",
  "lead": {
    "event": "lead.created",
    "name": "Jane Doe",
    "phone": "+15551234567",
    "email": "jane@example.com",
    "campaign": "Summer Sale",
    "sourceType": "ad",
    "source": "campaign",
    "tags": ["vip"],
    "leadAt": "2026-07-18T12:00:00.000Z",
    "conversationId": "cnv_…",
    "contactId": "cts_…",
    "organizationId": "org_…"
  },
  "data": { "leadSourceType": "campaign", "leadSource": "cmp_…", "referral": null },
  "occurredAt": "2026-07-18T12:00:00.000Z"
}

And a delivery update. status is one of sent, delivered, read, or failed; workflowRunId is set when an automation sent the message:

json
{
  "event": "message.status",
  "lead": null,
  "data": {
    "messageId": "msg_8f1a…",
    "status": "delivered",
    "errorCode": null,
    "workflowRunId": null
  },
  "occurredAt": "2026-07-18T12:00:04.000Z"
}

Two headers accompany it: X-Wasend-Event and X-Wasend-Signature.

Verifying the signature

X-Wasend-Signature is an HMAC-SHA256 of the raw request body using the integration’s signing secret (reveal it in the integration’s settings). Compute the same HMAC and compare in constant time. Always hash the raw bytes — re-serializing parsed JSON can change the output.

import crypto from 'node:crypto';
import express from 'express';

const app = express();
const SECRET = process.env.WASEND_WEBHOOK_SECRET;

// Capture the raw body for signature verification
app.post('/webhooks/wasend', express.raw({ type: '*/*' }), (req, res) => {
  const signature = req.header('X-Wasend-Signature') ?? '';
  const expected = crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');

  const ok =
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!ok) return res.status(401).end();

  const payload = JSON.parse(req.body.toString('utf8'));
  if (payload.event === 'lead.created') syncLeadToCrm(payload.lead); // your code
  res.json({ received: true });
});

From an automation

An automation can also call your endpoint mid-flow with the “Send to a webhook” step. It sends the event workflow.action with the automation, the run, the contact, and the run’s vars. Point the step at one of your Outbound Webhooks and it is signed the same way, with the same X-Wasend-Signature header, so the verification code above works unchanged.

json
{
  "event": "workflow.action",
  "workflow": { "id": "wf_9f2", "name": "Order follow-up" },
  "run": { "id": "run_7c1", "nodeId": "node_4" },
  "contact": { "id": "cts_31a…", "waPhone": "+15551234567" },
  "conversation": { "id": "cnv_88b…" },
  "vars": { "hook": { "orderId": "A-1049" } },
  "sentAt": "2026-08-27T09:20:00.000Z"
}

Delivery & retries

  • Respond with a 2xx quickly. Non-2xx responses are retried with backoff.
  • Do slow work (CRM sync, emails) after acknowledging, ideally on a queue.
  • Every attempt — success or failure — is recorded in the integration’s delivery history in Settings → Integrations, so you can see exactly what was sent and why anything failed.
Prefer a spreadsheet over code? The Google Sheets integration appends each new lead as a row — no endpoint to host.
MywaHey 👋 got a question? Say hi, a real human replies here.now