# Pay out to mobile money and banks in Africa

Send money from your Payd balance to a mobile money wallet or a bank account. For M-Pesa numbers in Kenya, see [pay out with M-Pesa](./kenya-payouts.md).

<div class="endpoint"><span class="method post">POST</span><code>https://api.payd.money/api/v2/payments/payout</code></div>

## Where you can pay out

| Country | `country` | `currency` | Mobile money | Banks |
| --- | --- | --- | --- | --- |
| Kenya | `KE` | `KES` | [M-Pesa](./kenya-payouts.md) | Yes |
| Uganda | `UG` | `UGX` | Airtel Money, MTN MoMo | |
| Tanzania | `TZ` | `TZS` | Airtel Money, Halopesa, M-Pesa, Tigo Pesa | Yes |
| Rwanda | `RW` | `RWF` | MTN MoMo | |
| Zambia | `ZM` | `ZMW` | Airtel Money, MTN MoMo | |
| Nigeria | `NG` | `NGN` | | Yes |
| South Africa | `ZA` | `ZAR` | | Yes |
| Botswana | `BW` | `BWP` | Orange Money | Yes |
| Ethiopia | `ET` | `ETB` | | Yes |
| Côte d'Ivoire | `CI` | `XOF` | MTN MoMo, Moov Money, Orange Money, Wave | |
| Senegal | `SN` | `XOF` | Free Money, Orange Money, Wave | |
| Cameroon | `CM` | `XAF` | MTN MoMo, Orange Money | |
| Benin | `BJ` | `XOF` | MTN MoMo, Moov Money | |
| Togo | `TG` | `XOF` | Moov Money, TogoCell | |

Banks, networks, limits and settlement times change, and the API is the source of truth for all of them. Read them from [payment options by country](./network-discovery.md) before each payout. See [limits and settlement](./limits-and-settlement.md).

## Before you start

- Your balance must cover the amount plus the fee. Check both with a [quote](./quotes.md) using `operation_type` `withdrawal`.
- The person who owns the API key must have set a withdrawal PIN in the Payd app.
- Payouts to some countries need your account's identity details to be complete in the Payd app. If they are not, the payout fails with `Complete your KYC before sending money to this country`.

## 1. Pick the network or bank

Fetch the options for the recipient's country, and keep the entries for payouts:

```bash
curl https://api.payd.money/api/v2/payments/discovery/TZ \
  -u "$PAYD_API_USERNAME:$PAYD_API_PASSWORD"
```

Keep networks where `ramp_type` is `withdraw` and `status` is `active`. `channel_type` tells you the kind:

| `channel_type` | What it is | Send `transaction_channel` | Send `provider_config.account_type` |
| --- | --- | --- | --- |
| `momo` | A mobile money network | `mobile` | `momo` |
| `bank` or `eft` | A bank | `bank` | `bank` |

Show `network_name` to the user. Banks are listed one per bank, so a country can have hundreds: offer a search box. Keep the chosen entry's `network_id`, `channel_id`, `min_amount` and `max_amount`.

## 2. Send the payout

::: code-group

```bash [Mobile money]
curl https://api.payd.money/api/v2/payments/payout \
  -u "$PAYD_API_USERNAME:$PAYD_API_PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50000,
    "currency": "TZS",
    "country": "TZ",
    "transaction_channel": "mobile",
    "customer": {
      "first_name": "Neema",
      "last_name": "Mushi",
      "phone_number": "255754123456",
      "country": "TZ"
    },
    "vendor_config": { "account_id": "4f28836c-2f1e-4d9b-9a51-6f3c2b1d8e70" },
    "provider_config": {
      "network_id": "NETWORK_ID_FROM_DISCOVERY",
      "channel_id": "CHANNEL_ID_FROM_DISCOVERY",
      "account_type": "momo"
    },
    "payment_config": {
      "account_number": "255754123456",
      "remark": "October commission",
      "reference": "payout-1043",
      "callback_url": "https://example.com/webhooks/payd"
    }
  }'
```

```bash [Bank account]
curl https://api.payd.money/api/v2/payments/payout \
  -u "$PAYD_API_USERNAME:$PAYD_API_PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150000,
    "currency": "NGN",
    "country": "NG",
    "transaction_channel": "bank",
    "customer": {
      "first_name": "Chinedu",
      "last_name": "Okafor",
      "phone_number": "2348031234567",
      "country": "NG"
    },
    "vendor_config": { "account_id": "4f28836c-2f1e-4d9b-9a51-6f3c2b1d8e70" },
    "provider_config": {
      "network_id": "BANK_NETWORK_ID_FROM_DISCOVERY",
      "channel_id": "CHANNEL_ID_FROM_DISCOVERY",
      "account_type": "bank"
    },
    "payment_config": {
      "account_number": "0123456789",
      "remark": "Invoice 2207",
      "reference": "payout-1044",
      "callback_url": "https://example.com/webhooks/payd"
    }
  }'
```

