Skip to content

Core flows

Which calls make up each job. Every write returns the same operation object; poll GET /operations/{uid} or wait for the webhook.

uid · type · state · amountMinor · feeMinor · taxMinor · currency · clientReference · links

Onboard a customer

StepCallScope
1POST /customers — creates the customer and main walletcustomers:write
2GET /customers/{uid} — status, KYC, walletscustomers:read

Webhook: customer.created, later customer.kyc_changed. Personal data in responses is masked unless you hold customers:pii.

A 409 on POST /customers means you already hold a customer with that verified identifier. A person who is also a customer of another business on Neo Wallet is a separate customer with a separate wallet there; you never see that customer, and onboarding the same person with you is not a conflict.

Choosing a wallet profile

A wallet profile decides what a customer's wallet can do — for example, whether it can pay out to a payment provider, or only be topped up and pay you. Your account is approved for one or more profiles, and has a default among them.

  • Omit rulesProfileKey and the wallet opens on your default profile.
  • Send rulesProfileKey to choose another profile your account is approved for.
  • The response, and GET /customers/{uid}, return the rulesProfileKey the wallet opened on.

The same approval covers the KYC level you assert (kyc.tierCode, and a later KYC level change) and the payment provider you name (railCode) on top-up intents and payouts. A profile, KYC level or payment provider outside your approval is refused with 422 and the generic message; the response does not say which ones exist. Ask us if what your account is approved for needs to change.

Credit a wallet

StepCallScope
1POST /wallet-creditswallet-credits:write

Synchronous: the 201 is already completed. Funds come from your prefunded balance with Neo Wallet — if it cannot cover the credit you get 422 FundingNotPositioned. Webhook: wallet_credit.completed.

Debit a wallet

Debits need the customer's consent first, as a mandate.

StepCallScope
1POST /mandates with an assertionmandates:write
2aCustomer present: POST /wallet-debits with an assertionwallet-debits:write
2bCustomer absent: POST /mandates/{uid}/debits — no assertionwallet-debits:write

Webhooks: mandate.created, wallet_debit.completed, wallet_debit.failed.

Pay out

StepCallScope
1Optional: POST /fees/quote — show the customer the totalwallets:read
2POST /payouts with an assertion (and feeQuoteId)payouts:write
3Wait for the webhook

Asynchronous. The 202 is processing: the wallet is already debited and the money is on its way. Then exactly one of:

WebhookMeaning
payout.confirmedDelivered.
payout.failedRejected by the payment provider; amount and refundable charges are back in the wallet.
payout.uncertainThe provider has not answered. Do not resend — we resolve it and notify you.

The destination must be the customer's own verified contact identifier.

Top up from outside

StepCallScope
1POST /topup-intentsintentReference, expiresAttopup-intents:write
2The customer pays the exact amount through the payment provider, quoting the reference
3Webhook topup.confirmed — or topup.expired if nothing matched in time

Read

WantCall
BalanceGET /wallets/{uid}/balance
StatementGET /wallets/{uid}/transactions
One operation + timelineGET /operations/{uid}
Customer-facing receiptGET /operations/{uid}/receipt