Webhook Troubleshooting Guide
Solutions to common webhook problems and debugging strategies.
Debugging Tools
Dashboard Delivery Attempts
The BlockEden dashboard provides powerful debugging tools:
- Navigate to Accept Payments → Webhooks
 - Click on an endpoint to view its delivery attempts
 - Expand any attempt to see:
Full request body sent by BlockEden Full response body from your server HTTP status code Error messages Response time Retry schedule 
 
Replay Failed Events
Don't worry if a webhook delivery fails - you can replay it:
- Find the failed attempt in the dashboard
 - Click Replay Event button
 - The event will be resent immediately
 - Check if it succeeds this time
 
Test Your Endpoint
Use the Test button to send a sample event:
- Click Test on your endpoint
 - A 
webhook.test.eventis sent with sample data - Verify your server receives and processes it correctly
 - Check server logs and dashboard for results
 
Common Issues
  Issue: "Invalid Signature" (401 Unauthorized)
Symptoms:
- Your endpoint returns HTTP 401
 - Logs show "Invalid signature" or "Unauthorized"
 - All webhook deliveries fail with signature errors
 
Cause 1: Using Parsed JSON Instead of Raw Body
This is the most common mistake. You must verify the signature against the raw request body, not the parsed JSON.
// <ion-icon name="close-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#ef4444"}}></ion-icon> WRONG - Body is already parsed and modified
app.use(express.json());
app.post('/webhooks', (req, res) => {
  const body = JSON.stringify(req.body); // This won't match!
  const signature = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  // Verification will fail
});
// <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> CORRECT - Use raw body
app.post('/webhooks',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const body = req.body; // Raw Buffer
    const signature = crypto
      .createHmac('sha256', secret)
      .update(body)
      .digest('hex');
    // Verification will succeed
  }
);
Cause 2: Wrong Secret
You may be using an old secret or the wrong endpoint's secret.
Solution:
- Go to dashboard → Click Rotate Secret on your endpoint
 - Copy the new secret immediately (it's only shown once)
 - Update your environment variable: 
WEBHOOK_SECRET=your_new_secret - Restart your server
 - Click Test to verify it works
 
Cause 3: Whitespace or Encoding Issues
Ensure you're using the exact raw bytes without modification.
// Correct approach
const signature = crypto
  .createHmac('sha256', secret)
  .update(req.body) // Must be Buffer, not modified string
  .digest('hex');
  Issue: Webhooks Timing Out
Symptoms:
- Dashboard shows "Timeout" errors
 - No response received after 30 seconds
 - Events are automatically retried
 
Cause 1: Endpoint Takes Too Long to Respond
Your handler is doing too much work synchronously.
// <ion-icon name="close-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#ef4444"}}></ion-icon> WRONG - Slow synchronous processing
app.post('/webhooks', async (req, res) => {
  const event = parseAndVerify(req);
  // This takes 60 seconds - too slow!
  await processOrder(event);
  await sendEmails(event);
  await updateAnalytics(event);
  res.status(200).send('OK'); // Too late - already timed out
});
// <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> CORRECT - Respond quickly, process async
app.post('/webhooks', async (req, res) => {
  const event = parseAndVerify(req);
  // Respond immediately (within 1 second)
  res.status(200).json({ received: true });
  // Process in background
  processWebhookAsync(event).catch(err => {
    console.error('Background processing error:', err);
    // Log to error tracking service
  });
});
async function processWebhookAsync(event) {
  // Heavy processing here
  await processOrder(event);
  await sendEmails(event);
  await updateAnalytics(event);
}
Cause 2: Endpoint Not Accessible
Your endpoint might be blocked by a firewall, behind a VPN, or using localhost.
Solutions:
Ensure endpoint is publicly accessible over the internet Use HTTPS (HTTP is not supported) Check cloud provider security groups allow inbound HTTPS For local testing, use ngrok: 
# Terminal 1: Start your server
npm start
# Terminal 2: Create secure tunnel
ngrok http 3000
# Output: Forwarding https://abc123.ngrok.io -> http://localhost:3000
# Use the ngrok HTTPS URL in BlockEden dashboard
  Issue: Missing Webhooks
Symptoms:
- Some events aren't received
 - Expected webhook never arrived
 - Inconsistent delivery
 
Cause 1: Not Subscribed to Event Type
You're not subscribed to that specific event type.
Solution:
- Go to dashboard → Edit your endpoint
 - Check the boxes for event types you want to receive
 - Click Save
 
Cause 2: Endpoint Disabled
The endpoint was automatically disabled due to repeated failures.
Solution:
- Check if endpoint shows "Disabled" badge in dashboard
 - Review failure reason in delivery attempts table
 - Fix the underlying issue (signature, timeout, etc.)
 - Click Edit → Toggle "Endpoint active" to re-enable
 - Click Test to verify it works
 
Cause 3: Event Already Sent
BlockEden doesn't send duplicate events - this is correct behavior.
Solution: Each event is sent once per endpoint. Check your server logs or database to confirm if you actually processed it.
  Issue: Duplicate Event Processing
Symptoms:
- Same order fulfilled twice
 - Customer charged multiple times
 - Duplicate database records
 
Cause: Not Implementing Idempotency
Your handler doesn't check if an event was already processed.
// <ion-icon name="close-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#ef4444"}}></ion-icon> WRONG - No duplicate check
async function handleWebhook(event) {
  // Process immediately without checking
  await fulfillOrder(event.data.paymentId);
}
// <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> CORRECT - Check before processing
async function handleWebhook(event) {
  // Check if already processed using event ID
  const existing = await db.webhookEvents.findOne({
    eventId: event.id
  });
  if (existing) {
    console.log('Event already processed:', event.id);
    return; // Skip duplicate
  }
  // Process the event
  await fulfillOrder(event.data.paymentId);
  // Mark as processed
  await db.webhookEvents.create({
    eventId: event.id,
    type: event.type,
    processedAt: new Date()
  });
}
  Issue: "Connection Refused" Errors
Symptoms:
- Dashboard shows "Connection refused"
 - Error message: "Cannot connect to endpoint"
 - Immediate failure (not timeout)
 
Cause 1: Server Not Running
Your webhook server is down or crashed.
Solution:
Check your server is running: systemctl status your-serviceVerify it's listening on the correct port Check server logs for startup errors Use process manager like PM2 or systemd for auto-restart 
Cause 2: Wrong URL or Port
The webhook URL doesn't match your actual endpoint.
Solution:
Verify URL in dashboard matches your server Check the path: /webhooksvs/webhookvs/api/webhooksEnsure using HTTPS (HTTP not supported) Test with curl: 
curl -X POST https://your-domain.com/api/webhooks \
  -H "Content-Type: application/json" \
  -d '{"test":"data"}'
Cause 3: Firewall Blocking Requests
Corporate firewall or cloud security group is blocking inbound traffic.
Solution:
Allow incoming HTTPS traffic (port 443) Check cloud provider security groups (AWS, GCP, Azure) Verify network ACLs Contact support for BlockEden IP ranges to whitelist 
  Issue: 500 Internal Server Error
Symptoms:
- Your endpoint returns HTTP 500
 - BlockEden automatically retries the event
 - Errors in your server logs
 
Cause 1: Unhandled Exception
Your code throws an error that crashes the request handler.
// <ion-icon name="close-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#ef4444"}}></ion-icon> WRONG - Unhandled errors crash the handler
app.post('/webhooks', async (req, res) => {
  const event = parseAndVerify(req);
  await processEvent(event); // If this throws, 500 is returned
  res.status(200).send('OK');
});
// <ion-icon name="checkmark-circle" style={{verticalAlign: "middle", fontSize: "1.2em", color: "#10b981"}}></ion-icon> CORRECT - Proper error handling
app.post('/webhooks', async (req, res) => {
  try {
    const event = parseAndVerify(req);
    // Always return 200 first (even if processing fails)
    res.status(200).json({ received: true });
    // Process with error handling
    await processEvent(event);
  } catch (error) {
    console.error('Webhook processing error:', error);
    // Log to error tracking service (Sentry, Datadog, etc.)
    // Don't throw - webhook was already acknowledged
  }
});
Cause 2: Database Connection Error
Can't connect to your database.
Solution:
Check database is running and accessible Verify connection string Check network connectivity Handle database errors gracefully: 
try {
  await db.orders.create(orderData);
} catch (dbError) {
  console.error('Database error:', dbError);
  // Still return 200 - retry won't help DB issues
}
  Issue: Events Out of Order
Symptoms:
- Receive 
payment.confirmedbeforepayment.pending - Events arrive in unexpected sequence
 - State transitions seem wrong
 
Cause: Network Delays or Retries
Events may arrive out of order due to network conditions or retries.
Solution: Use Status Field, Not Event Order
Don't rely on event arrival order. Always check the current status:
async function handlePaymentEvent(event) {
  const payment = event.data;
  // Get current status from your database
  const existingPayment = await db.payments.findOne({
    id: payment.paymentId
  });
  // Only update if new status is "higher" than current
  const statusPriority = {
    'pending': 1,
    'confirmed': 2,
    'failed': 3,
    'refunded': 4
  };
  if (statusPriority[payment.status] > statusPriority[existingPayment.status]) {
    await db.payments.update(payment.paymentId, {
      status: payment.status,
      updatedAt: new Date()
    });
  } else {
    console.log('Ignoring older event:', event.type);
  }
}
Best Practices
  1. Always Verify Signatures
Never skip signature verification:
const signature = req.headers['x-eden-signature'];
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
  return res.status(401).send('Unauthorized');
}
  2. Return 200 Within 1 Second
Acknowledge receipt immediately:
// Return 200 in < 1 second
res.status(200).json({ received: true });
// Process in background (can take minutes)
processAsync(event);
  3. Handle Idempotency
Prevent duplicate processing:
if (await alreadyProcessed(event.id)) {
  console.log('Duplicate event, skipping');
  return;
}
  4. Log Everything
Comprehensive logging helps debugging:
console.log('Webhook received:', {
  eventId: event.id,
  type: event.type,
  paymentId: event.data.paymentId,
  status: event.data.status,
  timestamp: new Date().toISOString()
});
  5. Monitor Failures
Set up alerts for webhook failures:
- Check dashboard regularly
 - Set up alerts for high failure rates (>10%)
 - Replay failed events after fixing issues
 - Monitor success rate badge on endpoints
 
  6. Handle Unknown Events
New event types may be added in the future:
switch (event.type) {
  case 'payment.confirmed':
    await handlePaymentConfirmed(event.data);
    break;
  case 'payment.failed':
    await handlePaymentFailed(event.data);
    break;
  default:
    // Log but don't fail for unknown event types
    console.log('Unknown event type:', event.type);
}