Authentication: All endpoints require Authorization: Bearer <access_token>.External Dependency: Juspay (sandbox: https://sandbox.juspay.in)

Wallet Provisioning Flow

The wallet is automatically created after the user’s first login — the app never needs to trigger this.
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 State Machine

Understanding wallet status is critical for app UX — the wallet UI should adapt to each state.
StatusMeaningApp UX Recommendation
CREATEDDB row created; Juspay provisioning not startedShow “Setting up your wallet…” spinner
PENDINGBackground worker picked up jobShow “Setting up your wallet…” spinner
IN_PROGRESSJuspay customer created; wallet creation in flightShow “Almost ready…”
COMPLETEDWallet fully active in JuspayShow balance and top-up button
RETRYINGTransient failure; retry in progressShow “Hang on, setting up…”
FAILEDAll retries exhausted; manual intervention neededShow “Wallet setup failed” with support contact

GET /wallet

Returns the authenticated user’s wallet summary.
curl http://localhost:8080/wallet \
  -H 'Authorization: Bearer eyJhbGci...'
wallet_id
string
The Transcorp wallet identifier assigned by Juspay during provisioning (e.g. wlm_jUT7wpiZAkpFFvaT). This is the transcorp_wallet_id stored internally. Use this value as the wallet_id path parameter in all subsequent wallet endpoints (/balance, /status, /transactions/history/{n}). Null until creation_status = COMPLETED.
creation_status
string (enum)
required
Internal provisioning state: CREATED | PENDING | IN_PROGRESS | COMPLETED | RETRYING | FAILED
wallet_status
string
Live Juspay status: ACTIVE | BLOCKED. Null until COMPLETED.
current_balance
number
Balance in INR. Null until wallet is COMPLETED. Cached locally — use the /balance endpoint for a live Juspay query.

POST /wallet (Idempotent Create)

Explicitly creates a wallet for the authenticated user. This is idempotent — safe to call multiple times. The wallet is normally auto-created on first login, so this endpoint is a fallback for edge cases (e.g. first login failed mid-provisioning).
curl -X POST http://localhost:8080/wallet \
  -H 'Authorization: Bearer eyJhbGci...'

GET /wallet//balance

Fetches fresh balance from Juspay — not a cached value. Use this when displaying the wallet balance screen to ensure the user sees a real-time figure.
The wallet_id path parameter is the transcorp_wallet_id — the wallet identifier on Transcorp’s PPI system, returned by GET /wallet in the wallet_id field (e.g. wlm_jUT7wpiZAkpFFvaT). This ID is assigned by Juspay/Transcorp when the wallet is first provisioned and stored in Aarokya’s database. It is null until creation_status = COMPLETED.
This endpoint makes a live API call to Juspay. It may be 200–500ms slower than other endpoints. Use the balance from GET /wallet (cached) for non-balance screens and this endpoint only for the balance screen.
curl "http://localhost:8080/wallet/wlm_jUT7wpiZAkpFFvaT/balance" \
  -H 'Authorization: Bearer eyJhbGci...'

GET /wallet//status

Returns the wallet’s current provisioning status enum only. Lightweight alternative to GET /wallet when you only need to poll the status.
The wallet_id path parameter is the transcorp_wallet_id from GET /wallet. See the balance endpoint note above for details.
curl "http://localhost:8080/wallet/tcx_550e8400-.../status" \
  -H 'Authorization: Bearer eyJhbGci...'

GET /wallet//transactions/history/

Returns recent transaction history. The number_of_days path parameter filters by recency. The wallet_id is the transcorp_wallet_id from GET /wallet.
ValueBehaviour
0All history (no time filter)
1–365Entries from the last N days
> 365Returns 400 NUMBER_OF_DAYS_OUT_OF_RANGE
< 0Returns 400 NUMBER_OF_DAYS_OUT_OF_RANGE
Maximum 10 entries are returned per call (stub limit — paginated results will be added with live Juspay integration).
curl "http://localhost:8080/wallet/tcx_550e8400-.../transactions/history/30" \
  -H 'Authorization: Bearer eyJhbGci...'

POST /nh/payment/session — Create Payment Session

Single endpoint for both wallet top-up and insurance purchase. The transaction_type field determines the Juspay session configuration.

Payment Flow

Key Differences Between Transaction Types

WALLET_TOPUPINSURANCE
Juspay session configIncludes payment_rules.load_moneyNo payment_rules
Money destinationUser’s health wallet (Transcorp)Narayana Health
Status check pathwallet.topup.status fieldTop-level status field
CHARGED maps toSUCCESS via topup statusSUCCESS directly
Post-success actionRefresh wallet balance displayCall POST /insurance/purchase
curl -X POST http://localhost:8080/nh/payment/session \
  -H 'Authorization: Bearer eyJhbGci...' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 1000.00,
    "transaction_type": "WALLET_TOPUP"
  }'
Pass sdk_payload directly to HyperServices.initiate(sdk_payload) in the React Native app — do not modify it. The Juspay SDK handles all payment instrument selection, UPI deep-linking, and card tokenization internally.

GET /nh/payment/order-status — Poll Order Status

Poll this endpoint until a terminal status is returned. The poll_interval_seconds field in the response tells you how long to wait before the next poll.
curl "http://localhost:8080/nh/payment/order-status?order_id=aarokya-1767808067" \
  -H 'Authorization: Bearer eyJhbGci...'
status
string (enum)
required
PENDING | SUCCESS | FAILED | CANCELLED
  • PENDING — payment is being processed; keep polling
  • SUCCESS — payment confirmed; proceed with post-payment action
  • FAILED — payment failed; show failure screen with retry option
  • CANCELLED — user cancelled; return to previous screen
poll_interval_seconds
integer
required
Seconds to wait before the next poll. Configured via app_config table (PAYMENT_STATUS_POLL_INTERVAL = 5). Respect this value — do not poll faster than specified.

Polling Implementation

async function pollPaymentStatus(orderId: string): Promise<'SUCCESS' | 'FAILED' | 'CANCELLED'> {
  const MAX_POLLS = 36; // 36 × 5s = 3 minutes max wait
  let polls = 0;

  while (polls < MAX_POLLS) {
    const response = await apiFetch(
      `/nh/payment/order-status?order_id=${orderId}`,
      { headers: { 'Authorization': `Bearer ${accessToken}` } }
    );

    const data = await response.json();

    if (data.status !== 'PENDING') {
      return data.status; // Terminal status
    }

    polls++;
    await sleep(data.poll_interval_seconds * 1000);
  }

  // Timeout — show "payment status unknown" screen
  throw new Error('Payment status check timed out');
}

Post-Payment Actions

After receiving SUCCESS, the app must perform a follow-up action depending on transaction_type:
transaction_typeSUCCESSAction
WALLET_TOPUPBalance updated in JuspayCall GET /wallet/{id}/balance to refresh displayed balance
INSURANCEPayment received by NHCall POST /insurance/purchase with the order_id to create the policy record
For INSURANCE payments, never skip the POST /insurance/purchase step after payment success. The payment alone does not create a policy — the purchase call is required to issue the policy through Narayana Health’s API and store it in Aarokya’s database.