Verify webhooks
Verify Samva's Standard Webhooks signatures with the samva SDK.
Samva signs every outbound webhook using the
Standard Webhooks convention. The samva
SDK provides two first-class verifier exports:
samva/webhooksfor Promise-based applications.samva/webhooks/effectfor Effect applications.
Both use WebCrypto and run on Node, Bun, Cloudflare Workers, and other edge
runtimes. There is no runtime-specific /node adapter.
Create endpoints and rotate signing secrets with the webhooks API or the SDK. The signing secret is shown only when it is created or rotated.
Signed request
Each delivery is a POST with these Standard Webhooks headers:
POST /webhooks/samva
webhook-id: evt_01…
webhook-timestamp: 1786796531
webhook-signature: v1,base64-signature
content-type: application/json
{"type":"message.delivered","timestamp":"2026-01-15T09:42:11.204Z","data":{"messageId":"msg_01…","status":"delivered"}}Verification covers the webhook-id, webhook-timestamp, and exact raw request
body. Read the body before JSON middleware changes it. The verifier rejects
timestamps outside the five-minute tolerance, which prevents replay of a
previously captured request.
Verify a request
bun add samvaimport { verifyRequest, WebhookVerificationError } from "samva/webhooks";
export async function POST(request: Request) {
try {
const event = await verifyRequest(
request,
process.env.SAMVA_WEBHOOK_SECRET!,
);
// Persist webhook-id under a unique constraint before processing.
await handleEvent(event);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof WebhookVerificationError) {
return new Response("invalid webhook", { status: 401 });
}
throw error;
}
}Effect applications can use the same contract without leaving Effect control flow:
import { verifyRequest } from "samva/webhooks/effect";Delivery behavior
- Return
2xxquickly. Samva gives eachPOST30 seconds. - Delivery is at least once and unordered. Dedupe on
webhook-id, not a message id or event type. - An automatic retry keeps the same
webhook-idbut uses a fresh timestamp and signature. - Automatic retries are scheduled after approximately
5s,5m,30m,2h,5h,10h, and10h, with jitter. A validRetry-Afterresponse can delay the next attempt. - Rotating a secret keeps the previous secret valid for 24 hours.
- Test deliveries are queued through the normal delivery lifecycle. Manual redelivery is a one-shot attempt and does not start another retry series.
- Delivery logs are retained for 90 days. An endpoint is disabled after five continuous days without a successful delivery.
For endpoint management and event payloads, see the webhooks API reference and event catalog.