Authentication: All endpoints require Authorization: Bearer <access_token>.Current state: Wallet is a local stub — Transcorp/Juspay integration is in progress. All balance and history data is seeded locally.

What Is the Health Wallet?

The Aarokya health wallet is a ring-fenced digital savings account for healthcare expenses. Unlike a general-purpose prepaid wallet, money loaded into it can only flow toward medical expenses — consultations, pharmacy, insurance premiums, and hospitalisation costs at network hospitals. It is powered by Juspay (payment orchestration) and Transcorp (the RBI-licensed PPI issuer). From a regulatory standpoint, it operates under the RBI’s Prepaid Payment Instrument (PPI) framework.

Who can contribute to the wallet?

One of the wallet’s most powerful features is multi-source funding:
SourceExample
Worker themselvesWeekly top-up via UPI
EmployerNamma Yatri contributes ₹200/month per driver
PlatformSwiggy health benefit linked to delivery milestones
Family memberSpouse tops up from their UPI
Government schemeState health department direct transfer
This design recognises that gig workers are at the intersection of multiple organisations that each have an incentive to keep them healthy.

Provisioning Flow

The wallet is automatically created when the user first logs in — the app never needs to call POST /wallet explicitly (though it is safe to do so idempotently).

Why async creation?

Calling Juspay’s API synchronously during login would make login latency dependent on an external service. If Juspay has a 2-second response time, every first login would take 2+ seconds. If Juspay is unavailable, users could not log in at all. Async provisioning:
  1. Makes login fast regardless of external service latency
  2. Isolates authentication availability from payment gateway availability
  3. Allows retry logic without user-facing impact
Wallet creation is async — it never blocks the login response. The app should check wallet status before initiating payments. Status transitions: CREATEDPENDINGIN_PROGRESSCOMPLETED | FAILED | RETRYING

Wallet Status Reference

wallet_status — Internal Provisioning State

This tracks the progress of Juspay/Transcorp wallet provisioning:
StatusMeaningApp UX
CREATEDRow created in DB; Juspay call not yet madeShow loading indicator on wallet tab
PENDINGBackground worker has picked up the jobShow loading indicator
IN_PROGRESSJuspay customer created; wallet creation in flightShow loading indicator
COMPLETEDWallet fully provisioned in JuspayShow balance and top-up button
FAILEDAll retries exhausted; manual intervention neededShow “wallet unavailable” error with support CTA
RETRYINGTransient failure; retry scheduledShow loading indicator with “setting up” message

wallet_status_transcorp — Live Juspay Status

This is the status as reported by Transcorp/Juspay in real time:
StatusMeaning
ACTIVEWallet is live and can transact
BLOCKEDWallet has been blocked (KYC issue, fraud flag, etc.)
UnknownWallet not yet provisioned in Juspay (initial state)

Full State Machine


Transcorp — The RBI-Licensed PPI Issuer

What Is Transcorp?

Transcorp is the Prepaid Payment Instrument (PPI) issuer licensed by the Reserve Bank of India. In practical terms, Transcorp is the regulated entity that holds the wallet balance on behalf of each user. Aarokya cannot directly issue a digital wallet — only an RBI-licensed PPI issuer can. Transcorp fills that role. Juspay is the payment orchestration layer that sits between Aarokya’s backend and Transcorp’s core banking system. When Aarokya calls Juspay’s API to register a customer or provision a wallet, Juspay translates those calls into the appropriate operations on Transcorp’s PPI infrastructure. From Aarokya’s perspective:
  • Aarokya Backend talks to Juspay’s API (REST)
  • Juspay talks to Transcorp’s PPI system (internal — transparent to Aarokya)
  • The user’s money is held at Transcorp, not at Juspay and not at Aarokya

The transcorp_wallet_id

Once a wallet is fully provisioned, Juspay returns a transcorp_wallet_id — an identifier that uniquely addresses the user’s wallet account on Transcorp’s system. This identifier looks like wlm_jUT7wpiZAkpFFvaT and is stored in Aarokya’s customer_wallets.transcorp_wallet_id column. Every subsequent balance query, top-up, and transaction history lookup uses this ID to address the correct Transcorp wallet. All path parameters named wallet_id on the wallet endpoints refer to this transcorp_wallet_id.

Wallet Status from Transcorp

Transcorp (via Juspay) reports a live wallet status that is distinct from Aarokya’s internal provisioning status:
wallet_status_transcorpMeaning
ACTIVEWallet is live and can transact normally
BLOCKEDWallet has been blocked — typically due to a KYC flag or fraud signal. Contact support.
UNKNOWNWallet not yet provisioned in Transcorp (initial state before provisioning completes)

