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 · amount · fee · tax · currency · clientReference · links

Every money field is a decimal string with two places ("2500.00") — see Amounts.

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 always masked; the summary tells you which identifier a payout may go to (payoutEligible).

Send only what we cannot work out. Each identifier needs its type and value; the class, primary flag and verification status follow from the type unless you say otherwise. kyc needs the level, the risk rating and your evidence. agreements must carry the customer's acceptance of the wallet terms, privacy notice and fee schedule — one entry each with agreementTypewallet_opening, privacy and fee_schedule, captured in your own channel. A request missing any of them is refused with 422 RequiredAgreementsMissing and nothing is created; there is no partial or pending customer.

clientReference is your own id for the customer. It is unique among your customers, comes back on every customer response and on the customer.created webhook, and the client portal finds a customer by it.

A 409 on POST /customers means your account already has a customer with that verified identifier, or with that clientReference.

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. Contact us to change what your account is approved for.

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.

Transfer between wallets ​

A customer sends money to another of your customers, or pays your business collection wallet.

StepCallScope
1POST /wallet-transfers with the sender's assertionwallet-transfers:write

Webhooks: transfer.completed. Details: Wallet transfers.

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-intents → intentReference, expiresAttopup-intents:write
2We send a payment prompt for the exact amount to the customer's phone; they approve it there—
3Webhook topup.confirmed — or topup.expired if the customer did not approve in time—

The prompt goes to the customer's verified phone number: their primary one, or, if the primary contact is not a verified phone number, the verified phone number added first. A customer without one is refused with 422. To prompt another of the customer's verified phone numbers for one top-up, send its uid (identifiers[].uid on GET /customers/{uid}) as payerIdentifierUid; a uid that is not one of the customer's verified phone numbers is refused with 422, and the customer's primary number stays as it is. Show your customer payerInstructions. A prompt the customer declines or ignores is not retried: the intent expires, and you create a new one to try again.

Read ​

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