Skip to content
Beta — the Waasl API is in private beta. Endpoints marked Coming soon are designed but not live yet. Request early access

Webhooks

Coming soon Webhook endpoint management ships with the public beta. The events themselves already flow inside Waasl — see the event catalog for which are ready.

Webhooks push events to your HTTPS endpoint as they happen, so you never need to poll.

Terminal window
curl -X POST https://api.waasl.io/v1/webhook-endpoints \
-H "Authorization: Bearer $WAASL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/waasl/webhook", "events": ["message.received", "message.status_updated"] }'

Use ["*"] to receive everything. The response contains a secret (whsec_…) shown only once.

Every event shares an envelope:

{
"id": "evt_2Nq8Ld",
"type": "message.received",
"created_at": "2026-10-01T08:15:00Z",
"workspace_id": "ws_26N2SaWtMR",
"api_version": "2026-10-01",
"data": { "message": { "id": "msg_Vb3kQ9xP", "direction": "in", "kind": "text", "body": "مرحبا" }, "conversation": { "id": "cv_3PzA91" } }
}

Each request carries a Waasl-Signature header:

Waasl-Signature: t=1759306500,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

v1 is the hex HMAC-SHA256 of {t}.{raw request body} keyed with your endpoint secret. Compute it over the raw bytes (before JSON parsing), compare in constant time, and reject timestamps older than five minutes to stop replays.

import crypto from "node:crypto";
export function verifyWaasl(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSec) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(parts.v1, "hex"), Buffer.from(expected, "hex"));
}
  • Return any 2xx within 10 seconds. Do slow work in a background job.
  • Anything else — or a timeout — is retried with exponential backoff: 1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h.
  • After 3 days of consecutive failures the endpoint is disabled and workspace admins are emailed.
  • Deliveries can arrive more than once and out of order. De-duplicate on id, and compare created_at (or re-fetch the resource) before overwriting newer state.
  • Inspect recent attempts with GET /webhook-endpoints/{id}/deliveries.