Wallet
Portal
Overview

Portal — Overview

Who calls these endpoints?Portal / SDK
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.

Portal and server-to-server surfacesArchitecture: the end user device reaches the portal surface with a publishable key and its own session, while your server reaches the server-to-server surface with signed requests. Both land on the same wallet core with different authority.The user's devicepublishable key + sessionYour backendEd25519 signed request/api/portal/v1/wallet/*own data only/api/v1/wallet/*tenant-wideWallet coreLEGENDEND USERAPI SURFACESHARED CORE
The two surfaces are not interchangeable. The portal surface is limited to the session owner's own data; a signing key reaches the whole tenant — which is why a signing key never goes on an end-user device.
⚠️

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:

Capabilitys2s (/api/v1/wallet/*)Portal (/api/portal/v1/wallet/*)
Operation listAll tenant operationsOnly the session owner's
BalanceAny user'sOnly their own
TransferFrom any accountOnly from their own account
Create userYesNo (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

AreaEndpoints
Identityregister, login, OTP, sessions, devices, password
Accountbalances, wallet/account creation, limits, KYC level
Moneytop-up, transfer, withdrawal, QR payment, invoices
Loyaltycashback, points, tiers, badges
Utilitysaved contacts, fee quotes, authorizations (mandates)

Architectural guidance

  1. Store the session securely on device. Keep access/refresh tokens in secure storage; never log them.
  2. Build step-up into the flow. Have the OTP screen ready on every outbound money path; retrofitting it breaks the flow.
  3. Show locked capabilities. Do not hide operations the user's KYC level does not allow — show them locked with their requirement.
  4. Quote fees live. Call quote as the amount is typed and show the total.
  5. Describe uncertain states correctly. HELD and AWAITING_RECON are not failures.