Portal — Overview
- Base path
/api/portal/v1/wallet- Identity
- Publishable key (
pk_live_…) → OTP → customer session. The session belongs to the end user, not to the merchant, and reaches only that user's own data. - Authorization
- The session itself is the authorization; a user reaches only their own record.Step-up: for money-out actions the session alone is not enough; an OTP code or a trusted action token is required.
- Cross-surface
- This surface is for the end user; your merchant server does not call it. For the server-side counterpart see the s2s API.
The portal is the surface where the end user interacts with their wallet: your mobile app, your web app, or your embedded wallet screens. It runs under your brand; the user never sees a Lunixi interface.
The end user's device never carries a signing key. If it did, anyone who installed the app could reach every wallet in your tenant. The portal surface exists with a separate identity model precisely to prevent that.
Authority scoping
The same capability is exposed with different scope on the s2s and portal surfaces:
| Capability | s2s (/api/v1/wallet/*) | Portal (/api/portal/v1/wallet/*) |
|---|---|---|
| Operation list | All tenant operations | Only the session owner's |
| Balance | Any user's | Only their own |
| Transfer | From any account | Only from their own account |
| Create user | Yes | No (registration is a separate flow) |
These are separate controllers — one endpoint is never shared across three identities. That is a structural choice so a single mis-scoped check cannot open the whole surface.
Publishable key
Your app identifies itself with a publishable key (pk_live_…). It can be embedded in the app; on its own it grants access to nothing, it only answers "which tenant and which program".
Creating a publishable key requires declaring allowed origins (allowedOrigins), and wildcards (*) are rejected. The key works only from the origins you list — even if leaked, it cannot be used from another site.
Session model
publishable key → register/login → OTP verify → customer session (access + refresh)
│
sensitive action → step-up (OTP / trusted token)- The access token is short-lived and carried on every
/me/*call. - The refresh token extends the session.
- Step-up is additional verification on outbound money — the session alone is not enough.
Details: Portal — Identity & Session.
Device awareness
Registration, login and OTP verification can carry a device descriptor (fingerprint, platform, install id, device name). This feeds the user's "which devices are connected" screen and suspicious-device detection.
Give users the device list and the ability to end a session. In wallet apps this is the only real answer to "I lost my phone".
What you can build
| Area | Endpoints |
|---|---|
| Identity | register, login, OTP, sessions, devices, password |
| Account | balances, wallet/account creation, limits, KYC level |
| Money | top-up, transfer, withdrawal, QR payment, invoices |
| Loyalty | cashback, points, tiers, badges |
| Utility | saved contacts, fee quotes, authorizations (mandates) |
Architectural guidance
- Store the session securely on device. Keep access/refresh tokens in secure storage; never log them.
- Build step-up into the flow. Have the OTP screen ready on every outbound money path; retrofitting it breaks the flow.
- Show locked capabilities. Do not hide operations the user's KYC level does not allow — show them locked with their requirement.
- Quote fees live. Call quote as the amount is typed and show the total.
- Describe uncertain states correctly.
HELDandAWAITING_RECONare not failures.