Build an email webhook receiver that queues work safely
Authenticate inbound events, isolate tenant routing and acknowledge only after durable acceptance.
Check signatures, timestamps and event identity without trusting parsed or reconstructed payloads.
Webhook authentication covers more than the JSON fields your application happens to use. Lupin signs the concatenation of delivery ID, timestamp and raw body, separated by periods, using HMAC-SHA256. Each request supplies svix-id, svix-timestamp and svix-signature. The secret has a whsec_ prefix and the signature uses the v1 format. Use a maintained verifier that understands this format rather than comparing a homegrown hexadecimal digest. The useful outcome is a verified envelope with a known delivery identity, not merely a JSON object that looks plausible.
Read the request body once and preserve it for signature verification before JSON parsing. A framework that parses JSON and later serializes it can change whitespace, key order or escaping, yielding different signed bytes even when the logical object looks identical. Verify all three delivery headers, not only the signature string. If your reverse proxy or middleware consumes the body, configure a raw-body path for this endpoint. Treat a missing body or a decoding problem as a failed verification rather than falling back to trusting a custom header.
The checked-in Lupin verifier rejects timestamps outside a five-minute window. Keep the receiver clock synchronized and distinguish clock skew from a wrong secret when investigating failures. A valid timestamp bounds replay age but does not prevent a duplicate delivery within the allowed window. Deduplicate the verified delivery or event identity in durable storage as a separate step. Conversely, an event timestamp inside the JSON is not a substitute for the signed svix-timestamp header, because your verifier must authenticate the header together with the exact body.
The signature header can carry multiple space-separated v1 signatures during rotation. Let a format-aware library evaluate the supported signatures rather than splitting on commas incorrectly or accepting an arbitrary matching substring. Keep old and new secrets only for the intended transition period, and record which endpoint configuration each secret belongs to. A secret for one customer endpoint should not become a global fallback for all tenants. If verification fails after rotation, inspect configuration and delivery metadata privately; printing the secret or a full customer message into shared logs creates another incident.
Build local fixtures for a correct signature, one changed body byte, missing header, old timestamp, wrong endpoint secret and duplicate delivery. Assert that invalid requests never create a queue record and that a valid duplicate creates no second job. Return a small error response without echoing private content. Verification establishes that the delivery came through the configured sender path; it does not make the email’s author or instructions trustworthy. Your next step is to connect this verifier to a durable inbox-scoped queue and keep model execution outside the receiver’s immediate response path.
If the verifier is unavailable or its secret cannot be loaded, reject the delivery and expose an operational error. Do not accept the body because its shape matches a known sample or because the sender IP looks familiar. Those shortcuts remove the exact cryptographic boundary that protects account routing before the application has established which inbox the event may reference.
Reviewed against checked-in Lupin API handlers and documentation. Example requests were not executed against live providers.
Prepared with AI assistance by Lupin editorial. How we research and maintain these guides.