PingMyUsers

What is a webhook? How webhooks work in email, SMS and WhatsApp APIs

Editorial team · updated · facts checked

A webhook is an HTTP request that an app sends to a URL you choose when an event happens, so your code hears about the event instead of asking for it. When a text is delivered, a customer replies or an email bounces, the messaging provider sends the details to your endpoint (a URL on your server), and your endpoint answers with a 2xx status. No-code tools work the same way: an automation platform gives you a URL to paste into the provider's webhook setting.

Twilio calls them status callbacks, SendGrid an Event Webhook. Each provider sets its own rules; the closest shared rulebook is Standard Webhooks, an open specification (version 1.0.0) that Bird follows. We re-read every vendor page quoted below on 24 September 2026.

Webhook vs API polling

Polling means calling the API on a timer, say reading a message's status every 30 seconds until it is final. A webhook reverses the direction: the provider calls you.

Polling the APIWebhook
Who sends the requestYour codeThe provider
When you learn of an eventAt the next pollWhen the provider sends it
RequestsOne per check, most with no newsOne per event, plus retries
What goes wrongRate limits, stale statusMissed, repeated, out-of-order or forged events

Many apps use both: webhooks for speed, an occasional API read to catch what a webhook missed.

How webhook delivery works

  1. You register a URL in the dashboard, through an API or per message (Telnyx takes a webhook_url in the send request).
  2. An event happens and the provider POSTs the details, usually as JSON. Twilio "occasionally adds parameters without advance notice", so don't reject unknown fields.
  3. Your endpoint answers 2xx before the timeout. Standard Webhooks recommends senders wait 15 to 30 seconds; some wait far less. Anything else is a failure: the provider retries on a schedule, then gives up.

The details differ by provider:

ProviderWaits for your answerRetries after a failure
Twilio15 s (5 s to connect), by default1, only when the connection fails, so a 500 from your app isn't retried; up to 5 with URL overrides
Telnyx messaging2 sUp to 3 attempts per URL, then the failover URL if set
PlivoNot stated3, after 60, 120 and 240 s
PostmarkNot stated6, 1 to 15 minutes apart
SMTP2GO10 sUp to 35 over 48 hours
Bird, Resend15 s at BirdSame schedule: 8 attempts over about 27.5 hours

Your status code matters too: Postmark and Sinch don't retry most 4xx responses, so a 401 from a broken auth check loses the event, while Bird retries every non-2xx. Botmaker pauses for 10 minutes after more than 100 failures in 10 minutes, and those messages are lost.

Webhook examples in email, SMS and WhatsApp APIs

  • Delivery receipts. A send call returns when the provider accepts the message; delivery news comes later. Twilio's status callbacks report sent or failed, then delivered or undelivered (and read on WhatsApp). Telnyx sends message.sent, then message.finalized.
  • Inbound messages. Replies to an SMS gateway number arrive as Telnyx message.received or a request to your Twilio Messaging URL. WhatsApp works the same way; our WhatsApp Cloud API tutorial sets one up.
  • Bounces and complaints. Telnyx Email sends email.delivered, email.bounced and email.complained; Postmark posts delivery, bounce, spam complaint, open and click events. The transactional email guide covers suppressing bounced addresses.
  • Verification codes. Bird's Verify events include verify.verification.verified.

Webhook signature verification: HMAC and public keys

A webhook URL is public, so anyone who finds it can post a fake "delivered" event or an invented reply. A signature over the body proves the request came from the provider, unchanged. With HMAC, the provider hashes the raw body (often with a timestamp and event ID) using a secret you share, and you recompute the hash and compare in constant time. With a public-key signature, you verify with the provider's public key, so no shared secret can leak.

Of the 27 providers in our dataset, 14 sign webhooks (sources checked 23 and 24 September 2026):

