Skip to content

Wallet transfers ​

Transfer money from your customer's wallet to any Neo Wallet ​

POST/api/v1/tenant/wallet-transfers

Scope wallet-transfers:write · Write — send Idempotency-Key and X-Request-Id

Moves the amount from your customer's wallet immediately, to the wallet with destinationAccountNumber: any Neo Wallet (p2p) or your business collection wallet (merchant). No payment provider is involved. Confirm the payee first with GET /wallets/lookup. The assertion is the sending customer's, is validated on every call and must be bound to this request, with destinationAccountNumber as the binding target (see the Customer authentication guide); above the 2FA threshold of the sender's KYC level both factors are required. The source wallet must be one you can see — anything else is a 404 — and its profile must allow transfers. An account number that is not 13 digits with a valid check digit is a 400. The transfer is all-or-nothing: a payee that cannot receive it, a limit, a profile or available funds short of the amount plus any fee are a 422 and nothing moves. The response state is terminal immediately and transfer.completed is delivered to your webhook.

Request body ​

FieldTypeRequiredDescription
sourceWalletUidstringyesThe wallet to take the money from; one of your customers' wallets.
destinationAccountNumberstringyesThe account number of the wallet to pay: any Neo Wallet for a p2p transfer, your business collection wallet for a merchant one. 13 digits, the last a check digit.
amountstringyesA positive amount with at most two decimal places.
currencystringyesISO-4217; must equal both wallets' currency.
transferCategoryenumyesp2p pays another customer's wallet; merchant pays a business collection wallet. Each counts against its own daily limit of the sender's KYC level. One of p2p, merchant.
clientReferencestringyesYour own reference; stored and returned verbatim, never interpreted; unique within your account.
assertionobjectyesThe sending customer's authentication for this transfer.
assertion.customerUidstringyesThe customer who authenticated.
assertion.identifierUidobjectnoThe customer identifier the customer authenticated with, if known.
assertion.assuranceLevelenumyesHow strongly your channel authenticated the customer. One of single_factor, two_factor, step_up.
assertion.factor1TypestringyesThe first factor used, e.g. pin, password, biometric. At most 32 characters.
assertion.factor2TypeobjectnoRequired unless assuranceLevel is single_factor. At most 32 characters.
assertion.authenticatedAtstringyesISO-8601 — when the customer authenticated.
assertion.expiresAtstringyesISO-8601; by default at most 10 minutes after authenticatedAt. An expired assertion is refused (422).
assertion.channelCodestringyesYour channel the customer authenticated on. At most 40 characters.
assertion.deviceHashobjectnoSHA-256 hex of the device fingerprint, if your channel has one.
assertion.sourceReferencestringyesOpaque channel evidence reference (a session or login id). Presenting one again while it is live is allowed only for the same customer. At most 160 characters.
assertion.transactionBindingobjectnoHash your channel computed over the request this assertion authorizes; a mismatch with the request received is refused.
assertion.transactionBinding.hashstringyesSHA-256 hex (64 characters) over the canonical string of this request.
assertion.transactionBinding.algorithmenumyesOne of sha256.
assertion.transactionBinding.canonicalenumyesThe field template hashed: the six values joined with |, UTF-8. The fifth is the payout destination, the mandate for a debit, or empty; clientReference is empty when absent. One of `customerUid
Example (placeholder values)
json
{
  "sourceWalletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "destinationAccountNumber": "6695731439148",
  "amount": "1000.00",
  "currency": "ETB",
  "transferCategory": "p2p",
  "clientReference": "string",
  "assertion": {
    "customerUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
    "assuranceLevel": "single_factor",
    "factor1Type": "string",
    "authenticatedAt": "2026-09-03T10:15:30.000Z",
    "expiresAt": "2026-09-03T10:15:30.000Z",
    "channelCode": "string",
    "sourceReference": "string"
  }
}

Response · 201 ​

FieldTypeDescription
uidstring
typestringOperation type, e.g. wallet_credit, wallet_debit, payout_b2c or topup_c2b.
statestringOperation state: created, awaiting_authorization, authorized, held, processing, pending_provider, confirmed, failed, reversed or completed.
amountstringA decimal amount with two decimal places.
feestringA decimal amount with two decimal places.
taxstringA decimal amount with two decimal places.
currencystringISO-4217.
clientReferencestring | nullYour reference, exactly as you supplied it.
sourceWalletUidstring | nullWallet transfers only: the wallet the money left.
destinationAccountNumberstring | nullWallet transfers only: the account number the money was sent to.
createdAtstring
completedAtstring | nullWhen the operation first reached a final state.
linksobject
links.selfstringAPI-relative path of this operation (GET /tenant/operations/{uid}).
links.receiptstringAPI-relative path of its receipt (GET /tenant/operations/{uid}/receipt).
Example (placeholder values)
json
{
  "uid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "type": "string",
  "state": "string",
  "amount": "2500.00",
  "fee": "12.50",
  "tax": "1.88",
  "currency": "ETB",
  "clientReference": "string",
  "sourceWalletUid": "01J9Z3K5X8QW2R4T6V8Y0B1C3D",
  "destinationAccountNumber": "string",
  "createdAt": "2026-09-03T10:15:30.000Z",
  "completedAt": "2026-09-03T10:15:30.000Z",
  "links": {
    "self": "string",
    "receipt": "string"
  }
}

Errors ​

400 401 403 404 409 422 429 500 503 — one body shape for all of them, see Errors.