Fieldproof
Field collections infrastructure

Your overdue book, worked on the ground. Your money never touches us.

Push delinquent loans over a signed API. Verified field partners visit the borrower, collect on your own payment rails, and every outcome streams back to your LMS as a sequenced webhook. We hold the record, never the rupees.

Funds land directly with youCash never acceptedLive in 1–2 engineer-days
POST your-lms.example/webhooks/fieldproof200 OK
lenderCode YOURORGloan LN-2026-0001signature verified
1
case.receivedpending09:28:39
sourceLoanNumberLN-2026-0001caseIdCC-2026-2388ED42
2
case.assignedin_progress09:29:26
partnerverifiedregionPune, MH
3
visit.completedin_progress09:30:13
outcomeptpptpDate2026-07-26noteWill pay after salary credit
4
payment.collectedin_progress09:31:00
amount₹3,000modeupireferenceUTR2026072312345outstandingAfter₹9,500
5
case.closedcollected09:31:47
statuscollectedtotalCollected₹12,500outstandingAfter₹0
HMAC
SHA-256 signed
ORDER
per-case sequence
DELIVERY
at-least-once
REPLAY
30 days
The guarantee

We never hold, route, or touch your borrowers' money.

Collections land straight in your account — via your own static QR and bank details, or a payment link minted by your gateway. There is no pass-through account, no nodal account, and no float sitting with us.

Because of that, cash is not accepted on your cases. The partner app removes the option entirely and the API rejects it. A payment.collected event is only ever emitted for money verifiably booked in your account — proof-verified by our ops, or confirmed by you. We never announce money before it exists.

BBorrowerpays on your rail
Your bank accountdirectly, in full
Your LMSpayment.collected webhook
Fieldproof keeps the authoritative collection record — and none of the funds.
The case lifecycle

Five events, one sequence, no ambiguity.

Your system sees a deliberately simple four-state model. Internal operational sub-states are mapped underneath, so we can evolve them without ever breaking you.

SEQ 1
case.received

Validated and accepted. Push with ?dryRun=1 first for row-level validation that commits nothing.

SEQ 2
case.assigned

Assignment is ours to manage. You are told when it happens, not asked to arrange it.

SEQ 3
visit.completed

Outcomes include ptp with a committed date, refused, not_available, and wrong_address or shifted — your cue to push a corrected address. Just re-send the row.

SEQ 4
payment.collected

Payloads carry absolute values, never deltas — a missed intermediate event can never corrupt your books. Partial payments keep the case open.

SEQ 5
case.closed

Includes a recall you initiate. The exit is tested before you go live, so you know it works before you need it.

Getting cases in

Three ways in. One needs no engineering at all.

Set per organisation at onboarding, changeable later. Most teams see their first case.received webhook within an hour of receiving sandbox credentials.

Push APIrecommended

You POST batches of overdue loans to our API. Real-time, content-hash deduped, with an optional Idempotency-Key.

You build An outbound signed client + a webhook receiver
1–2 engineer-days

Pull API

We fetch from a read endpoint you expose, every 30 minutes. Built for systems that cannot make outbound calls.

You build One GET endpoint
~1 engineer-day

CSV

Send a file and our ops import it, with batch history and rollback. The way to start before any engineering is scheduled.

You build Nothing
Live immediately

Sign the request

Every call carries an HMAC-SHA256 signature over the canonical request. Keys are environment-locked — a test key is rejected on production, and vice versa.

// canonical string -> HMAC-SHA256 -> header
const canonical = [method, path, timestamp, bodyHash].join('\n');
const sig = crypto
  .createHmac('sha256', process.env.FP_SECRET)
  .update(canonical)
  .digest('hex');

await fetch('https://gig.quikkred.in/api/v1/partner/cases', {
  method: 'POST',
  headers: {
    'X-FP-Key':        process.env.FP_KEY,
    'X-FP-Timestamp':  timestamp,
    'X-FP-Signature':  sig,
    'Idempotency-Key': batchId,
  },
  body: JSON.stringify({ fullSnapshot: false, cases }),
});

Receive the event

Identical envelope on the webhook and the poll API. Dedupe on eventId; apply only if sequence beats the last you applied for that loan; on a gap, fetch the authoritative snapshot.

