LendEasy/DocsLMS + Servicing·v1
Start integrating
Guides›Overview›Webhooks

Webhooks

At-least-once notifications, each one signed, so your systems learn that something real happened in LendEasy without polling for it.

Webhooks are how your system finds out that something real happened in LendEasy without asking every few seconds whether it has. They are at-least-once notifications: duplicates, retries, and out-of-order delivery are ordinary conditions rather than incidents — and a consumer written as though each event arrives once, in order, posts the same payment twice the first time a delivery is retried.

The signing secret is shown exactly once

curl -X POST "$BASE/v1/servicing/webhook-subscriptions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "LendEasy-Tenant: demo-lender" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: portfolio-webhook-v1" \
  -d '{
    "url": "https://events.example.test/lendeasy",
    "eventTypes": [
      "payment.posted",
      "payment.returned",
      "funding.posted",
      "case.transitioned"
    ],
    "apiVersion": "2026-07-01",
    "description": "Portfolio event consumer"
  }'

The create response returns signingSecret exactly once. Later reads expose only secretVersion, so a secret that was never captured is recovered by rotating it rather than by reading it back.

The payload is a snapshot, not the state

{
  "eventId": "evt_PAY_81",
  "eventType": "payment.posted",
  "apiVersion": "2026-07-01",
  "occurredAt": "2026-09-15T14:22:08Z",
  "subject": { "type": "payment", "id": "9011" },
  "data": {
    "paymentId": 9011,
    "loanRef": "7204",
    "status": "POSTED",
    "amount": "517.14",
    "currency": "USD",
    "effectiveDate": "2026-09-12"
  }
}

The payload is a notification snapshot, not a substitute for an authorized API read when your workflow needs current state.

Verify before parsing business data

LendEasy signs the exact raw request bytes with the active secret. Verification order:

1. Read raw bytes without JSON reserialization.
2. Reject timestamps outside your replay window.
3. Compute HMAC over timestamp + separator + raw bytes.
4. Compare signatures in constant time against active overlap secrets.
5. Deduplicate eventId, then parse and enqueue.

Keep the signature, the secret, and the unrestricted payload out of your logs: a signature sitting next to the bytes it signs is a delivery that anybody who can read that log can replay against your endpoint. During rotation, accept the old and new secret only until previousSecretExpiresAt — a secret you keep honouring past that point is a second live key, and the rotation has not actually happened.

Acknowledge fast, then do the work

Heavy work done before the acknowledgement runs past the delivery timeout, the event is retried, and the same slow work starts again on the next copy — and the copy after that.

Non-2xx responses and timeouts retry with increasing delay. A 2xx means your endpoint accepted responsibility; it does not mean your downstream workflow finished.

The timeline is truth, not arrival order

  • Put a unique constraint on eventId — with at-least-once delivery a second copy is a matter of when, not whether.
  • Track the last applied resource version for state projections.
  • Ignore an older version after a newer version, but retain it for diagnostics.
  • Fetch current state when a missing predecessor matters.
  • Make side effects idempotent independently of webhook deduplication.

payment.returned may reach one consumer before a delayed payment.posted delivery. A consumer that applies them in the order they landed records a payment that was returned before it was ever made, and every figure downstream inherits that.

Five operations run the delivery side

You can inspect and change delivery without touching the consumer. Five operations, and none destroys history:

Operation Purpose
POST /v1/servicing/webhook-subscriptions/{id}/test Send a signed non-business test event through the normal path.
GET /v1/servicing/webhook-subscriptions/{id}/deliveries Inspect attempts, response codes, latency, and retry state.
POST /v1/servicing/webhook-subscriptions/{id}/rotate-secret Rotate with a bounded overlap window.
PATCH /v1/servicing/webhook-subscriptions/{id} Change filters or destination with optimistic concurrency.
POST /v1/servicing/webhook-subscriptions/{id}/deactivate Stop new deliveries while retaining history.

Subscribe narrowly, then read current state

Published families include customer and consent lifecycle, loan and schedule changes, payment and return state, funding state, statements, servicing actions, restrictions, cases, tasks, interactions, promises, documents, merchant sales/remittances, and reconciliation exceptions. Every family you take that nothing downstream consumes is traffic your endpoint has to verify, deduplicate, and acknowledge inside five seconds anyway.

A webhook is authenticated system-to-system evidence that LendEasy emitted an event. It does not verify the identity of a caller, email sender, phone participant, or document uploader described inside the event.

How this contract is delivered today

Signed, at-least-once delivery is the contract. The surface it travels over is named here, in one place, so nothing above has to interrupt itself to say so.

Unified search across guides, recipes & the API referenceEsc