API Webhooks
Consume signed journey events and reduce borrower-side polling.
The API Experience supports webhook-driven lifecycle updates for borrower journeys. Use webhooks to keep backend systems in sync with offers, step progression, and state changes without relying exclusively on app polling.
Event model
Current consumer-facing event type:
journey.snapshot
The snapshot event contains the latest borrower journey representation that your backend can store, index, or use to drive downstream workflows.
Delivery headers
X-AL-Event-IDX-AL-Tenant-IDX-AL-Application-IDX-AL-Webhook-TimestampX-AL-Webhook-Signature
Payload shape
{
"schema_version": "1.0.0",
"event_type": "journey.snapshot",
"event_id": "evt_01JB2X4CE6",
"event_at": "2026-03-05T16:48:20.000Z",
"snapshot_generated_at": "2026-03-05T16:48:20.000Z",
"sequence": 1741179022100,
"issuer": {
"source": "credit-api",
"trigger": "journey.offer.updated"
},
"tenant_id": "8b6a3eb1-ffcb-4094-9f7d-2802c2cb5a61",
"application_id": "fbb1fd74-c502-4419-83f3-82a2a019fc9a",
"borrower": {
"borrowerExternalID": "CUST_0012",
"journeys": [
{
"journeyID": "b7bb465b-135e-4ffd-9c7f-0d43f7ee43d9",
"status": "OFFERS_AVAILABLE",
"nextAction": "VIEW_OFFERS"
}
]
}
}Verification
Verify the signature before processing the payload. Keep the raw request body and compare a computed HMAC digest against X-AL-Webhook-Signature.
Example in Node.js:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWebhookSignature(
rawBody: string,
timestamp: string,
signature: string,
secret: string,
) {
const signedPayload = `${timestamp}.${rawBody}`;
const digest = createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
return timingSafeEqual(
Buffer.from(digest, "utf8"),
Buffer.from(signature, "utf8"),
);
}Use the raw body
Do not JSON-stringify a parsed object and verify that new string. Signature verification should run against the exact raw request body bytes that were delivered.
Recommended receiver behavior
- Return
2xxonly after the event is accepted for processing. - Deduplicate using
event_id. - Persist
sequenceandsnapshot_generated_atfor ordering and replay safety. - Correlate events using
journeyID,borrowerExternalID, or your partner-side borrower mapping. - Update your own cached journey record so the borrower app can fetch backend-curated state if needed.
Polling vs webhooks
Use both, but for different purposes:
- Polling is best for the screen the borrower is actively waiting on.
- Webhooks are best for your backend, analytics systems, alerting, and asynchronous catch-up.
Recommended borrower polling intervals:
- Active operations: 2-5 seconds
- Passive waiting: 10-20 seconds
Failure handling
- Reject malformed payloads with a non-2xx status.
- Log
X-AL-Event-ID,X-AL-Tenant-ID, andX-AL-Application-ID. - Do not let signature failures silently pass.
- Store a replay-safe audit trail for compliance-sensitive borrower actions.