Customer app, staff underwriting, vendor callbacks, and ops.After a successful login, tokens and ids are saved for the next request. Follow the flow below — skipping a step is how you get 409 / 422 or a screen that still shows the previous action.Start a run: GET /products → OTP request → OTP verify. Then stay on Bearer.Ground rules#
Money is integer paise. ₹15,000 = 1500000. Never send rupees as a decimal.
Ids look like cust_1735…, app_1735… — not UUID-shaped. Screen reference PME-XXXXXXXX.
Failures look like { "code": "snake_case", "message": "plain English" }.
Revision: application writes need a fresh revision from the last screen. Stale → 409 revision_conflict. Refresh GET /applications before a write.
There is no create-application API. OTP verify starts the case when isNewUser is true.
Customer URLs have no application id. The session picks the open application. Staff URLs still use /applications/{id}.
Who calls what#
| Who | How | What to use |
|---|
| Anyone | no login | Health, Products, OTP, token refresh |
| Customer | Bearer accessToken | Profile, Applications, Journey, email verify |
| Staff | staff actor (local hatch) | Evaluate, verify-bureau, cancel |
| Ops | X-Internal-Secret | dispatch-due, poll-timers, redrive |
| Vendor | signed webhook | /webhooks/* |
Customer flow — what to call, when, and why#
1. Is the API up?#
Why: wrong host looks like “login is broken”. This only proves the process is listening.2. Pick a loan product#
Call GET /products before OTP.Why: every OTP is tied to a published product_id. The session stays on that product. Unknown id → invalid_product.Then: copy products[n].id into product_id (first product is saved automatically).3. Login (this also starts the application)#
Call POST /auth/otp/request with phone + product_id.Why: a one-time code is stored. It is not in the JSON. Mock code is 123456 unless QA says otherwise.Then POST /auth/otp/verify with the same phone, same product_id, and the code.Why: this returns phoneSessionToken always. One account also gets accessToken / refreshToken / customerId and application (auto-started when isNewUser is true). Two family accounts → pick with POST /auth/accounts/select. From here every customer URL needs Bearer.Token expired → POST /auth/token/refresh. Sign out → POST /auth/logout.4. Email (optional to log in; required if you collect email)#
Call POST /auth/email/verify/start → customer taps the mail → POST /auth/email/verify/confirm with token + Bearer.Why: email cannot be saved on the profile POST. Confirm sets emailVerified: true. Poll GET /auth/email/verify/status while waiting.5. Bind and save the profile#
Call GET /application/profile as soon as you have a token.Why: pre-populate phone / email / KYC name. null means unknown, not “field missing”.When the current step is update_profile, call POST /application/profile (fullName, dob as dd-mm-yyyy, address string, employment type, employer name, monthlyIncomePaise).Why: PAN/Aadhaar matching and later bank-name check need these on file.6. Read the screen, then begin verification#
Call GET /applications (status, youCanNow, revision) and/or GET /application/journey (current.step, upcoming).Why: do not guess the next URL. Use youCanNow[].submitTo or current.path.If status is started, call POST /applications/begin-verification.Why: started → verifying. KYC is not allowed before this.7. PAN → bureau → customer sees the score#
Call POST /application/kyc/pan (or POST /applications/verify-pan).Why: DigiLocker PAN. Open redirectUrl, finish consent, then poll GET /application/kyc/pan/status until resolved + verified. If PAN is not in the locker, status stays pending and returns redirectUrl again.Then GET /application/bureau.Why: the credit-score screen needs score. Acknowledge too early → credit_score_not_ready.Then POST /applications/acknowledge-credit-score.Why: the borrower saw the score. This is not an offer. Staff still evaluate.8. Aadhaar, then wait — do not jump to bank#
Call POST /application/kyc/aadhaar/start → open redirectUrl → poll GET /application/kyc/aadhaar/status.Why: Aadhaar is matched to PAN name/DOB. Optional POST /application/kyc/aadhaar/address if they want a different communication address.When Aadhaar is verified, onboarding is finished and current is under_verification.Why you wait: underwriting. Bank KYC and eSign are after the offer.9. Staff sanction (not the customer app)#
Call (staff) POST /applications/{id}/evaluate with bureauClaimId, amount in paise, and the rule-set fields.Why: this is the only step that creates offer_ready (or rejects). A customer token here is 403.Then GET /application/eligibility. If eligible: true, show amountPaise and call POST /applications/accept-offer.10. After accept: bank, eSign, loan, payout#
Call POST /application/kyc/bank then POST /application/esign/start (poll GET /application/esign/status).Then POST /applications/create-loan → POST /applications/request-payout.Why: one loan per application. 202 on payout means “request accepted”, not “money sent”.Then ops POST /ops/dispatch-due so payout actually runs.
How to pick the next API#
Prefer GET /application/journey → call current.method + current.path.Or GET /applications → youCanNow.| Now | Call this | Why |
|---|
| no token | products → OTP request → verify | Login + auto-start |
started | POST /applications/begin-verification | Unlock KYC |
update_profile | POST /application/profile | Name/DOB/income before PAN |
kyc_pan | PAN start + status poll | DigiLocker PAN |
bureau | GET /application/bureau | Score for the next screen |
acknowledge_credit_score | POST /applications/acknowledge-credit-score | Customer saw the score |
kyc_aadhaar | Aadhaar start + status poll | Identity vs PAN |
under_verification | wait; staff evaluate | Underwriting |
eligibility / offer_ready | eligibility GET, then accept-offer | Show amount, take consent |
accepted | bank → esign → create-loan | After the offer |
| loan exists | request-payout, then ops dispatch | Disbursal |
Refresh GET /applications before a write that needs revision.Other flows#
Cancel an offer (staff): POST /applications/{id}/cancel from sanctioned. Terminal.Vendor callbacks: POST /webhooks/bureau|kyc|esign|income with a signature. claimId is in the body.Stuck payout: POST /ops/redrive/{handlerRowId} then dispatch-due again.Deprecated folder: old by-id customer aliases. Do not use them unless a ticket says so.Values saved after a good response#
| Value | Comes from | Used for |
|---|
product_id | GET /products, OTP verify | OTP body |
accessToken / refreshToken / customerId | OTP verify, refresh | login |
applicationId / revision | screens, journey, verify | staff path, writes |
redirectUrl | KYC start/status | open DigiLocker |
claimId | staff verify-bureau, webhooks | evaluate |
loanId / payoutRequestId | create-loan, request-payout | payout |
journeyCurrent / journeyUpcoming | GET /application/journey | next step |
lastErrorCode | any error JSON | debug |
Modified at 2026-09-09 21:04:00