Skip to main content

Event reference

Every event — delivered by webhook or returned by the poll API — uses the same envelope. The webhook body and the poll-API item are byte-for-byte the same shape.

The envelope​

{
"eventId": "evt_66a1f0c2d3e4f5a6b7c8d9e0",
"type": "payment.collected",
"occurredAt": "2026-08-17T09:31:00.000Z",
"version": 1,
"lenderCode": "YOURORG",
"sourceLoanNumber": "LN-2026-0001",
"sequence": 4,
"payload": { }
}
FieldTypeMeaning
eventIdstringevt_ + 24 hex chars. Globally unique — your dedupe key.
typestringOne of the types below. Also sent as the X-Fieldproof-Event header.
occurredAtISO 8601 datetimeWhen the change happened on our side.
versionnumberEnvelope version. Currently 1.
lenderCodestringYour organisation code.
sourceLoanNumberstring | nullYour loan number as pushed. null only on ping.
sequenceinteger | nullPer-case counter, starts at 1 for every new case, counts all event types (including ones you are not subscribed to). null only on ping.
payloadobjectType-specific, below.

All money values are whole INR rupees (numbers, no paise).

Subscription​

The default subscription is case.received, payment.collected, case.closed. case.assigned and visit.completed are opt-in — ask our integration team to enable them (CRM Integrations tab); there is no self-serve subscription API yet. Two consequences:

  • sequence still counts the events you don't receive, so a default subscriber sees gaps (1 → 4 → 5). That is normal — see Ordering.
  • The poll API returns all types regardless of your webhook subscription.

case.received​

Your pushed row is a live case in the network. Also the first event of a fresh case when a loan whose previous case had closed is re-pushed — the caseId changes and sequence restarts at 1.

payload
{ "sourceLoanNumber": "LN-2026-0001", "caseId": "CC-2026-2388ED42", "status": "pending" }
FieldValue
statusalways "pending"

case.assigned (opt-in)​

A field partner accepted the case. External status becomes in_progress.

payload
{ "sourceLoanNumber": "LN-2026-0001", "caseId": "CC-2026-2388ED42", "assignedAt": "2026-08-17T06:10:11.000Z" }

visit.completed (opt-in)​

A visit was recorded, whatever the outcome. The case stays in_progress.

payload
{
"sourceLoanNumber": "LN-2026-0001",
"caseId": "CC-2026-2388ED42",
"outcome": "ptp",
"visitAt": "2026-08-17T08:45:02.000Z",
"ptpDate": "2026-08-20",
"notes": "Will pay after salary credit"
}
FieldTypeNotes
outcomeenumSee table below.
visitAtISO datetimeWhen the visit was recorded.
ptpDatestring | nullCommitted pay-by date; set for ptp, null otherwise.
notesstring | nullPartner's free-text note, may be null.

outcome values:

outcomeMeaning
delivered_acknowledgedPayment notice delivered and acknowledged by the borrower
collectedMoney collected in full during the visit
partialPart of the outstanding collected
ptpPromise to pay — ptpDate carries the committed date
rtpRefuse to pay — the borrower declined to pay
refusedBorrower refused (also a case.closed reason when the case ends this way)
not_availableBorrower not found at the address this visit
wrong_addressAddress does not lead to the borrower — push a corrected row
shiftedBorrower has moved — push a corrected row

A collected or partial outcome is the partner's claim; money is only announced by payment.collected once it is verified/booked (below).

payment.collected​

Money verifiably received on your rails. Fires per payment, with both the incremental amount and the absolute case totals.

payload
{
"sourceLoanNumber": "LN-2026-0001",
"caseId": "CC-2026-2388ED42",
"amount": 3000,
"mode": "upi",
"reference": "UTR2026081712345",
"totalCollected": 3000,
"outstandingAfter": 9500,
"collectedAt": "2026-08-17T09:30:12.000Z"
}
FieldTypeNotes
amountnumber (INR)Incremental — this payment only.
modeenumupi | card | netbanking | wallet | online | link | neft | nach | cheque | razorpay. Never cash (not accepted for external-org cases).
referencestring | nullUTR / gateway payment id. null when no reference could be captured.
totalCollectednumber (INR)Absolute total collected on this case after this payment.
outstandingAfternumber (INR)Absolute outstanding on this case after this payment.
collectedAtISO datetimeWhen the payment was booked.

When it fires and what mode / reference carry:

  • Mode A — static QR: after our operations team verifies the partner's payment proof. amount is the operator-confirmed figure — it may differ from what the partner claimed; trust the event. mode is the partner-recorded mode (usually upi); reference is the UTR where extracted. Rejected proofs emit nothing.
  • Mode B — your gateway: the event mirrors your own POST /cases/{loan}/payments/confirm. mode echoes the mode you sent (default link); reference is the gateway payment id you sent.

Money is never announced before it is booked. Use the absolute values to set your loan record; use amount for your ledger row.

case.closed​

Terminal — the last event of a case.

payload
{
"sourceLoanNumber": "LN-2026-0001",
"caseId": "CC-2026-2388ED42",
"status": "collected",
"reason": "full_settlement",
"totalCollected": 12500
}
FieldTypeNotes
statusenumcollected | closed_unrecovered
reasonenumfull_settlement | recalled | not_available | refused | written_off | legal_handover
totalCollectednumber (INR)Absolute total collected on this case at closure.
reasonstatusTrigger
full_settlementcollectedPayments reached full clearance (≥ 99 % of the remaining outstanding — 1 % tolerance). Preceded by a payment.collected.
recalledclosed_unrecoveredYou called POST /cases/{loan}/recall.
not_availableclosed_unrecoveredClosed after the borrower could not be found.
refusedclosed_unrecoveredClosed after the borrower refused to pay.
written_offclosed_unrecoveredWritten off — an admin action on the Fieldproof side.
legal_handoverclosed_unrecoveredHanded over for legal action — an admin action on the Fieldproof side.

A closed loan can be re-pushed: that opens a new case with a new caseId and sequence restarting at 1 (you will see case.received again).

verification.completed​

:::note Address-verification product only verification.completed belongs to the address-verification product. It is not emitted for collection cases — if you only run collections you will never receive it, and you can ignore the type. Its payload is documented with that product. :::

ping​

Sent only by the webhook self-test (POST /api/v1/partner/webhooks/test). The envelope is the same shape, but sourceLoanNumber and sequence are null and the eventId has a ping_ marker:

full ping body
{
"eventId": "evt_ping_9c4e21a7b3d0",
"type": "ping",
"occurredAt": "2026-08-17T09:00:00.000Z",
"version": 1,
"lenderCode": "YOURORG",
"sourceLoanNumber": null,
"sequence": null,
"payload": { "message": "<free text>" }
}

Verify the signature, return 2xx, apply nothing. Your JSON model must accept the nulls.


Ready to consume these? Updating your LMS from our events has the complete, transactional apply handler with SQL.