Skip to main content

Setting Up Webhooks

Webhooks notify your application in real-time when events occur, eliminating the need for polling.

Step 1: Create a Webhook Endpoint

Create an endpoint in your application to receive webhook events:

Express.js

import express from 'express';
import { MaesClient, MaesWebhookSignatureError } from '@nuvoni/maes-sdk';

const app = express();
const client = new MaesClient({ apiKey: process.env.MAES_API_KEY! });

// Use raw body for signature verification
app.post(
'/webhook/maes',
express.raw({ type: 'application/json' }),
(req, res) => {
try {
const event = client.webhooks.verifySignature(
req.body.toString(),
req.headers['x-webhook-signature'] as string,
process.env.WEBHOOK_SECRET!,
);

// Handle the event
handleWebhookEvent(event);

// Respond quickly with 200
res.status(200).send('OK');
} catch (error) {
if (error instanceof MaesWebhookSignatureError) {
console.error('Invalid signature:', error.message);
return res.status(401).send('Invalid signature');
}
throw error;
}
},
);

async function handleWebhookEvent(event) {
console.log(`Received event: ${event.event}`);

switch (event.event) {
case 'card.enabled':
console.log('Card enabled:', event.data.card_id);
break;
case 'card.disabled':
console.log('Card disabled:', event.data.card_id);
break;
case 'card.activated':
console.log('Card activated:', event.data.card_id);
break;
case 'sync.completed':
// Sync from MAES completed - contains ALL cards for this environment
// Note: Sandbox webhook receives sandbox cards, Production webhook receives production cards
console.log(`Sync completed (${event.data.environment}): ${event.data.cards_created} created, ${event.data.cards_updated} updated`);

// Cards array contains ALL cards for this environment - replace your local DB
if (event.data.cards) {
// Replace all cards for this environment (recommended for full sync)
await db.cards.deleteMany({
project_id: projectId,
environment: event.data.environment,
});
await db.cards.createMany({ data: event.data.cards });

console.log(`Replaced ${event.data.cards.length} ${event.data.environment} cards in local DB`);
}
break;
case 'sync.failed':
// Sync failed - log and alert
console.error(`Sync failed: ${event.data.error}`);
await notifyAdmin('MAES sync failed', event.data);
break;
}
}

app.listen(3000);

Step 2: Deploy Your Endpoint

Your webhook endpoint must be:

  • Publicly accessible (not localhost)
  • HTTPS only (required)
  • Fast response (< 30 seconds)

For local testing, you can use:

# Using ngrok
ngrok http 3000

Step 3: Register the Webhook

Via Dashboard

Webhooks are configured separately for Sandbox and Production environments:

  1. Go to your project at https://maes-platform.nuvoni.eu
  2. Open Webhooks tab
  3. Select the environment (Sandbox or Production)
  4. Click "Configure Webhook"
  5. Enter your endpoint URL
  6. Select events to subscribe to
  7. Click "Save"
  8. Copy the secret — shown only once!
Environment Separation
  • Sandbox webhook only receives events for sandbox/test cards
  • Production webhook only receives events for production/real cards
  • Each environment has its own URL and secret

This ensures your test environment doesn't mix with production data.

Step 4: Test Your Webhook

Send a Test Event

Use the dashboard to send a test event, or use the API:

// The test event will be sent to your configured webhook URL
// Check the Webhooks tab in the dashboard for delivery status

Handling Events

Event Structure

interface WebhookEvent {
id: string;
event: string;
created_at: string;
data: {
environment: 'sandbox' | 'production'; // Always included
card_id?: string;
card_number?: string;
status?: string;
// ... other fields
};
}
note

The environment field is always included in the payload, indicating whether the event is for sandbox or production data.

Idempotency

Events may be delivered more than once. Use event.id for deduplication:

const processedEvents = new Set<string>();

async function handleWebhookEvent(event) {
if (processedEvents.has(event.id)) {
console.log(`Already processed event ${event.id}`);
return;
}

processedEvents.add(event.id);

// Process the event...
}

For production, store processed event IDs in a database.

Webhook Signature Verification

The SDK handles signature verification for you:

import { MaesClient, MaesWebhookSignatureError } from '@nuvoni/maes-sdk';

const client = new MaesClient({ apiKey: 'sk_live_xxxxx' });

try {
const event = client.webhooks.verifySignature(
rawBody, // Raw request body as string
signatureHeader, // X-Webhook-Signature header
webhookSecret, // Your webhook secret
);

// Event is verified and safe to process
console.log('Verified event:', event.event);
} catch (error) {
if (error instanceof MaesWebhookSignatureError) {
// Invalid signature - reject the request
console.error('Invalid signature');
}
}

Troubleshooting

Webhook Not Receiving Events

  1. Check the URL is publicly accessible
  2. Verify HTTPS is working
  3. Check firewall rules
  4. Review delivery history in dashboard

Signature Verification Failing

  1. Use the raw request body (not parsed JSON)
  2. Ensure you're using the correct secret
  3. Check for encoding issues

Missing Events

  1. Verify you've subscribed to the event type
  2. Check if webhook is enabled
  3. Review delivery history for failures