SchemeProviders
HMAC-SHA256360dialog (optional), Bird, Infobip (opt-in, algorithm configurable), Mailgun, Mailtrap, Plivo, Resend, Sinch (optional on Conversation API)
HMAC-SHA1 (SHA-256 with your own key, in beta)Twilio
JWT signed with HMAC-SHA256Vonage (Messages and Verify APIs)
Amazon SNS signature, checked with its X.509 certificateAmazon SES, AWS End User Messaging
ECDSA, opt-inTwilio SendGrid
Ed25519Telnyx
Not signedBlip, Botmaker, Brevo, ClickSend, Gupshup, Mailjet, Postmark, SendPulse, SMTP2GO, Textmagic, Treble, WATI, Zenvia

Check what a scheme covers: Mailgun's HMAC signs only a timestamp and a random token, and Plivo's V2 signature the URL plus a nonce, so neither vouches for the event data. Postmark "does not currently support HMAC webhook signature verification" and recommends Basic auth plus its IP ranges. Each provider card shows its signing status and the page it came from.

Replay protection. A captured delivery can be resent and it still verifies. A signed timestamp lets you refuse old copies, if your code checks it: Bird and Telnyx's Email docs say to reject deliveries more than 5 minutes old, and Mailgun notes that caching its tokens "will prevent replay attacks". Twilio's signature covers no timestamp.

Signing is 7 of the 100 points in our Developer & AI score: full credit with replay protection, 70% for a signature alone, 20% unsigned (methodology; scores in the AI-readiness data).

Verify a webhook signature in Node.js

Bird signs the Standard Webhooks way: HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{raw body}, keyed with the base64-decoded secret after whsec_, sent as v1,<base64> (two during a secret rotation). Resend sends the same three values as svix- headers.

Illustrative code, written from the official documentation and not run by the editorial team.

import crypto from "node:crypto";
import express from "express";

const app = express();
const key = Buffer.from(process.env.WEBHOOK_SECRET.replace(/^whsec_/, ""), "base64");

app.post("/webhooks/messages", express.raw({ type: "application/json" }), (req, res) => {
  const id = req.get("webhook-id");
  const ts = req.get("webhook-timestamp") ?? "";
  const age = Math.abs(Date.now() / 1000 - Number(ts));
  if (!id || !(age <= 300)) return res.sendStatus(400); // missing header or older than 5 minutes
  const expected = crypto.createHmac("sha256", key).update(`${id}.${ts}.${req.body}`).digest();
  const valid = (req.get("webhook-signature") ?? "").split(" ").some((s) => {
    const sig = Buffer.from(s.replace(/^v1,/, ""), "base64");
    return sig.length === expected.length && crypto.timingSafeEqual(sig, expected);
  });
  if (!valid) return res.sendStatus(400);
  res.sendStatus(200); // acknowledge first
  enqueue(id, req.body); // your job queue; it skips IDs already processed
});

express.raw keeps the exact bytes; re-serialized JSON breaks the signature. In production, prefer the provider's SDK or a Standard Webhooks library; Twilio says "Don't implement your own signature validation."

Handle each webhook event once

Telnyx warns that events "can be concurrent, duplicated, delayed, or delivered out of order." A handler that copes verifies the signature on the raw body, then:

  1. Stores the event ID under a unique constraint and skips repeats: webhook-id at Bird (unchanged on retries), data.id at Telnyx, the X-PM-Webhook-Trace-Id header at Postmark (stable across retries), and at Twilio the I-Twilio-Idempotency-Token header, which Twilio says distinguishes retry attempts.
  2. Answers 2xx at once and does slow work from a queue.
  3. Orders events by their own timestamp: at Bird, email.delivered can arrive before email.accepted.

The sending side of the same problem is an idempotency key. If a provider signs nothing, use what it offers (Basic auth, a URL secret, a static header, IP ranges) and read the status back from the API before acting on an event. The vibe coding security checklist makes this a pre-launch check, and the SMS API tutorial has Twilio and Telnyx handlers in Node.js and Python.

  • Vibe coding security checklist
  • How to send a text message with an API in Node.js and Python
  • Transactional email guide
  • WhatsApp Business API webhooks
  • What is an idempotency key?

