Why Webhooks?
Webhooks are essential for production applications because:
No Polling Get instant notifications instead of checking every few seconds
Efficient Save bandwidth and reduce server load
Real-time Know immediately when settlement completes
Reliable Automatic retries if your endpoint is down
How Webhooks Work
Step 1: Provide Webhook URL
Include webhook_url when verifying payment:
const response = await fetch ( "https://facilitator.api.0xmeta.ai/v1/verify" , {
method: "POST" ,
headers: {
"Content-Type" : "application/json" ,
"Idempotency-Key" : `verify_ ${ orderId } ` ,
},
body: JSON . stringify ({
transaction_hash: txHash ,
chain: "base" ,
seller_address: sellerAddress ,
expected_amount: amount ,
webhook_url: "https://your-app.com/webhooks/settlement" , // Add this!
}),
});
Step 2: Create Webhook Endpoint
Node.js + Express
Python + Flask
import express from "express" ;
import crypto from "crypto" ;
const app = express ();
app . use ( express . json ());
// Webhook endpoint
app . post ( "/webhooks/settlement" , ( req , res ) => {
const signature = req . headers [ "x-webhook-signature" ];
const payload = req . body ;
// 1. Verify signature FIRST
if ( ! verifySignature ( payload , signature )) {
console . error ( "Invalid webhook signature" );
return res . status ( 401 ). json ({ error: "Invalid signature" });
}
// 2. Acknowledge receipt immediately
res . status ( 200 ). json ({ received: true });
// 3. Process webhook asynchronously
processWebhook ( payload ). catch ( console . error );
});
function verifySignature ( payload , signature ) {
// Get from environment
const secret = process . env . WEBHOOK_SECRET ;
// Serialize payload (same way 0xmeta.ai does)
const payloadStr = JSON . stringify ( payload , Object . keys ( payload ). sort ());
// Compute expected signature
const expected = crypto
. createHmac ( "sha256" , secret )
. update ( payloadStr )
. digest ( "hex" );
// Constant-time comparison
return crypto . timingSafeEqual ( Buffer . from ( signature ), Buffer . from ( expected ));
}
async function processWebhook ( payload ) {
const { event_type , settlement_id , status , data } = payload ;
if ( event_type === "settlement.completed" && status === "settled" ) {
// Update order in database
await db . orders . update ({
where: { settlement_id },
data: {
status: "completed" ,
settlement_tx_hash: data . settlement_tx_hash ,
settled_at: new Date (),
},
});
// Send confirmation email
await sendConfirmationEmail ( settlement_id );
console . log ( "Settlement completed:" , settlement_id );
}
}
app . listen ( 3000 );
Step 3: Test Locally with ngrok
# Install ngrok
npm install -g ngrok
# Start your server
node server.js # Port 3000
# In another terminal, start ngrok
ngrok http 3000
# Output will show:
# Forwarding: https://abc123.ngrok.io -> http://localhost:3000
# Use the ngrok URL in your API request
{
"webhook_url" : "https://abc123.ngrok.io/webhooks/settlement"
}
Webhook Payload
When settlement completes, you’ll receive:
{
"event_type" : "settlement.completed" ,
"event_id" : "evt_abc123def456" ,
"timestamp" : "2025-01-15T10:35:00Z" ,
"verification_id" : "ver_abc123" ,
"settlement_id" : "set_xyz789" ,
"status" : "settled" ,
"data" : {
"settlement_tx_hash" : "0xabcdef..." ,
"settled_amount" : "1000000000000000000" ,
"fee" : "50000000000000000"
},
"signature" : "hmac_sha256_signature_here"
}
Event Types
Event Type Description When verification.completedVerification succeeded Payment verified verification.failedVerification failed Invalid payment settlement.completedSettlement succeeded Funds transferred settlement.rejectedSettlement rejected Settlement failed
Security Checklist
Verify Signatures
ALWAYS verify the HMAC signature before processingif ( ! verifySignature ( payload , signature )) {
return res . status ( 401 ). send ();
}
Use HTTPS
Webhook URLs must use HTTPS in production ❌ http://your-app.com/webhooks ✅
https://your-app.com/webhooks
Return 200 Quickly
Acknowledge receipt within 5 seconds // Do this ✅
res . status ( 200 ). json ({ received: true });
processWebhook ( payload ); // Async
// Not this ❌
await longRunningProcess (); // Takes 30s
res . status ( 200 ). send ();
Handle Idempotency
Store event IDs to prevent duplicate processing const processedEvents = new Set ();
if ( processedEvents . has ( payload . event_id )) {
return res . status ( 200 ). json ({ received: true });
}
processedEvents . add ( payload . event_id );
processWebhook ( payload );
Retry Logic
If your endpoint fails, 0xmeta.ai retries with exponential backoff:
Attempt Delay 1 Immediate 2 1 minute 3 5 minutes 4 15 minutes 5 30 minutes 6 1 hour
After 6 attempts, webhook is marked as failed.
Production Deployment
1. Environment Variables
# .env
WEBHOOK_SECRET = your_webhook_secret_here
WEBHOOK_URL = https://your-app.com/webhooks/settlement
2. Load Balancer Configuration
If behind a load balancer, ensure:
Timeout is > 10 seconds
Keep-alive enabled
Proper header forwarding
3. Monitor Webhooks
// Log all webhook deliveries
app . post ( "/webhooks/settlement" , ( req , res ) => {
console . log ( "Webhook received:" , {
event_id: req . body . event_id ,
event_type: req . body . event_type ,
timestamp: new Date (),
});
// ... verify and process
});
// Alert on failures
if ( failedWebhooks > 5 ) {
await alertTeam ( "Multiple webhook failures detected" );
}
Common Issues
Check :
Is URL accessible? curl -X POST https://your-url/webhooks
Is it HTTPS? (required in production)
Is firewall blocking?
Check webhook logs in dashboard (if available)
Signature verification fails
Fix : 1. Ensure you’re using correct secret 2. Serialize payload the same
way (sorted keys, no spaces) 3. Use constant-time comparison 4. Check for
encoding issues
Fix : 1. Return 200 immediately 2. Process async 3. Don’t do long
operations in webhook handler
Fix :// Store event IDs
const processed = await db . webhook_events . findOne ({
event_id: payload . event_id
});
if ( processed ) {
return res . status ( 200 ). json ({ received: true });
}
Testing Checklist
Never skip signature verification! Anyone can POST to your webhook URL.
Use webhooks instead of polling for 100x better performance and user
experience.