This guide walks you through setting up a webhook endpoint to receive events from Fungies.
Quick Start
Create endpoint handler
Build an HTTP endpoint that accepts POST requests
Register in Dashboard
Add your endpoint URL in the Fungies Dashboard
Verify signatures
Validate that requests are from Fungies
1. Create Your Endpoint Handler
Your webhook endpoint needs to:
Accept POST requests with a JSON body
Return a 2xx status code quickly (before any heavy processing)
Handle the Event object payload
Return your 2xx response immediately, then process the event asynchronously. This prevents timeouts on long-running operations.
Basic Handler Example
// This example uses Express to receive webhooks
import express from "express" ;
const app = express ();
app . post ( '/webhook' , express . json ({ type: 'application/json' }), ( request , response ) => {
const event = request . body ;
// Handle the event
switch ( event . type ) {
case 'payment_success' :
// handlePaymentSuccess(event.data);
break ;
case 'payment_refund' :
// handlePaymentRefund(event.data);
break ;
// ... handle other event types
default :
console . log ( `Unhandled event type ${ event . type } ` );
}
response . json ({ success: true });
});
app . listen ( 8081 , () => console . log ( 'Running on port 8081' ));
2. Register in the Fungies Dashboard
Once your endpoint is ready (or you’re using a tool like ngrok for local development):
Go to Fungies Dashboard → Developers → Webhooks
Click Create a webhook
Configure your webhook:
Field Description URL Your endpoint URL (must be HTTPS in production) Secret A random string to sign events (save this securely) Events Select which event types to receive
Click Save
You can create multiple webhooks for different event types, or use a single endpoint that handles all events.
3. Verify Webhook Signatures
Never skip signature verification in production. Without it, anyone could send fake events to your endpoint.
Fungies signs every webhook payload using HMAC-SHA256 with your webhook secret. The signature is included in the x-fngs-signature header:
x-fngs-signature: sha256_6808ed5be1262b60818359fa586145810d0793e8a677f1326520d3844e21b640
How to Verify
Get the raw request body (before any JSON parsing)
Compute HMAC-SHA256 using your webhook secret
Compare with the signature in the header
Verification Function
// This example shows standalone function to verify webhook event signature
import crypto from "crypto" ;
const verifyWebhookEventSignature = ( payload : Buffer , signature : string , secret : string ) => {
const hmac = crypto . createHmac ( "sha256" , secret );
return `sha256_ ${ hmac . update ( payload . toString ()). digest ( "hex" ) } ` === signature ;
};
Express Middleware Example
// This example uses Express to receive webhooks
import express , { Response , Request } from "express" ;
import crypto from "crypto" ;
const WEBHOOK_SECRET = "<YOURE_SECRET_HERE>" ;
const app = express ();
const verifyWebhookEventSignature = ( request : Request , response : Response , buf : Buffer , encoding : string ) => {
const signature = request . header ( 'x-fngs-signature' );
const hmac = crypto . createHmac ( "sha256" , WEBHOOK_SECRET );
const calcSignature = `sha256_ ${ hmac . update ( buf . toString ( encoding )). digest ( "hex" ) } ` ;
request [ "signature_match" ] = ( calcSignature === signature );
};
app . post ( '/webhook' , express . json ({
type: 'application/json' ,
verify: verifyWebhookEventSignature
}), ( request , response ) => {
if ( ! request [ "signature_match" ]) {
return response . status ( 403 ). send ( 'signature mismatch' );
}
// Do something with the event
// ...
});
app . listen ( 8081 , () => console . log ( 'Running on port 8081' ));
Common Patterns
Idempotent Event Handling
Since webhooks guarantee at-least-once delivery, your handler might receive the same event multiple times. Use the event ID to prevent duplicate processing:
app . post ( '/webhook' , async ( req , res ) => {
const event = req . body ;
// Check if we've already processed this event
const processed = await db . events . findById ( event . id );
if ( processed ) {
return res . status ( 200 ). send ( 'Already processed' );
}
// Process the event
await handleEvent ( event );
// Mark as processed
await db . events . create ({ id: event . id , processedAt: new Date () });
res . status ( 200 ). send ( 'OK' );
});
Async Processing with Queues
For complex operations, queue events for background processing:
app . post ( '/webhook' , async ( req , res ) => {
// Respond immediately
res . status ( 200 ). send ( 'Received' );
// Queue for async processing
await queue . add ( 'process-webhook' , req . body );
});
Troubleshooting
Issue Solution Not receiving events Check your endpoint is publicly accessible and returns 2xx Signature mismatch Ensure you’re using the raw body, not parsed JSON Timeout errors Return 2xx faster, process events asynchronously Duplicate events Implement idempotent handling using event IDs
Next Steps
Test Your Webhooks Learn how to test webhooks locally before deploying to production