About this guide

The PingMyUsers editorial team wrote this entry for developers and AI coding agents wiring delivery, reply and bounce events into an app. Sensaria AG in Switzerland operates the directory; providers don't pay for placement, and none reviewed this page. Report errors, with a source, to contact@sensaria.ch.

Methodology

On 24 September 2026 we re-read the Standard Webhooks specification and every vendor page cited above. The signing table comes from the webhook records of our 27 provider cards. We did not send messages, register endpoints or open accounts, and the code was not run. Section order follows 19 AI-search queries and the questions Google shows for "what is a webhook".

Last updated

24 September 2026: first version. Next re-check: March 2027, or sooner if a listed provider changes its retry or signing rules.

Frequently asked questions

What is the difference between a webhook and an API?

Your code calls an API when it wants something; a webhook is the provider calling your code when something happens. You send messages through the API and hear back through webhooks.

How do I create a webhook?

Write an HTTPS route that accepts POST, verifies the signature and returns 200, then register its URL with the provider. Locally, a tunnel such as ngrok gives you a public URL; Sinch's Conversation API doesn't retry callbacks to ngrok domains.

Are webhooks free?

Receiving them costs only your hosting, but plans can limit them. On 24 September 2026, WATI's Growth plan listed "No Webhooks", Resend's Pro plan included 5 webhook endpoints, and Botmaker billed webhooks at US$0.001 per call.

What are the downsides of using webhooks?

Your endpoint must stay up; events can be lost after the last retry, repeated or out of order; unsigned webhooks can be forged.

What is a webhook secret?

The key a provider uses to sign deliveries to one endpoint. Bird shows its whsec_ secret once, when you create the endpoint. Keep it in a secret manager, never in frontend code, and rotate it if it leaks.

Sources

  1. Standard Webhooks specification, version 1.0.0 — checked 24 September 2026
  2. Bird — Webhooks (signing, delivery semantics, replay) — checked 24 September 2026
  3. Twilio — Webhooks security — checked 24 September 2026
  4. Twilio — Webhooks connection overrides (timeouts, retries, I-Twilio-Idempotency-Token) — checked 24 September 2026
  5. Twilio — Outbound message status in status callbacks — checked 24 September 2026
  6. Twilio — Secure your Express app by validating incoming Twilio requests — checked 24 September 2026
  7. Telnyx — Receiving messaging webhooks — checked 24 September 2026
  8. Telnyx — Receiving webhooks (API fundamentals) — checked 24 September 2026
  9. Telnyx — Email webhooks and events — checked 24 September 2026
  10. Plivo — Messaging callbacks — checked 24 September 2026
  11. Plivo — Signature validation (messaging, V2) — checked 24 September 2026
  12. Resend — Webhook retries and replays — checked 24 September 2026
  13. Resend — Verify webhooks requests — checked 24 September 2026
  14. Resend — Pricing — checked 24 September 2026
  15. Postmark — Webhooks overview (security, retry attempts) — checked 24 September 2026
  16. SMTP2GO — Webhooks overview (retries and timeout) — checked 24 September 2026
  17. Sinch — Conversation API callbacks (delivery and retries) — checked 24 September 2026
  18. Mailgun — Securing webhooks — checked 24 September 2026
  19. Twilio SendGrid — Event Webhook security features — checked 24 September 2026
  20. Amazon SNS — Verifying the signatures of Amazon SNS messages — checked 24 September 2026
  21. Amazon SNS — Verifying message signatures (X.509 certificate steps) — checked 24 September 2026
  22. Botmaker — Cómo integrar webhooks — checked 24 September 2026
  23. Botmaker — Precios actualizados (USD add-ons) — checked 24 September 2026
  24. WATI — Pricing — checked 24 September 2026