```js [Node.js]
const auth = Buffer.from(
  `${process.env.PAYD_API_USERNAME}:${process.env.PAYD_API_PASSWORD}`,
).toString('base64')
const headers = { Authorization: `Basic ${auth}`, 'Content-Type': 'application/json' }

// Find the bank the user picked
const options = await fetch('https://api.payd.money/api/v2/payments/discovery/NG', { headers })
  .then((r) => r.json())
const bank = Object.values(options.provider_networks ?? {})
  .flatMap((group) => group.networks)
  .find((n) => n.ramp_type === 'withdraw' && n.status === 'active' && n.network_name === 'Kuda Microfinance Bank')

const res = await fetch('https://api.payd.money/api/v2/payments/payout', {
  method: 'POST',
  headers,
  body: JSON.stringify({
    amount: 150000,
    currency: 'NGN',
    country: 'NG',
    transaction_channel: 'bank',
    customer: { first_name: 'Chinedu', last_name: 'Okafor', phone_number: '2348031234567', country: 'NG' },
    vendor_config: { account_id: process.env.PAYD_ACCOUNT_ID },
    provider_config: { network_id: bank.network_id, channel_id: bank.channel_id, account_type: 'bank' },
    payment_config: {
      account_number: '0123456789',
      reference: 'payout-1044',
      callback_url: 'https://example.com/webhooks/payd',
    },
  }),
  signal: AbortSignal.timeout(35_000),
})
const body = await res.json()
if (!res.ok) throw new Error(`${body.error_code}: ${body.error_message}`)
console.log(body.gateway_response.merchant_reference)
```

```python [Python]
import os
import requests

AUTH = (os.environ["PAYD_API_USERNAME"], os.environ["PAYD_API_PASSWORD"])

options = requests.get("https://api.payd.money/api/v2/payments/discovery/NG", auth=AUTH, timeout=15).json()
bank = next(
    n
    for group in (options.get("provider_networks") or {}).values()
    for n in group["networks"]
    if n["ramp_type"] == "withdraw" and n["status"] == "active" and n["network_name"] == "Kuda Microfinance Bank"
)

res = requests.post(
    "https://api.payd.money/api/v2/payments/payout",
    auth=AUTH,
    json={
        "amount": 150000,
        "currency": "NGN",
        "country": "NG",
        "transaction_channel": "bank",
        "customer": {"first_name": "Chinedu", "last_name": "Okafor", "phone_number": "2348031234567", "country": "NG"},
        "vendor_config": {"account_id": os.environ["PAYD_ACCOUNT_ID"]},
        "provider_config": {"network_id": bank["network_id"], "channel_id": bank["channel_id"], "account_type": "bank"},
        "payment_config": {
            "account_number": "0123456789",
            "reference": "payout-1044",
            "callback_url": "https://example.com/webhooks/payd",
        },
    },
    timeout=35,
)
res.raise_for_status()
print(res.json()["gateway_response"]["merchant_reference"])
```

:::

### Fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount` | number | Yes | Amount the recipient gets, in the local currency, as a whole number, within the network's limits. |
| `currency` | string | Yes | The country's currency from the table above. |
| `country` | string | Yes | The two-letter country code. |
| `transaction_channel` | string | Yes | `mobile` or `bank`. |
| `customer.first_name`, `customer.last_name` | string | Yes | Recipient's name. For a bank payout, the name on the bank account. |
| `customer.phone_number` | string | Yes for mobile money | Recipient's number in international form without a `+`, such as `255754123456`. Send it for bank payouts too when you have it. |
| `customer.country` | string | No | The same country code. |
| `vendor_config.account_id` | string | Yes | Your [account id](./authentication.md#find-your-account-id). |
| `provider_config.network_id` | string | Yes | `network_id` of the chosen network or bank. |
| `provider_config.channel_id` | string | Yes | `channel_id` of its `withdraw` entry. |
| `provider_config.account_type` | string | Yes | `momo` or `bank`. See the table in step 1. |
| `payment_config.account_number` | string | Yes | For mobile money, the wallet number without a `+`. For a bank, the account number. |
| `payment_config.reference` | string | No | Your own id for the payout. |
| `payment_config.callback_url` | string | No | Where Payd posts the result. See [webhooks](./webhooks.md). |
| `payment_config.remark` | string | No | A short note kept with the payout. |
| `wallet_type` | string | No | Balance to pay from: `fiat` (default) or `crypto`. |

## Response and confirmation

The response has the same shape as an [M-Pesa payout](./kenya-payouts.md#response). Store `gateway_response.merchant_reference`, which ends in `eW`.

Payd posts `payout.succeeded` or `payout.failed` to your `callback_url` when the payout finishes. See [webhooks](./webhooks.md). If you set no `callback_url`, or a webhook does not arrive, [check the status](./transaction-status.md) until it is `success` or `failed`. Mobile money usually finishes within minutes. Banks can take longer, up to the next business day. A failed payout puts the money back in your balance.

## Nigeria

If your Payd account is registered in Nigeria, payouts need a second identity document, a BVN or NIN, on your account. Payd tells you with `Nigeria needs a second form of identification`. Email [support@paydhq.com](mailto:support@paydhq.com) to add one. Accounts registered outside Nigeria can pay Nigerian bank accounts without it.

## Common errors

| `error_code` | Message | Fix |
| --- | --- | --- |
| `insufficient_balance` | `insufficient balance` | Top up your balance or pay out less. |
| `invalid_argument` | `Complete your KYC before sending money to this country...` | Complete your identity details in the Payd app. |
| `invalid_form` | the network's reason | The payout was refused and the money is back in your balance. Check the account number and name. |
| `resource_not_found` | `no pricing configured...` | Payd does not offer this country, currency and channel together. |

See [errors](./errors.md) for every code.
