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.event
is 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.
// ❌ 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
});
// ✅ 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.
// ❌ 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
});
// ✅ 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.
// ❌ WRONG - No duplicate check
async function handleWebhook(event) {
// Process immediately without checking
await fulfillOrder(event.data.paymentId);
}
// ✅ 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-service
- ✅ Verify 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:
/webhooks
vs/webhook
vs/api/webhooks
- ✅ Ensure 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.
// ❌ 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');
});
// ✅ 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.confirmed
beforepayment.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);
}
}