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 #
- Extract
X-Webhook-SignatureandX-Webhook-Timestampfrom headers - Construct the signed payload:
${timestamp}.${raw_body} - Compute HMAC-SHA256 using your webhook
secret - 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 |