← All projects
⚖️
CompletedIO2i Outsourcing Pvt. Ltd. · 2026

Resolution Desk

Self-service ODR portal for borrowers in arbitration

FastAPIPythonOTP AuthCase MgmtZoom API

OTP · No password

Auth model

ARB + LAN

Case ID types

v1.3.0

Version

Actively shipping

Status

A self-service portal for borrowers facing arbitration proceedings. Instead of receiving confusing legal notices with no context, borrowers log in with their Loan Account No. or Arbitration Case No., verify via OTP, and land on a personal dashboard showing exactly where their case stands.

The problem

Issuing over 5,000 notices per day created a massive influx of borrowers. Creating traditional user accounts for this scale was unfeasible. Furthermore, borrowers had no way to understand their case status without calling the firm, and many lacked the technical literacy or contact info (email/phone) to use standard web portals.

The solution

Resolution Desk authenticates users dynamically without creating individual database records. We used APIs to connect the Frappe portal with the Fin portal using a universal "borrower" role. Borrowers log in via dynamic OTP (email prioritized over SMS) or scan a QR code included on their notice (sent via any medium) for instant access. For users with no contact info, we cross-verify LAN and ARB numbers to securely collect their details.

Platform Internals

Resolution Desk OTP Login

Stateless OTP Verification Flow

Resolution Desk Sign-in

Borrower Secure Sign-in

Resolution Desk Dashboard

Borrower Case Overview

Architecture

FastAPI handles the stateless authentication flow via HMAC-signed tokens. The system routes OTPs dynamically based on available contact data to optimize costs. QR codes embedded in all notices contain secure tokens for instant session creation. The backend bridges the internal case management systems to pull live data without duplicating borrower records.

Engineering Deep Dive

⚠️

Challenges Faced

Handling massive scale without ballooning SMS costs or database size. Additionally, solving the "offline borrower" problem: how do you authenticate a user when you have no email and no phone number on file, while keeping the system secure?

🛠️

Techniques Used

I implemented stateless OTP auth using HMAC-signed JWT-like tokens without a DB lookup. I also built strict anti-enumeration protections: invalid and valid case IDs both trigger a constant-time response with a generic "Check your email/SMS" message, preventing brute-force case discovery.

Why This Technology?

Python's built-in `hmac` and `hashlib` libraries allowed me to build a highly secure, lightweight, stateless session verification layer (`Depends(bearer_scheme)`) inside FastAPI without the bloat and common vulnerabilities of massive 3rd-party JWT libraries.

Technical deep-dive

The dual case ID parser was a non-trivial piece — ARB and LAN numbers have different formats and map to different data sources. The auto-detection logic reads the prefix and routes the query accordingly. Anti-enumeration protections are layered underneath: failed lookups are rate-limited and return identical response times regardless of whether the case exists, preventing brute-force case ID discovery. OTPs use HMAC-signed tokens with 60-second expiry — no database table, fully auditable.

What was built

Dynamic OTP routing prioritizing email over SMS to optimize messaging costs

QR code integration on all notices across any medium for instant token-based login

Cross-verification fallback (LAN/ARB) for offline borrowers without contact info

Universal "borrower" role bridging Frappe and Fin portals via API

Stateless OTP verification — 6-digit code, expires in 60 seconds

Borrower dashboard — Total Cases, Resolved, Pending Action at a glance

Secure file upload and Zoom API meeting scheduler

Anti-enumeration protection: rate-limited lookups with constant-time responses

Outcome

Dramatically reduced inbound phone calls to IO2i from borrowers asking for case status. Borrowers now have a self-service window that works 24/7. The portal is actively shipping at v1.3.0 with new features added continuously.