Signature Verification
Primitive signs outbound webhook deliveries so your endpoint can verify that the payload came from Primitive and was not modified in transit.
The header is:
Primitive-Signature: t=<unix-seconds>,v1=<hex-hmac>Verify the exact raw request body. Do not parse JSON and re-stringify it before verification.
SDK Helpers
import { verifyWebhookSignature, WebhookVerificationError, PRIMITIVE_SIGNATURE_HEADER, } from '@primitivedotdev/sdk/api'; export async function POST(req: Request) { const rawBody = await req.text(); const signatureHeader = req.headers.get(PRIMITIVE_SIGNATURE_HEADER) ?? ''; try { await verifyWebhookSignature({ rawBody, signatureHeader, secret: process.env.PRIMITIVE_WEBHOOK_SECRET!, }); } catch (error) { if (error instanceof WebhookVerificationError) { return new Response('invalid signature', { status: 401 }); } throw error; } return Response.json({ ok: true }); }
Manual Flow
- Parse the
tandv1parts ofPrimitive-Signature. - Reject timestamps outside your tolerance window.
- Build the signed payload as
<timestamp>.<raw-body>. - Compute HMAC-SHA256 with your webhook signing secret.
- Compare the expected hex digest to
v1using a constant-time comparison.
Common Mistakes
- Reading
request.json()before verification. - Verifying a re-serialized JSON string instead of raw bytes.
- Comparing strings with normal equality instead of a timing-safe comparison.
- Returning 4xx for event types you intentionally ignore. Verify first, then return 2xx for ignored but trusted events.
Related Pages
- Receiving Mail: webhook delivery behavior.
- Webhook Payload: event schema and fields.
- SDKs: high-level receive helpers.