Integration Architecture

Third-Party Services

ServiceRoleWhat Aarokya calls
JuspayPayment orchestration, Hyper Checkout SDK host, Transcorp intermediaryPOST /customers, POST /customers/{ref}/wallets, payment session creation, balance queries
TranscorpRBI-licensed PPI issuer — holds the actual wallet balanceVia Juspay API (indirect; Aarokya has no direct API calls to Transcorp)
Aarokya’s backend never calls Transcorp directly. All Transcorp operations go through Juspay’s API. The transcorp_wallet_id is a Transcorp-internal identifier surfaced by Juspay and stored by Aarokya for routing subsequent wallet operations.

Juspay Integration Details

The wallet creation flow makes two sequential calls to Juspay:

Step 1: Create Juspay Customer

POST https://sandbox.juspay.in/customers
{
  "object_reference_id": "aarokya_{user_id}",
  "mobile_number": "9876543210",
  "email_address": "priya@example.com",
  "first_name": "Priya",
  "last_name": "Kumar"
}

Step 2: Provision Wallet for Customer

POST https://sandbox.juspay.in/customers/aarokya_{user_id}/wallets
{
  "object_reference_id": "wallet_{user_id}",
  "merchant_id": "cumta"
}
The transcorp_wallet_id returned in this call (e.g. wlm_jUT7wpiZAkpFFvaT) is stored in customer_wallets.transcorp_wallet_id and used for all subsequent balance and transaction queries.

Endpoints

MethodPathDescription
GET/walletWallet summary — includes transcorp_wallet_id
POST/walletIdempotent create — safe to call multiple times
GET/wallet/{wallet_id}/balanceBalance + linked status (live Juspay query)
GET/wallet/{wallet_id}/statusWallet creation status enum
GET/wallet/{wallet_id}/transactions/history/{number_of_days}Local ledger (max 10 entries)

Payment Sessions

The wallet is funded and used via Juspay payment sessions. There are two payment types, each with different Juspay session configuration:

WALLET_TOPUP — Adding Money

Used when the user wants to add money to their health wallet from an external UPI/card/netbanking source.
  • Juspay session includes payment_rules.load_money flag
  • After SUCCESS, Juspay credits the wallet balance
  • Balance is refreshed from Juspay on the next /wallet/{id}/balance call

INSURANCE — Paying Premium

Used when the user is paying an insurance premium. The money does NOT go through the health wallet — it goes directly to Narayana Health as a payment.
  • Juspay session does NOT include payment_rules.load_money
  • After SUCCESS, POST /insurance/purchase creates the policy record
WALLET_TOPUPINSURANCE
Juspay session typeIncludes payment_rules.load_moneyStandard payment session
Money flows toUser’s health walletNarayana Health
Status check pathwallet.topup.statusTop-level status
Post-success actionRefresh wallet balanceCall POST /insurance/purchase
When the user pays insurance premium from their wallet balance, use INSURANCE type and set the payment instrument to the wallet (CUMTA_WALLET). This deducts from the wallet balance and pays NH in a single step.

Transaction History

The number_of_days path parameter controls the filter window:
ValueBehaviour
0All history (no time filter)
1–365Entries from the last N days
Maximum 10 entries are returned per call. Entries are stub data until Transcorp integration is complete. Transaction entry structure:
FieldTypeDescription
transaction_idstringUnique transaction reference
transaction_statusstringSuccess | Pending | Failed
amountintegerAmount in paise (100 = ₹1)
currencystringAlways INR
typestringCREDIT (top-up) or DEBIT (payment)
descriptionstringHuman-readable description
occurred_attimestampWhen the transaction occurred

Request / Response Examples

curl http://localhost:8080/wallet \
  -H 'Authorization: Bearer eyJhbGci...'
curl -X POST http://localhost:8080/wallet \
  -H 'Authorization: Bearer eyJhbGci...'
curl "http://localhost:8080/wallet/tcx_550e8400-.../balance" \
  -H 'Authorization: Bearer eyJhbGci...'
curl "http://localhost:8080/wallet/tcx_550e8400-.../transactions/history/30" \
  -H 'Authorization: Bearer eyJhbGci...'

Database Schema

transaction_history is stored as JSONB — a local stub ledger until live Transcorp transaction history is available. Balance is stored in paise (integer) to avoid floating-point rounding errors. Divide by 100 to display in rupees.