Loading...
Loading...
> Build a production-grade webhook system with 22 event types, HMAC-SHA256 verification, and automatic retries.
22 webhook event types across 5 categories, HMAC-SHA256 signature verification, automatic retry with exponential backoff, delivery tracking and status monitoring, per-organization webhook management.
Use src/lib/webhooks/server.ts to trigger events
1import { triggerWebhook } from "@/lib/webhooks/server";2import { WEBHOOK EVENTS } from "@/lib/webhooks/events";34// Trigger when a member is added5await triggerWebhook(6 organizationId,7 WEBHOOK EVENTS.ORG_MEMBER_ADDED,8 {9 memberId: "mem_123",10 userId: "user_456",11 email: "newmember@example.com",12 role: "MEMBER"13 }14);
Verify webhook signatures in your receiver
1import crypto from "crypto";23export async function POST(request: Request) {4 const payload = await request.text();5 const signature = request.headers.get("X-Webhook-Signature");67 const secret = process.env.WEBHOOK_SECRET;89 const expectedSignature = crypto10 .createHmac("sha256", secret)11 .update(payload)12 .digest("hex");1314 const isValid = crypto.timingSafeEquals(15 Buffer.from(signature || ""),16 Buffer.from(expectedSignature)17 );1819 if (!isValid) {20 return new Response("Invalid signature", { status: 401 });21 }2223 const data = JSON.parse(payload);24 return new Response("OK", { status: 200 });25}
Use ngrok to test webhooks locally
1$# Expose local server2$ngrok http 300034$# Trigger a test webhook5$curl -X POST http://localhost:3000/api/test-webhook \6$ -H "Content-Type: application/json" \7$ -d '{"event": "payment.succeeded"}'
X-Webhook-Signature - HMAC-SHA256 signatureX-Webhook-Event - Event typeX-Webhook-Delivery-ID - Unique delivery IDX-Webhook-Retry - Retry attempt numberUser-Agent - Fabrk-Webhooks/1.0[ERROR]: Signature verification failed (401 Unauthorized)
Solution: Verify WEBHOOK_SECRET matches between sender and receiver
# Generate a shared secret
openssl rand -hex 32
# Add to both sender and receiver .env.local
WEBHOOK_SECRET="your-generated-secret"
# Sender uses it to sign, receiver uses it to verify[ERROR]: Webhook endpoint not receiving events
Solution: Test webhook URL is accessible
# For local testing, expose with ngrok
ngrok http 3000
# Use the ngrok URL in webhook configuration
https://abc123.ngrok.io/api/webhooks/your-endpoint
# Verify endpoint responds
curl https://abc123.ngrok.io/api/webhooks/your-endpoint[ERROR]: Duplicate events being processed
Solution: Implement idempotency check using delivery ID
// Store processed delivery IDs
const deliveryId = request.headers.get("X-Webhook-Delivery-ID");
// Check if already processed
const existing = await prisma.webhookDelivery.findUnique({
where: { deliveryId }
});
if (existing) {
return new Response("Already processed", { status: 200 });
}[ERROR]: Webhook timing out (504 Gateway Timeout)
Solution: Process webhook asynchronously, respond immediately
// Don't process synchronously
export async function POST(request: Request) {
// Validate signature first
const isValid = verifySignature(request);
if (!isValid) return new Response("Invalid", { status: 401 });
// Queue for background processing
await queueWebhookProcessing(request.body);
// Respond immediately
return new Response("OK", { status: 200 });
}