neo-pays

Payouts

Send funds from your balance to a mobile wallet or a bank account.

Create a payout

POST /v1/payouts

Pass an Idempotency-Key — it matters even more than on a collection: a payout replayed without idempotency would pay twice.

Field Required Description
asset_id yes The asset, by identifier
amount_minor yes String, in minor units
country yes Country of the operation (ISO 3166-1 alpha-2)
platform_id yes The paying platform (GET /v1/corridors)
recipient yes The recipient — see below
platform_account_handle The sub-account debited. Empty = the corridor's primary
rail Forces a specific rail
client_ref Your reference
description Free text

The recipient is typed

"recipient": {
  "name": "Mouhamadou Sy",
  "identifier_type": "msisdn",
  "identifier": "+221771234567"
}

identifier_type says what kind of address you hold (msisdn, iban, an alias…) and identifier carries it. That is what lets a platform carrying several rails resolve without you naming one.

phone alone remains fully supported and reads as (msisdn, phone). Supplying both forms, or a type with no value, is refused — never guessed at.

Balance first

A payout is debited from your available balance:

GET /v1/balances

Balances are returned per asset and per (platform, country), with the available, pending and reserved amounts. Reserved covers outgoing operations already committed: it is not spendable.

Balances never add up across assets. There is no currency conversion on this platform, so a total across all assets would mean nothing.

A payout that would exceed the available balance is refused immediately, in the response itself: 422 with code insufficient_funds. It is never queued waiting for funds — there is no overdraft.

Requesting a withdrawal of your balance

POST /v1/payout-requests
GET  /v1/payout-requests
GET  /v1/payout-requests/{id}

It is a request, not a payout. No money moves and nothing is reserved: the operation opens at status requested, and a person decides it in the dashboard. That is the maker-checker split — the API key asks, an account owner decides. There is deliberately no cancel: a pending request holds nothing, so it is rejected by the decider rather than withdrawn by you.

The amount is GROSS: withdrawal fees come out of it, they are not added on top. With no fee schedule configured for the asset the request is refused with 409 — a free withdrawal is a zero row somebody entered on purpose, never an absence.

Following up

payout.success, payout.failed and payout.cancelled arrive by webhook; GET /v1/payouts/{id} gives the state on demand.