Verify webhooks
Verify the signature on Samva's outbound webhooks with the samva SDK's samva/webhooks export.
Verify webhooks
Samva signs every outbound webhook so you can confirm a request genuinely came
from Samva before acting on it. The samva SDK's
samva/webhooks export verifies that signature
for you — it's edge-safe (WebCrypto), so the same code runs on Node, Bun,
Cloudflare Workers, and Vercel Edge.
samva/webhooks only verifies signatures. Create endpoints and rotate
signing secrets with the webhooks API or the SDK.
The signature
Each delivery is a POST with the signature in a header:
POST /webhooks/samva
X-Webhook-Signature: sha256=<hex>
X-Webhook-Event: message.delivered
X-Webhook-Id: <endpoint id>
{ "event": "message.delivered", "messageId": "msg_…", "timestamp": "…", "data": { … } }The signature is HMAC-SHA256(secret, rawBody), hex-encoded. Verify the raw
body — re-serializing it (for example, via JSON middleware) changes the bytes and
breaks verification.
Verify a request
bun add samvaA Web Request (Next.js Route Handlers, Hono, TanStack Start, Remix, Workers):
import { verifyRequest, WebhookVerificationError } from "samva/webhooks";
export async function POST(req: Request) {
try {
const event = await verifyRequest(req, process.env.SAMVA_WEBHOOK_SECRET!);
switch (event.event) {
case "message.delivered":
// mark delivered…
break;
case "message.bounced":
// suppress the address…
break;
}
return new Response(null, { status: 200 });
} catch (err) {
if (err instanceof WebhookVerificationError) {
return new Response("invalid signature", { status: 400 });
}
throw err;
}
}A Node IncomingMessage — use the /node adapter and a raw-body parser, since
JSON middleware re-serializes the body:
import { verifyNodeRequest } from "samva/webhooks/node";
// app.post("/webhooks/samva", express.raw({ type: "application/json" }), handler)Tips
- Return
2xxfast, then do any slow follow-up work asynchronously. - Be idempotent — webhooks are delivered at least once. Dedupe on
event+messageIdtogether; a single message emits several events (sent,delivered,read, …), somessageIdalone would drop valid ones.
For the full list of events and for endpoint management, see the webhooks API reference.