Payment Webhooks

Register webhook endpoints to receive real-time notifications when payments are completed, failed, refunded, or when subscriptions change.


Register a Webhook #

POST /api/v1/webhooks
Field Type Required Description
url string Yes HTTPS endpoint to receive events
events array No Event types to subscribe to (omit for all)
curl -X POST /api/v1/webhooks \
  -H "X-Client-ID: $CLIENT_ID" \
  -H "X-Client-Secret: $CLIENT_SECRET" \
  -d '{
    "url": "https://yourplatform.com/webhooks/audit1",
    "events": ["payment.completed", "payment.failed", "payment.refunded"]
  }'
{
  "ok": true,
  "data": {
    "id": "685abc123def456789012345",
    "url": "https://yourplatform.com/webhooks/audit1",
    "secret": "whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "events": ["payment.completed", "payment.failed", "payment.refunded"],
    "status": "active"
  }
}

Warning

Save the secret immediately — it's only shown once. You'll use it to verify webhook signatures.


Event Types #

Event Trigger
payment.completed Payment received (card or bank debit)
payment.failed Payment attempt failed
payment.refunded Refund processed
subscription.created New recurring subscription started
subscription.cancelled Subscription cancelled
installment.paid Installment payment received
installment.failed Installment payment failed

Webhook Payload #

Each delivery includes these headers:

Header Description
X-Webhook-Signature HMAC-SHA256 signature
X-Webhook-Timestamp Unix timestamp in milliseconds
X-Webhook-Event Event type (e.g., payment.completed)
X-Webhook-ID Unique event ID

Example payload body:

{
  "event_type": "payment.completed",
  "payment_id": "686abc123def456789012345",
  "employer_id": "681xyz789abc123456789012",
  "carrier_id": "680abc456def789012345678",
  "policy_id": "682def789ghi012345678901",
  "amount_cents": 150000,
  "payment_type": "down_payment",
  "completed_at": "2026-04-14T15:30:00.000Z"
}

Verifying Signatures #

Always verify the X-Webhook-Signature to confirm the event came from Audit1 and wasn't tampered with.

Verification Steps #

  1. Extract X-Webhook-Signature and X-Webhook-Timestamp from headers
  2. Construct the signed payload: ${timestamp}.${raw_body}
  3. Compute HMAC-SHA256 using your webhook secret
  4. Compare signatures (use constant-time comparison)

JavaScript #

const crypto = require("crypto");

function verifyWebhook(secret, signature, timestamp, body) {
  const payload = `${timestamp}.${body}`;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// In your webhook handler:
app.post("/webhooks/audit1", (req, res) => {
  const sig = req.headers["x-webhook-signature"];
  const ts = req.headers["x-webhook-timestamp"];

  if (!verifyWebhook(WEBHOOK_SECRET, sig, ts, req.rawBody)) {
    return res.status(401).send("Invalid signature");
  }

  // Process the event
  const event = req.body;
  console.log(`Received ${event.event_type}`);
  res.status(200).send("OK");
});

Python #

import hmac, hashlib

def verify_webhook(secret, signature, timestamp, body):
    payload = f"{timestamp}.{body}"
    expected = hmac.new(
        secret.encode(), payload.encode(), hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Retry Policy #

If your endpoint doesn't respond with 2xx within 10 seconds, we retry with exponential backoff. The first attempt fires immediately; subsequent attempts wait the delay below.

Attempt Delay before this attempt
1 — (immediate)
2 1 second
3 5 seconds
4 25 seconds
5 ~2 minutes (125 s)
6 ~10 minutes (625 s)

After 6 attempts per event (1 initial + 5 retries), we stop retrying that event.

After 10 consecutive failures across all events, the webhook subscription is automatically disabled. Re-enable it by deleting and re-registering.


Manage Webhooks #

List Webhooks #

GET /api/v1/webhooks

Delete a Webhook #

DELETE /api/v1/webhooks/{id}

Best Practices #

✓ Do ✗ Don't
Respond with 200 quickly, process async Block the response while doing heavy processing
Verify the HMAC signature on every event Trust events without signature verification
Handle duplicate events idempotently Assume each event is delivered exactly once
Use HTTPS endpoints only Use HTTP (we reject non-HTTPS URLs)
Log the X-Webhook-ID for debugging Ignore event IDs