Skip to main content

Webhooks

Webhooks allow your application to receive real-time notifications when events occur in MAES Platform.

How Webhooks Work

MAES Platform                Your Server
│ │
│ 1. Event occurs │
│ (card enabled) │
│ │
│ 2. POST to your URL │
│ ─────────────────────────► │
│ with signed payload │
│ │
│ 3. 200 OK │
│ ◄───────────────────────── │
│ │

Webhook Events

EventDescription
card.createdNew card synced from MAES
card.activatedCard was activated
card.enabledFuel authorizations enabled
card.disabledFuel authorizations disabled
card.syncedCard data synced from MAES
sync.completedFull sync completed successfully
sync.failedSync operation failed
testTest event for verification

Webhook Payload

All events follow this format:

{
"id": "evt_1a2b3c4d5e6f",
"event": "card.enabled",
"created_at": "2025-12-27T16:24:05.712Z",
"data": {
"card_id": "09c9861c-4c4b-411f-be85-c16ed7e26da4",
"card_number": "782521009000153700",
"license_plate": "AB-123-CD",
"driver": "John Doe",
"status": "active",
"auth_gasoline": true,
"auth_diesel": true,
"auth_lpg": true,
"auth_heating_oil": true,
"environment": "production"
}
}

Signature Verification

All webhook requests include a signature header:

X-Webhook-Signature: t=1703693400,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Verification Steps

  1. Extract timestamp (t) and signature (v1) from the header
  2. Construct the signed payload: {timestamp}.{raw_body}
  3. Compute HMAC-SHA256 using your webhook secret
  4. Compare computed signature with v1
  5. Optionally verify timestamp to prevent replay attacks

Node.js Example

const crypto = require('crypto');

function verifyWebhookSignature(payload, signatureHeader, secret) {
const [timestampPart, signaturePart] = signatureHeader.split(',');
const timestamp = timestampPart.replace('t=', '');
const signature = signaturePart.replace('v1=', '');

const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');

// Timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex'),
);
}

// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body.toString();

if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

const event = JSON.parse(payload);
console.log('Received event:', event.event);

// Handle the event
switch (event.event) {
case 'card.enabled':
// Handle card enabled
break;
case 'card.disabled':
// Handle card disabled
break;
}

res.status(200).send('OK');
});

Retry Logic

Failed deliveries are retried with exponential backoff:

AttemptDelay After Failure
1Immediate
21 minute
35 minutes
430 minutes
52 hours

After 5 failed attempts, the delivery is marked as permanently failed.

Auto-Disable

Webhooks are automatically disabled after 10 consecutive failures. You can re-enable them from the dashboard.

Requirements

  • HTTPS only — Webhook URLs must use HTTPS
  • 200-299 response — Return a 2xx status to acknowledge receipt
  • Respond quickly — Process asynchronously if needed (< 30s timeout)

Best Practices

  1. Always verify signatures — Don't trust unverified payloads
  2. Process asynchronously — Queue events for processing
  3. Handle duplicates — Events may be delivered more than once
  4. Log everything — Keep records for debugging
  5. Return 200 quickly — Avoid timeouts