Payment Webhooks
Receive real-time payment event notifications with HMAC-signed delivery
🔔 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"
}
}
Save thesecretimmediately — 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:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 second |
| 3 | 5 seconds |
| 4 | 25 seconds |
| 5 | ~2 minutes |
After 5 consecutive failures per event, 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 |
Updated 1 day ago
