Skip to main content
This page walks through the full integration order: which endpoints to call, in what sequence, and how on-device signing from bmoni_embedded_sdk interlocks with the proxy. It mirrors the reference Flutter app shipped with the API (see example/lib/main.dart in the bmoni-proxy-api repo).
Every user-scoped endpoint takes the bmoniUserId returned from POST /v1/users as a path parameter. Persist this id (the example uses shared_preferences) so PIN unlock can re-enter without recreating the user.

Prerequisites

  • A partner API key — pass it as x-api-key: <key> on every request.
  • The proxy base URL — origin only, no trailing /v1. Path constants already start with /v1/; including it in the base URL produces /v1/v1/... → 404.
  • bmoni_embedded_sdk initialised on-device for owner-wallet generation and signing.

High-level flow

KYC data is submitted once and reused across currencies, but activation is per-onboarding, not per-profile. A user who already completed (for example) Nigeria onboarding and now wants to add a USD / CAD / EUR wallet must call POST /kyc/activate again before the new currency’s POST /onboarding/start-*. The KYC wizard (uploads + PATCH /kyc) does not need to be repeated.

Step-by-step

1

Create the user

The response contains the bmoniUserId you will reuse for every subsequent call. Persist it locally — recreating the user on each launch will fork the wallet history.
2

Generate the on-device owner wallet

Use bmoni_embedded_sdk to produce (or load) the EVM keypair whose address will be registered as the smart wallet’s owner.
The private key never leaves the device’s secure element — only ownerAddress and signatures are sent to the proxy.
3

Request the owner-proof challenge

The response includes an EIP-191 message and a challengeId. The same key that produced userOwnerAddress must sign the message — otherwise create-managed rejects the request.
4

Sign the challenge and create the managed smart wallet

The proxy performs prepare → deploy → owner-address registration server-side and returns the SmartWallet (address, chain, currency, status).
Smart-wallet calls take the stablecoin currency, not the fiat code: USDB (USD), CNGN (NGN), CADC (CAD), EURe (EUR), GBPe (GBP), MEXe (MXN). Fetch the live list from GET /v1/smart-wallets/supported-currencies.
5

Check onboarding status

If the chosen currency is already active, jump straight to the wallet home. Otherwise, enter the KYC wizard.
6

Run the KYC wizard

The submit order is fixed — do not reorder these calls:
  1. GET /v1/users/{userId}/kyc/options — option lists for personal / address / employment fields.
  2. GET /v1/users/{userId}/kyc/occupations?search=… — autocompleted occupation list.
  3. POST /v1/users/{userId}/kyc/documents/identificationmultipart/form-data with the ID image plus type, documentNumber, issuingCountry, optional expirationDate / issueDate.
  4. POST /v1/users/{userId}/kyc/documents/proof-of-addressmultipart/form-data with the proof image plus type.
  5. POST /v1/users/{userId}/kyc/documents/biometricmultipart/form-data with the selfie. Required on the Global KYC path (USD / EUR / MXN); not needed for CAD / NGN.
  6. PATCH /v1/users/{userId}/kyc — personal + address + employment + compliance (bvn for NGN — in sandbox, use the test BVN 22222222222).
  7. GET /v1/users/{userId}/kyc/readiness — gate before activation.
  8. POST /v1/users/{userId}/kyc/activate — passes a sumsubLevelName (e.g. "id-and-liveness") for USD / EUR; omit the body for CAD / NGN.
Currency-specific field requirements live in KYC — USD, KYC — NGA, KYC — CAN, KYC — EUR, KYC — MEX, and KYC — ROW.
7

Start the rail-specific onboarding

One call per currency, with the smart wallet’s on-chain address (and bvn for Nigeria):The body carries the currency-prefixed wallet fields — cadWalletAddress + cadWalletIndex, eurWalletAddress + eurWalletIndex, or (for Nigeria) bvn + ngnWalletAddress + ngnWalletIndex. MXN is the exception: POST /latam/mx/kyc/activate takes the Mexican paternal/maternal surnames, and approval additionally requires the user to sign Etherfuse’s agreements via GET /latam/mx/kyc/launch/agreements. After this returns successfully, GET /onboarding/status will report the currency as active and the wallet home becomes available.
USD is slightly different. Gate it on GET /v1/users/{userId}/kyc/usd-readiness (a USD-specific check, separate from /kyc/readiness), then call POST /v1/users/{userId}/onboarding/start-usa with { smartWalletId } — it returns { workflowId }. Track the resulting account with GET /v1/users/{userId}/vba/usd (status becomes active once issued). See Wallet home operations.
Adding a wallet later: if the user already onboarded one currency and now wants another, re-run POST /kyc/activate first (the submitted KYC data is reused — no need to repeat the wizard), then call this currency’s start-* endpoint.

Smart-wallet creation handshake


Wallet home operations

Once a wallet is active, the home screen drives top-up, withdraw, and swap from these endpoints:

Regional money movement


Gotchas

  • Base URL is origin-only. Strip any trailing /v1 from copy-pasted values.
  • Owner-proof signature must match userOwnerAddress. Reuse the same SDK instance for initWallet / walletAddress and signMessage.
  • KYC submit order is fixed. Uploads → PATCH /kyc/readiness/activate → rail start-*. USD additionally gates start-usa on GET /kyc/usd-readiness, then tracks the account via GET /vba/usd. Skipping or reordering will return validation errors.
  • Global KYC activation is currency-specific. USD and EUR require a sumsubLevelName (id-only, id-and-liveness, or idv-and-phone-verification); CAD and NGN must omit it.
  • Each additional currency needs its own activation. Activation is per-onboarding, not per-profile — adding a wallet to an already-onboarded user means calling POST /kyc/activate again before the new rail’s start-*. The KYC wizard itself is not repeated.
  • Persist bmoniUserId. The reference example uses shared_preferences so PIN unlock returns the user to their existing wallet instead of provisioning a new one.

Reference implementation

The full Flutter reference app (example/lib/main.dart in the bmoni-proxy-api repo) implements every endpoint above with concrete request/response shapes, multipart uploads, error handling, and session persistence. Use it as the source of truth for body shapes and edge-case handling.