Webhooks

Receive real-time notifications when events happen in your Expereon account.

Overview

Instead of polling the API for changes, configure webhook endpoints to receive HTTP POST requests whenever invoices, payments, or other resources change state.

Setting up a webhook

Create a webhook endpoint via the API or the Expereon dashboard:

curl -X POST "https://api.expereon.com/api/v1/settings/webhooks" \
  -H "X-API-Key: exp_prod_xxxxxxxxxxxx" \
  -H "X-API-Secret: your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/expereon",
    "events": ["invoice.created", "invoice.approved", "payment.settled"],
    "authMethod": "SIGNING_SECRET",
    "signingSecret": "whsec_your_secret_here"
  }'

Requirements

Events

EventDescription
invoice.createdA new invoice was created
invoice.submittedInvoice submitted to buyer for review
invoice.approvedBuyer approved the invoice
invoice.disputedBuyer raised a dispute on the invoice
invoice.cancelledInvoice was cancelled by either party
invoice.settledInvoice payment has been completed
payment.settledA payment was successfully processed
payment.failedA payment attempt failed
connection.createdNew business connection established
connection.suspendedA connection was suspended
withdrawal.approvedWithdrawal request was approved
withdrawal.rejectedWithdrawal request was rejected
refund.approvedRefund request was approved
refund.rejectedRefund request was rejected

Payload format

Every webhook delivery is a JSON POST request with this structure:

{
  "id": "evt_abc123def456",
  "type": "invoice.approved",
  "timestamp": "2024-03-15T14:30:00Z",
  "data": {
    "invoiceId": "inv_xyz789",
    "invoiceNumber": "INV-2024-001",
    "status": "APPROVED",
    "amount": "1500.00",
    "currency": "USD",
    "buyerOrganizationId": "org_buyer_1",
    "sellerOrganizationId": "org_seller_1"
  }
}

Signature verification

Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature of the request body using your signing secret.

Verifying signatures

// Node.js example
const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
app.post('/webhooks/expereon', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const isValid = verifyWebhook(req.rawBody, signature, 'whsec_your_secret');

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const event = req.body;
  switch (event.type) {
    case 'invoice.approved':
      // Handle approved invoice
      break;
    case 'payment.settled':
      // Handle settled payment
      break;
  }

  res.status(200).send('OK');
});
Always verify signatures Never process webhook payloads without verifying the signature first. This prevents attackers from sending fake events to your endpoint.

Retry policy

If your endpoint fails to respond with 2xx, Expereon retries with exponential backoff:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours

After 5 failed attempts, the delivery is marked as failed. You can view failed deliveries and manually retry them from the dashboard.

Testing webhooks

Use the test endpoint to send a sample event to your webhook:

curl -X POST "https://api.expereon.com/api/v1/settings/webhooks/{webhookId}/test" \
  -H "X-API-Key: exp_prod_xxxxxxxxxxxx" \
  -H "X-API-Secret: your-secret-key"

This sends a test event with "type": "webhook.test" to your endpoint. Use this to verify your handler and signature validation work correctly.

Best practices