Testing webhooks can be challenging since they’re triggered by external events. This guide provides comprehensive strategies for testing your webhook implementations during development, staging, and production phases.
Testing strategies overview
Effective webhook testing involves multiple approaches:
Local development Test on your development machine with tunneling tools
Staging environment Test in production-like environment before going live
Production monitoring Verify real-world performance with actual events
Automated testing Include webhooks in your continuous integration pipeline
Local development testing
SDK Installation : Install the Rise SDK for secure webhook testing: npm install @riseworks/sdk
Setting up a local webhook endpoint
First, create a simple webhook endpoint for testing:
test-webhook.js
test_webhook.py
import express from 'express' ;
import { WebhookValidator } from '@riseworks/sdk' ;
const app = express ();
app . use ( express . raw ({ type: 'application/json' }));
// Create validator with test secret
const validator = new WebhookValidator ({
secret: process . env . RISE_WEBHOOK_SECRET || 'test-secret' ,
tolerance: 600 // 10 minutes
});
app . post ( '/webhook' , async ( req , res ) => {
const signature = req . headers [ 'x-rise-signature' ];
console . log ( '📨 Webhook received:' );
console . log ( 'Headers:' , req . headers );
console . log ( 'Body:' , req . body . toString ());
try {
// Verify signature and get validated event
const event = validator . validateEvent ( req . body , signature );
console . log ( 'Success: Signature valid' );
console . log ( 'Event details:' );
console . log ( '- Type:' , event . event_type );
console . log ( '- ID:' , event . id );
console . log ( '- Created:' , new Date ( event . created * 1000 ));
res . status ( 200 ). json ({ received: true });
} catch ( error ) {
console . error ( 'Error: Error processing webhook:' , error . message );
res . status ( 400 ). send ( 'Error processing webhook' );
}
});
const port = process . env . PORT || 3000 ;
app . listen ( port , () => {
console . log ( `Webhook test server running on port ${ port } ` );
console . log ( `Webhook URL: http://localhost: ${ port } /webhook` );
});
Making your local endpoint accessible
Use tunneling tools to expose your local server to the internet:
ngrok (Recommended)
localtunnel
Cloudflare Tunnel
Installation and usage: # Install ngrok
npm install -g ngrok
# Start your webhook server
node test-webhook.js
# In another terminal, expose it
ngrok http 3000
Benefits:
Reliable and stable tunnels
HTTPS support included
Custom subdomain options (paid plans)
Web interface for monitoring requests
Example output: Forwarding https://abc123.ngrok.io -> http://localhost:3000
Installation and usage: # Install localtunnel
npm install -g localtunnel
# Start your webhook server
node test-webhook.js
# In another terminal, create tunnel
lt --port 3000 --subdomain my-webhook-test
Benefits:
Free and open source
Custom subdomain support
Simple setup
Note: Less reliable than ngrok for production testingInstallation and usage: # Install cloudflared
# Download from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/
# Start tunnel
cloudflared tunnel --url http://localhost:3000
Benefits:
Built-in DDoS protection
Global CDN
No bandwidth limits
Requirements: Cloudflare account recommended for custom domains
Using Rise’s test webhook feature
The Rise app includes a built-in webhook testing tool:
Access test feature
Navigate to your webhook details page in the Rise app and click the “Test Webhook” button
Select event type
Choose an event type to simulate from the dropdown list (e.g., payment.sent, payment.group.created)
Choose version (optional)
If multiple event versions are available, select the version you want to test
Send test event
Click “Send Test Event” to deliver a test payload to your endpoint
Verify results
Check both the Rise app interface and your server logs to confirm the test was successful
Interpreting test results
The test interface shows:
Response status HTTP status code returned by your endpoint (should be 200-299)
Response time How long your endpoint took to respond (should be under 10 seconds)
Response body What your endpoint returned (helpful for debugging)
Error details Any connection or processing errors that occurred
Testing checklist
Before deploying your webhook implementation, verify all these aspects:
Functionality tests
Success: Webhook receives and processes events correctly
Success: Returns 200 status code within 10 seconds
Success: Handles all subscribed event types properly
Success: Logs events appropriately for debugging
Success: Processes business logic correctly
Success: Signature verification works for valid signatures
Success: Invalid signatures are properly rejected with 400 status
Success: Timestamp validation prevents replay attacks
Success: HTTPS is recommended for production environments
Success: Webhook secrets are stored securely
Success: Unknown event types are handled gracefully
Success: Malformed payloads don’t crash the application
Success: Network timeouts are handled appropriately
Success: Database errors don’t fail webhook processing
Success: External API failures are handled gracefully
Idempotency testing
Test that your webhook can safely handle duplicate events using the idempotency_key field:
// Test idempotency with duplicate events
async function testIdempotency () {
const testEvent = {
id: 'we-test123' ,
object: 'event' ,
created: Math . floor ( Date . now () / 1000 ),
event_type: 'payment.sent' ,
event_version: '1.0' ,
request_id: 'req_test789' ,
idempotency_key: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' , // UUID for duplicate detection
payment: {
nanoid: 'pa-test123' ,
amount: '1000000' ,
currency: 'USD' ,
// ... other fields
}
};
// Send the same event multiple times
for ( let i = 0 ; i < 3 ; i ++ ) {
const response = await fetch ( 'http://localhost:3000/webhook' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-Rise-Signature' : generateTestSignature ( testEvent )
},
body: JSON . stringify ( testEvent )
});
console . log ( `Attempt ${ i + 1 } : ${ response . status } ` );
}
// Verify only one payment was processed using idempotency_key
const processedEvents = await database . count ( 'processed_webhooks' , {
idempotency_key: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
});
console . log ( `Event processed ${ processedEvents } times (should be 1)` );
}
// Example idempotency handler implementation
function processWebhook ( event ) {
const idempotencyKey = event . idempotency_key ;
// Check if already processed using idempotency_key
const existingRecord = database . findByIdempotencyKey ( idempotencyKey );
if ( existingRecord ) {
console . log ( `Event ${ idempotencyKey } already processed, skipping` );
return { processed: false , status: "duplicate" };
}
// Process the event
database . saveEvent ({
idempotency_key: idempotencyKey ,
event_type: event . event_type ,
processed_at: new Date (),
event_data: event
});
// Trigger business logic
handleBusinessLogic ( event );
return { processed: true , status: "success" };
}
Monitoring test webhook deliveries
Once your webhook is active, track its performance in the Rise app:
Navigate to webhook details
Go to your webhook details page in the Rise app
Access delivery history
Click on the “Delivery History” tab to see all webhook deliveries
Monitor key metrics
Track delivery success rates, response times, and error patterns
Set up alerts
Configure monitoring alerts for high failure rates or slow response times
Key metrics to track
Success rate Target : >99%Percentage of successful first-attempt deliveries
Response time Target : <2 secondsAverage time for your endpoint to respond
Error rate Target : <1%Percentage of deliveries that ultimately fail
The delivery history shows:
Successful deliveries - Events delivered with 200 response
Failed deliveries - Events that couldn’t be delivered
Pending retries - Events waiting to be retried
Response details - HTTP status codes and response times
What’s next?
Now that you can test your webhooks effectively:
Testing tip : Test early, test often, and test everything. A well-tested webhook implementation prevents production surprises and ensures a reliable integration experience.