Async delivery

Webhooks

Webhooks push events to a URL you register so your app doesn't have to poll. Every delivery is signed with HMAC-SHA256 using the secret returned when you created the webhook — verify it before trusting any payload.

Available events

EventWhen it fires
loan.application.submittedFired when a customer completes a loan application through your integration.
loan.application.approvedFired on partner-lender approval.
loan.application.disbursedFired when funds hit the customer's bank.
kyc.verification.completedFired after KYC PAN+Aadhaar hard match completes.
insurance.policy.issuedFired when the insurer issues the policy document.
payment.successFired for a successful EMI or subscription collection.
rate_limit.hitFired the moment your key hits the per-minute burst ceiling.
quota.exhaustedFired the moment your monthly quota is fully consumed.

Payload shape

Every event uses this envelope; the `data` object varies per event and mirrors the underlying resource.

{
  "event": "loan.application.approved",
  "id": "evt_9K3f...",
  "occurred_at": "2026-02-01T12:34:56Z",
  "data": {
    "application_id": "app_ab12cd34",
    "customer_id": "cus_5F8k9",
    "product": "personal_loan",
    "sanctioned_amount": 850000,
    "partner_lender": "hdfc_bank"
  }
}

Verifying the signature

Every request carries an X-L4B-Signature: sha256=<hex> header. Compute HMAC-SHA256 over the raw request body using your webhook secret and constant-time compare.

Node.js
import crypto from "crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody, "utf8")
    .digest("hex");
  // constant-time compare
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader.replace(/^sha256=/, ""))
  );
}

app.post("/webhooks/l4b", (req, res) => {
  if (!verify(req.rawBody, req.get("X-L4B-Signature"), process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("bad signature");
  }
  // handle req.body.event
  res.status(200).send("ok");
});
Python
import hmac, hashlib

def verify(raw_body: bytes, sig_header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    provided = sig_header.replace("sha256=", "")
    return hmac.compare_digest(expected, provided)
Always verify against the raw request body — not a JSON re-serialisation. Any whitespace change breaks the HMAC.

Retry semantics

  • Any non-2xx response (or a timeout > 10s) is treated as a failed delivery.
  • Failed deliveries retry with exponential backoff: 30s → 2m → 10m → 1h → 6h → 12h → 24h.
  • After 7 attempts spanning ~44 hours the delivery is marked failed_permanent and the webhook is auto-disabled.
  • Retries are idempotent — use the top-level id field to de-dupe on your side.

Test a webhook locally

The dashboard Test event button dispatches a stub payload for the event of your choice — perfect for wiring up your endpoint before events flow in production.

# Or fire it from the CLI
curl -X POST 'https://api.loan4bank.com/v3/marketplace/developers/webhooks/<id>/test' \
  -H 'X-Dev-Session: <session>' \
  -H 'Content-Type: application/json' \
  -d '{"event":"loan.application.submitted"}'

Everything you need to build a rock-solid webhook integration

LOAN4BANK webhooks push real-time notifications for every material event on your account — loan applications submitted and approved, KYC completed, EMI paid or missed, gold-loan prices refreshed, wallet ledger movements, referral commissions accrued, corporate gift redemptions and more. You subscribe to the events you care about, we sign every payload with HMAC-SHA256, and your endpoint decides how to react. Because webhooks are push-based they eliminate the need for polling loops, drop end-to-end latency to under a second, and keep your infrastructure costs predictable as your customer base grows.

Signature verification is not optional. Every request carries an `X-L4B-Signature` header whose value is the hex-encoded HMAC-SHA256 of the raw request body using your endpoint's signing secret. Compute the digest yourself and compare in constant time using your language's `hmac.compare_digest` (or equivalent) helper — never string equality, which is vulnerable to timing attacks. If the signature does not match, respond with 401 and log the incident; a well-behaved endpoint will only ever parse the body after the signature check succeeds.

Because networks fail, we retry every non-2xx response with exponential backoff — seven attempts spanning about 44 hours. Retries are idempotent, meaning the same event id can be redelivered any number of times, so build your handler to detect duplicates using the top-level `id` field and short-circuit if it has already been processed. Persist the event id in a database with a unique index, or use an in-memory cache with a 48-hour TTL if you're strictly stateless. The dashboard's Test event button and the CLI snippet above let you simulate deliveries locally through ngrok or Cloudflare Tunnel, so you can exercise your retry logic before ever taking real traffic.

For very high-throughput integrations, we recommend two additional patterns. First, terminate the webhook as fast as possible: return 2xx from the HTTP handler and push the payload onto a queue (SQS, RabbitMQ, Kafka) for asynchronous processing. Second, enable payload verification on both the HMAC AND the top-level `occurred_at` timestamp — reject any event older than five minutes to prevent replay attacks. Both patterns are shown in the SDK-specific snippets in the sidebar.

Frequently asked questions

How do I verify a LOAN4BANK webhook signature?
Read the raw request body as bytes, compute HMAC-SHA256 with your endpoint's signing secret, hex-encode the result, and compare it to the value of the X-L4B-Signature header using a constant-time comparison. Never use == for the comparison; use hmac.compare_digest or the equivalent in your language.
What events does LOAN4BANK send via webhooks?
Loan application lifecycle (submitted, in-review, approved, rejected, disbursed), KYC completion, EMI reminders and payments, gold-loan price refreshes, wallet ledger movements, referral commission accruals, corporate gift redemptions and rate-limit / quota alerts. The full list, with sample payloads, is on the event table above.
How many times will LOAN4BANK retry a failed webhook delivery?
Up to 7 attempts over approximately 44 hours: 30s, 2m, 10m, 1h, 6h, 12h, 24h. If every attempt fails, the delivery is marked failed_permanent and the webhook endpoint is auto-disabled until you reactivate it from the dashboard.
Can I test webhooks locally before going live?
Yes. Use ngrok or Cloudflare Tunnel to expose your local server, register that URL on the developer dashboard, then click Test event or use the CLI curl snippet in the section above. Every dashboard delivery row has a Replay button so you can re-fire an event as often as needed.
How do I make my webhook handler idempotent?
Store the top-level id field of every event in a database with a unique index (or an in-memory cache with a 48-hour TTL) and short-circuit if you have already processed that id. LOAN4BANK guarantees the same id will always represent the same logical event, even across retries.

Prefer async? Terminate the webhook handler in under 200ms by returning 2xx immediately and pushing the payload onto a queue for downstream processing. This is the pattern our largest partners use to process millions of events per day.