Payouts
Send money from your balance to a mobile-money number or bank account, and check who holds an account first.
| Endpoint | What it does |
|---|---|
POST /v1/name-enquiries |
Look up who holds an account |
POST /v1/payouts |
Create a payout |
GET /v1/payouts/{id} |
Retrieve a payout |
Try any of them in the API tester.
Look up who holds an account
Section titled “Look up who holds an account”POST /v1/name-enquiries
Asks the bank or mobile-money network for the name on an account, before you pay out to it. The cheapest protection against a mistyped number: a payout to the wrong account cannot be recalled.
Send expected_name to have the answer compared for you; matches then says whether the holder is that person or business, ignoring case, punctuation, word order and middle initials.
Server-side only: a publishable key is refused, because the answer is a person’s name. A test key asks the sandbox, which echoes expected_name (or a fixed name); a number ending 0000 returns a different holder and one ending 9999 fails with partner_unavailable, so both branches can be tested.
Not every bank or network can be asked; those that cannot return 422.
Your pricing plan may set a fee per enquiry, shown on each response as fee_minor. Only an enquiry that returned a name is charged, from your available balance; if it cannot cover the fee the request is refused with 402 before anything is asked.
Authentication: a secret key (server-side only) with the scope verification.create.
Request body
Section titled “Request body”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
account_number |
string | yes | at least 5 characters; at most 30 characters; e.g. 0244123456 |
The mobile-money number or bank account number. |
bank |
string | no | at most 30 characters | Required for bank unless bank_id is sent. Codes are at /v1/reference/banks. |
bank_id |
string (uuid) | no | Alternative to bank; send one, not both. | |
channel |
string | yes | One of: mobile_money, bank |
What kind of account to ask about. |
expected_name |
string | no | at most 200 characters; e.g. Ama Mensah |
Optional. The name you expect. When sent, the response says whether the holder matches it (case, punctuation, word order and middle initials are ignored). |
momo_network |
string | no | at most 30 characters; e.g. mtn |
Required for mobile_money unless momo_network_id is sent. Codes are at /v1/reference/momo-networks. |
momo_network_id |
string (uuid) | no | Alternative to momo_network; send one, not both. |
curl -X POST https://sandbox.paysigna.com/v1/name-enquiries \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -d '{ "account_number": "0244123456", "channel": "mobile_money", "expected_name": "Ama Mensah", "momo_network": "mtn"}'Response 200
Section titled “Response 200”| Field | Type | What it is |
|---|---|---|
account_name |
string | The holder’s name as the bank or network holds it, usually upper case. |
account_number |
string | As sent. |
channel |
string | |
fee_currency |
string | |
fee_minor |
integer | What this enquiry cost, in minor units of fee_currency. 0 when your plan makes enquiries free. Only an enquiry that returned a name is charged. |
matches |
boolean | Present only when expected_name was sent: whether the holder is that person or business. |
object |
string |
Errors
Section titled “Errors”400: the request is malformed401: the key is missing, wrong or revoked403: the key lacks the scope, or your account may not do this422: a field is invalid;errorsnames it429: too many requests; wait forRetry-After500: Internal Server Error503: the payment partner is unreachable; safe to retry
Every error is a JSON problem with a stable code. See Errors.
Create a payout
Section titled “Create a payout”POST /v1/payouts
Sends money to a recipient over mobile money or a bank account.
YOUR BALANCE IS DEBITED WHEN THE PAYOUT IS CREATED, not when it completes. A payout that returns pending has already taken the funds out of your available balance.
Requires an Idempotency-Key header, and it matters more here than anywhere else in this API: a payout resent without one sends the money twice. A retry with the same key returns the original response.
If a request times out, READ the payout. Do not resend it. A timeout does not mean the money did not move, and the funds stay reserved until the partner tells us what happened.
The fee is always borne by you. amount is what the recipient receives; debited is what leaves your balance.
Authentication: a secret key (server-side only) with the scope payout.create.
Headers
Section titled “Headers”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
Idempotency-Key |
string | yes | Unique per operation. Resend the same value to retry safely — this matters more here than anywhere else in the API, because a payout retried without it sends the money twice. |
Request body
Section titled “Request body”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
amount |
integer | yes | min 1; e.g. 1250 |
What the RECIPIENT receives, in minor units — 1250 is GHS 12.50. Your balance is debited by this plus the fee. |
bank |
string | no | at most 30 characters | The bank’s code. Required for bank unless bank_id is sent instead. Codes are listed at /v1/reference/banks?payout_only=true. |
bank_branch_code |
string | no | at most 20 characters | Required by some GhIPSS transfers. |
bank_id |
string (uuid) | no | The bank’s id from /v1/reference/banks. An alternative to bank; send one, not both. | |
callback_url |
string (uri) | no | HTTPS only. | |
channel |
string | yes | One of: mobile_money, bank |
Where the money goes: mobile_money (a mobile-money wallet; send momo_network) or bank (a bank account; send bank). Payouts to a card are not supported. |
currency |
string | yes | exactly 3 characters; e.g. GHS |
The currency of amount, as an ISO 4217 code such as GHS. Your balance in this currency is debited. |
description |
string | no | at most 500 characters | May appear on the recipient’s statement, depending on the rail. |
metadata |
object | no | Returned unchanged on every read and webhook. | |
momo_network |
string | no | at most 30 characters; e.g. mtn |
The network’s code, such as mtn, telecel or airteltigo. Required for mobile_money unless momo_network_id is sent instead. Codes are listed at /v1/reference/momo-networks. |
momo_network_id |
string (uuid) | no | The network’s id from /v1/reference/momo-networks. An alternative to momo_network; send one, not both. | |
recipient |
object | yes | ||
recipient.identifier |
string | yes | at most 100 characters; e.g. 0244123456 |
Mobile-money number or bank account number. |
recipient.name |
string | no | at most 150 characters; e.g. Ama Mensah |
Strongly recommended. Some banks reject a transfer with no beneficiary name, and it is what appears on the recipient’s statement. |
reference |
string | no | at most 100 characters | Your own identifier for this payout. |
curl -X POST https://sandbox.paysigna.com/v1/payouts \ -H "Authorization: Bearer sk_test_…" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "amount": 1250, "channel": "mobile_money", "currency": "GHS", "momo_network": "mtn", "recipient": { "identifier": "0244123456", "name": "Ama Mensah" }}'Response 201
Section titled “Response 201”| Field | Type | What it is |
|---|---|---|
amount |
integer | What the recipient receives, in minor units. |
channel |
string | Where the money was sent: mobile_money or bank. |
completed_at |
string (date-time) | When the payout reached a final state. Absent while it is in flight. |
created_at |
string (date-time) | When the payout was created. |
currency |
string | ISO 4217 code of every amount on this payout. |
debited |
integer | What was taken from your balance: the amount plus the fee. |
failure_code |
string | A machine-readable reason, present when the payout failed. |
failure_message |
string | Present when the payout failed. The funds have been returned to your balance. |
fee |
object | |
fee.platform |
integer | PaySigna’s payout fee, in minor units. |
fee.tax |
integer | Tax charged on the fee, in minor units. |
id |
string | The payout’s id. Read it at /v1/payouts/{id}. |
metadata |
object | The metadata you sent, unchanged. |
object |
string | |
recipient |
object | |
recipient.masked |
string | A partial identifier. The full value is never returned. |
recipient.name |
string | The recipient name you sent. |
reference |
string | PaySigna’s reference. Quote it to support. |
status |
string | pending or processing means in flight — your balance is already debited. Wait for the webhook or read the payout; do not resend it. One of: initiated, pending, processing, succeeded, failed, cancelled, expired |
your_reference |
string | The reference you sent when creating the payout. |
Errors
Section titled “Errors”400: the request is malformed401: the key is missing, wrong or revoked402: your balance cannot cover it403: the key lacks the scope, or your account may not do this409: it conflicts with the current state, or the idempotency key was used for a different request422: a field is invalid;errorsnames it429: too many requests; wait forRetry-After500: Internal Server Error502: the payment partner refused it503: the payment partner is unreachable; safe to retry504: the partner timed out; read the resource before retrying
Every error is a JSON problem with a stable code. See Errors.
Retrieve a payout
Section titled “Retrieve a payout”GET /v1/payouts/{id}
Reads a payout’s current state. This is the authority after a timeout: if a create request did not return, read the payout rather than resending it.
Authentication: a secret key (server-side only) with the scope payout.read.
Parameters
Section titled “Parameters”| Field | Type | Required | Allowed values and limits | What it is |
|---|---|---|---|---|
{id} |
string (uuid) | yes | The payout’s id, as returned when it was created. |
Response 200
Section titled “Response 200”| Field | Type | What it is |
|---|---|---|
amount |
integer | What the recipient receives, in minor units. |
channel |
string | Where the money was sent: mobile_money or bank. |
completed_at |
string (date-time) | When the payout reached a final state. Absent while it is in flight. |
created_at |
string (date-time) | When the payout was created. |
currency |
string | ISO 4217 code of every amount on this payout. |
debited |
integer | What was taken from your balance: the amount plus the fee. |
failure_code |
string | A machine-readable reason, present when the payout failed. |
failure_message |
string | Present when the payout failed. The funds have been returned to your balance. |
fee |
object | |
fee.platform |
integer | PaySigna’s payout fee, in minor units. |
fee.tax |
integer | Tax charged on the fee, in minor units. |
id |
string | The payout’s id. Read it at /v1/payouts/{id}. |
metadata |
object | The metadata you sent, unchanged. |
object |
string | |
recipient |
object | |
recipient.masked |
string | A partial identifier. The full value is never returned. |
recipient.name |
string | The recipient name you sent. |
reference |
string | PaySigna’s reference. Quote it to support. |
status |
string | pending or processing means in flight — your balance is already debited. Wait for the webhook or read the payout; do not resend it. One of: initiated, pending, processing, succeeded, failed, cancelled, expired |
your_reference |
string | The reference you sent when creating the payout. |
Errors
Section titled “Errors”401: the key is missing, wrong or revoked403: the key lacks the scope, or your account may not do this404: no such resource on this account422: a field is invalid;errorsnames it429: too many requests; wait forRetry-After500: Internal Server Error503: the payment partner is unreachable; safe to retry
Every error is a JSON problem with a stable code. See Errors.

