API & Integrations
How webhooks are signed and verified
Every webhook carries an HMAC-SHA256 signature. Verify it before trusting the payload.
4 min readUpdated 28 Mar 2026
Webhook endpoints are public by definition. To confirm that an incoming request actually came from SMSLocal and wasn't tampered with, verify the signature in every webhook header.
Verification steps
- 01Read the X-SMSLocal-Signature header — it contains a timestamp and an HMAC.
- 02Reject requests where the timestamp is older than 5 minutes (prevents replay).
- 03Compute HMAC-SHA256 over 'timestamp.raw_body' using your webhook secret.
- 04Compare the computed HMAC with the one in the header using constant-time equality.
- 05Only act on the payload if the HMACs match.
import crypto from "node:crypto"
function verify(req, secret) {
const [tsPart, sigPart] = req.headers["x-smslocal-signature"].split(",")
const ts = tsPart.split("=")[1]
const sig = sigPart.split("=")[1]
if (Date.now() / 1000 - Number(ts) > 300) return false
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.${req.rawBody}`)
.digest("hex")
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
}Related articles in API & Integrations
Browse other help categories
Getting started
Create your account, add credits, and send your first message in under fifteen minutes.
SMS campaigns
Templates, DLT routing, sender IDs, delivery reports, scheduling, and retries.
WhatsApp Business API
Onboarding, template approval, chatbot flows, team inbox, quality rating, and analytics.
AI WhatsApp Agents
Train your agent, sync your catalogue, set handoff rules, and go multilingual.
Did this article help?
If something isn't clear or the steps don't match what you're seeing, tell us and we'll fix the doc the same day.