{
  "eventId":          "evt_01a2b3c4d5e6f7a8b9c0",
  "type":             "payment.collected",
  "occurredAt":       "2026-07-23T09:31:00.000Z",
  "version":          1,
  "lenderCode":       "YOURORG",
  "sourceLoanNumber": "LN-2026-0001",
  "sequence":         3,
  "payload": {
    "amount":           3000,
    "mode":             "upi",
    "reference":        "UTR2026072312345",
    "totalCollected":   3000,
    "outstandingAfter": 9500
  }
}
Getting money out

Two rails. Both end in your account.

Mode A — your static QR / bankMode B — your gateway's links
Money lands inYour bank, directlyYour gateway, directly
Borrower pays viaYour fixed UPI QR or bank transfer, shown in the partner appA link minted by your gateway, delivered over WhatsApp and email
ConfirmationPartner uploads proof, our ops verify with OCR and duplicate-UTR checks, then webhookYou call payments/confirm on capture, then instant webhook
You buildNothingA mint-link endpoint and one confirm call
Confirmation latencyOps verification, typically same dayInstant
Best forFastest start, no gateway requiredFully automatic confirmation
CashNever acceptedNever accepted
Reliability

Nothing lost. Nothing applied twice. Order always recoverable.

Every business event is written to a durable event store in the same database transaction as the case change. An update cannot exist without its event.

Webhook retry ladder

Immediate0sRetry 130sRetry 22mRetry 310mRetry 41hRetry 56hRetry 624hThenreplay 30d

A second independent path

If your endpoint is down for a week, nothing is lost. The poll API serves the identical events from the same store, and parked events stay self-serve replayable for 30 days.

Replays are normal, not errors

Delivery is at-least-once, so you dedupe on eventId. Batch pushes dedupe on content hash; a duplicate payment confirm returns 409 DUPLICATE_CONFIRMATION with the original booking attached.

Out-of-order is handled

Under concurrent delivery a case.closed can genuinely arrive before the payment.collected that caused it. Absolute totals mean a sequence-aware receiver needs no special case.

Onboarding

Sandbox to production, with a checklist you walk together.

Production credentials are issued only when every box is ticked — jointly, with your integration contact.

01
Apply

A short wizard — entity details, CIN or LLPIN, RBI licence, business and technical contacts, expected volume and operating states. Email verified, no login needed, and you get a tracking link.

02
Review and approval

Our ops team reviews and approves, which creates your organisation and its immutable lender code.

03
Sandbox keys

Integration mode, webhook URL and event subscriptions, payment mode and branding are configured. Test keys issued — the secret is shown exactly once. A runnable reference implementation, mock LMS and dashboard included, is yours to clone.

04
Build and prove it

Push a real batch with ?dryRun=1 until zero row errors. We send a deliberate replay to prove your eventId dedupe, and test a sequence gap. Recall tested.

05
Go live

Legal signs off the disclosure wording on a sample receipt. Live keys issued on production. First push is a small batch with ops on standby both sides, then your full book with fullSnapshot: true.

Questions

The ones your risk team will ask.

Who is the lender of record?

You are, throughout. Fieldproof is a service provider running the field operation on your behalf. Your lender-of-record disclosure is printed on every borrower receipt in wording your legal team approves at onboarding, and borrower-facing screens are re-skinned with your logo and brand colour.

Can a field partner take cash?

No. This is enforced in two places, not just policy: the partner app removes the option entirely, and the API rejects cash as a payment mode. Every collection runs on your own rail, which is what lets us guarantee that a payment.collected event always corresponds to money already booked in your account.

What happens if we want a case back?

Recall it through the API at any point. The case moves to closed_unrecovered and stops being worked. Recall is one of the items we test together before you go live, precisely so you know the exit works before you need it.

What if our webhook endpoint goes down?

Nothing is lost. Delivery retries on a ladder out to 24 hours, then parks the event as self-serve replayable for 30 days. The poll API is a completely independent second path to the same event store, so you can also just catch up by reading.

Can another lender on the platform see our data?

Isolation is enforced at the query layer rather than by convention, and covered by an automated cross-tenant test matrix. A probe with another organisation's key returns 404 — it does not even reveal that the record exists.

We cannot make outbound API calls from our LMS. Is that fatal?

No. Use pull mode: expose one read endpoint and we fetch from you every 30 minutes. If even that is blocked, start on CSV with zero engineering and migrate later — the integration mode is a per-organisation setting you can change.

Get started

Send us one overdue case. See it close.

Apply in about ten minutes, no login required. Sandbox credentials, a runnable reference implementation, and a named integration contact.