Webhook best practices
Respond fast, process later
Section titled “Respond fast, process later”Verify the signature, enqueue the event (database row, queue, background job) and return 2xx immediately. SpacePost waits at most 15 seconds, and a slow handler turns into retries and duplicates.
Be idempotent
Section titled “Be idempotent”Delivery is at least once. Store the webhook-id of each processed delivery (a unique constraint is enough) and skip repeats.
const inserted = await db.insert(processedWebhooks).values({ id: webhookId }).onConflictDoNothing().returning();if (inserted.length === 0) return new Response("duplicate", { status: 200 });Always verify signatures
Section titled “Always verify signatures”Use the raw body, reject timestamps older than 5 minutes, compare signatures in constant time (the SDK and libraries do). See Verifying signatures.
Do not trust order
Section titled “Do not trust order”Events can arrive out of order, especially after retries. Compare the payload’s created_at with what you have stored, and only apply changes that move state forward. SpacePost’s own last_event follows the same rule.
Act on the events that matter
Section titled “Act on the events that matter”| Event | Typical action |
|---|---|
email.bounced |
Mark the address invalid in your database; stop sending to it. |
email.complained |
Unsubscribe the user from non-essential mail immediately. |
email.delivery_delayed |
Usually nothing; alert if it is a time-critical email (password reset). |
email.failed |
Log data.failed.reason, alert, possibly retry with a fix. |
email.suppressed |
A recipient was on your suppression list; show it in your admin UI. |
Subscribe only to the events you handle. Fewer events means less traffic and fewer failures.
Secure the endpoint
Section titled “Secure the endpoint”- Serve over HTTPS with a valid certificate.
- Do not log secrets or full payloads containing personal data.
- Use a dedicated path with no other behavior and return a generic body.
- Keep the
signing_secretin a secret manager, not in source control.
Monitor
Section titled “Monitor”Watch the dashboard delivery log or alert on repeated failures: if your endpoint is down for more than about 27 hours all retries are used up, after which you must replay manually.