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

Sandbox webhooks

You can register a webhook using a sandbox API key against sandbox.expereon.com the same way you would in production.

Current limitation: no per-environment isolation Webhook endpoints are configured per key/URL, not scoped to an environment field - this is not yet modeled. The sandbox reset endpoint (POST /api/v1/settings/api-keys/sandbox/reset) does not remove your webhook registrations; it only clears sandbox API keys, sandbox logs, sandbox invoices, and sandbox bookings.

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