Payouts overview
Send funds from your merchant balance to cards, phone numbers, wallets and PIX keys
Payouts send funds from your merchant balance to your users. Every destination uses the same endpoint and the same payout object; only the currency, paymentType and recipient fields differ.
Before you start
Authorization
How to sign your requests with the API token.
Payouts are debited from your merchant balance. Make sure the balance is sufficient before creating a payout, and ask your manager to enable the destinations you need.
How it works
Pick a destination
Each country and operator pair is a separate payout service. The list of services enabled for your account is returned by:
GET /v1/payouts/servicesPass the serviceId of the target destination when creating a payout. When serviceId is omitted, your default service is used.
Create the payout
One POST /v1/payouts request. The response is the payout object with the CREATED status. Store its id.
Track the result
We send a signed webhook on every status change. Polling GET /v1/payouts/{payoutId} is a fallback. The final status usually arrives within minutes.
Destinations
Brazil
PIX: random key, phone, email, CPF, CNPJ. Currency BRL.
Bangladesh
Bank and mobile wallets: Nagad, bKash, Rocket. Currency BDT.
Russia (SBP)
Transfers to phone numbers via SBP. Currency RUB.
West Africa
Mobile money: MTN, Orange, Wave, Moov and others. Currency XOF.
Steam
Top up a Steam account by login. RUB, KZT, UAH, USD.
Create a payout
POST /v1/payoutsRequest parameters
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | ✅ Yes | Payout amount |
currency | string | ✅ Yes | Currency code (ISO 4217) |
paymentType | string | ✅ Yes | Payment type. Allowed values depend on the destination |
account | object | ✅ Yes | Recipient details. Base fields are listed below; some destinations require extra fields |
serviceId | number | ❌ No | Destination service from GET /v1/payouts/services. When omitted, the default service is used |
note | string | ❌ No | Free-form note for the payout |
externalId | string | ❌ No | Payout ID in your system |
callbackUrl | string | ❌ No | Webhook URL for this payout. When omitted, the address from merchant settings is used |
account object
Base fields, required for every destination:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | ✅ Yes | Recipient name |
requisites | string | ✅ Yes | Recipient requisites: card number, phone number, PIX key, etc., depending on paymentType |
userId | string | ✅ Yes | User ID in your system |
Some destinations require extra recipient fields such as userEmail, userPhone, userIp or bankName. They are listed on the destination pages.
Request example
curl -X POST "https://api.riopay.online/v1/payouts" \
-H "Content-Type: application/json" \
-H "X-Api-Token: YOUR_API_TOKEN" \
-d '{
"amount": 1000.50,
"currency": "BRL",
"paymentType": "EMAIL",
"externalId": "payout-123",
"callbackUrl": "https://example.com/webhooks/payouts",
"account": {
"name": "John Doe",
"requisites": "john.doe@example.com",
"userId": "user_12345"
},
"note": "Payout for order #1234"
}'Response
A successful request returns the payout object. The same object is returned by the status endpoint.
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"merchantId": "123e4567-e89b-12d3-a456-426614174000",
"amount": "1000.50",
"account": {
"name": "John Doe",
"requisites": "4111111111111111",
"userId": "user_12345"
},
"status": "CREATED",
"type": "C2C",
"requisites": {},
"statusMessage": null,
"metadata": null,
"callbackUrl": null,
"createdAt": "2023-03-21T12:34:56Z",
"updatedAt": "2023-03-21T12:34:56Z",
"completedAt": null
}Payout object fields
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Payout ID. Use it to check the status |
merchantId | string (UUID) | Your merchant ID |
amount | string | Payout amount |
account | object | Recipient details as passed in the request |
status | string | Current payout status, see Payout statuses |
type | string | Payment type |
requisites | object | Additional requisites |
statusMessage | string / null | Status details, if any |
metadata | object / null | Additional data |
callbackUrl | string / null | Webhook URL for this payout (null — merchant settings are used) |
createdAt | string | Creation time (ISO 8601) |
updatedAt | string | Last update time (ISO 8601) |
completedAt | string / null | Completion time (ISO 8601) |
Payout statuses
| Status | Final | Description |
|---|---|---|
CREATED | No | Payout created |
PROCESSING | No | Payout is being processed |
COMPLETED | Conditionally | Payout completed successfully |
FAILED | Yes | Payout failed |
CANCELED | Yes | Payout canceled |
EXPIRED | Yes | Payout expired |
COMPLETED is final in normal operation. It changes only when the status is corrected manually because of an error on the bank side. The webhook for such a change is not sent automatically: request it from your manager.
CREATED does not trigger a webhook; the first notification arrives when the payout moves to PROCESSING or a final status.
Check payout status
GET /v1/payouts/{payoutId}curl -X GET "https://api.riopay.online/v1/payouts/123e4567-e89b-12d3-a456-426614174000" \
-H "X-Api-Token: YOUR_API_TOKEN"Use webhooks as the primary channel: pass callbackUrl in the request or set it in your merchant settings. Poll by ID only as a fallback, for example every 30 minutes for payouts that have not received a final status.
Recommendations
- Check your merchant balance before creating payouts.
- Store the payout
idfrom the response together with yourexternalId. - Use webhooks (
callbackUrl) and verify theX-Signatureheader; polling by ID is a fallback. - Handle every status value, including the final unsuccessful ones.
- Do not retry a payout automatically on
FAILEDwithout checking the reason instatusMessage.