Skip to main content
This guide walks you through setting up a webhook endpoint to receive events from Fungies.

Quick Start

1

Create endpoint handler

Build an HTTP endpoint that accepts POST requests
2

Register in Dashboard

Add your endpoint URL in the Fungies Dashboard
3

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):
  1. Go to Fungies DashboardDevelopersWebhooks
  2. Click Create a webhook
  3. Configure your webhook:
FieldDescription
URLYour endpoint URL (must be HTTPS in production)
SecretA random string to sign events (save this securely)
EventsSelect which event types to receive
  1. 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

  1. Get the raw request body (before any JSON parsing)
  2. Compute HMAC-SHA256 using your webhook secret
  3. 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

IssueSolution
Not receiving eventsCheck your endpoint is publicly accessible and returns 2xx
Signature mismatchEnsure you’re using the raw body, not parsed JSON
Timeout errorsReturn 2xx faster, process events asynchronously
Duplicate eventsImplement idempotent handling using event IDs

Next Steps

Test Your Webhooks

Learn how to test webhooks locally before deploying to production