> ## Documentation Index
> Fetch the complete documentation index at: https://docs.payaza.africa/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfers

> Send funds from your Payaza account to bank accounts and mobile wallets across Africa.

## Overview

Payaza Transfers lets you send money to any bank account or mobile wallet in Nigeria, Ghana, Kenya, and other supported countries. This guide walks through the full flow — from confirming the recipient's account name to initiating the payout and verifying the outcome.

The transfer flow has four steps:

<Steps>
  <Step title="Verify account name">
    Confirm the recipient's account name before sending (Nigeria and Ghana
    only).
  </Step>

  <Step title="Get your account reference">
    Fetch the `payazaAccountReference` for the currency you want to send from.
  </Step>

  <Step title="Initiate the transfer">
    Send the funds using the recipient details, amount, and your transaction
    PIN.
  </Step>

  <Step title="Confirm the outcome">
    Query the transaction status or listen for a webhook to confirm success or
    failure.
  </Step>
</Steps>

***

## Before you begin

You need the following before making your first transfer:

* A funded Payaza account for the currency you want to send from
* Your **public API key** encoded in Base64 — see the [Authentication guide](/guides/authentication)
* A webhook URL saved on the dashboard — see the [Webhooks guide](/guides/webhooks)
* Your **transaction PIN** set up on the dashboard — required to authorise every payout
* Your **server IP address whitelisted** on the dashboard — required before live payouts are processed

Set these headers on every request:

```json theme={null}
{
  "Authorization": "Payaza <Your public API key encoded in base 64>",
  "X-TenantID": "test",
  "Content-Type": "application/json"
}
```

<Note>
  Switch `X-TenantID` from `test` to `live` when you are ready to process real
  payouts.
</Note>

***

## Step 1 — Verify account name

<Note>
  This step applies to **Nigeria (NGN)** and **Ghana (GHS)** only. Skip to Step
  2 if you are sending to other countries.
</Note>

Resolve and display the recipient's account name before proceeding. This prevents sending funds to the wrong account and is required as a confirmation step before initiating a transfer.

```bash cURL theme={null}
curl --request POST \
  --url https://api.payaza.africa/live/payaza-account/api/v1/mainaccounts/merchant/provider/enquiry \
  --header 'Authorization: Payaza <Your public API key encoded in base 64>' \
  --header 'Content-Type: application/json' \
  --header 'X-TenantID: test' \
  --data '{
    "service_payload": {
      "currency": "NGN",
      "bank_code": "090123",
      "account_number": "0103937899"
    }
  }'
```

**Response**

```json theme={null}
{
  "response_code": 200,
  "response_message": "Approved or completely successful",
  "response_content": {
    "account_number": "0938573814",
    "bank_code": "090123",
    "account_name": "JOHN DOE",
    "account_status": "ACTIVE",
    "transaction_reference": 9
  },
  "response_content_class": "com.fin78.app.payload.response.enquiry.account.AccountNameEnquiryInfoResponse"
}
```

<Warning>
  Always display the resolved `account_name` to the user and require their
  confirmation before proceeding. Never auto-submit.
</Warning>

***

## Step 2 — Get your account reference

Retrieve the `payazaAccountReference` for the currency you want to send from. You will pass this as `account_reference` in your transfer request.

```bash cURL theme={null}
curl --request GET \
  --url https://api.payaza.africa/live/payaza-account/api/v1/mainaccounts/merchant/enquiry/main \
  --header 'Authorization: Payaza <Your public API key encoded in base 64>' \
  --header 'X-TenantID: test'
```

**Response**

```json theme={null}
{
  "message": "Account enquiry response",
  "status": true,
  "data": [
    {
      "id": 1,
      "accountName": "Test Merchant",
      "payazaAccountReference": "1010000000",
      "status": "ACTIVE",
      "accountBalance": 990.13,
      "businessId": 92,
      "currency": "NGN",
      "country": "NGA",
      "organizationName": "PAYAZA",
      "productCode": "PAYOUT-MAIN-NGN",
      "productNumber": "101",
      "postNoCredit": false,
      "postNoDebit": false,
      "originatorName": null,
      "pauseTransactions": null,
      "hasVirtualAccounts": true,
      "holdTransactionAtLowBalance": false,
      "virtualAccounts": [
        {
          "accountNumber": "99926838326",
          "accountName": "PAYAZA(Test Merchant)",
          "bankCode": "000023",
          "bankId": 306
        }
      ]
    }
  ]
}
```

<Note>
  The response contains one object per currency. Use the
  `payazaAccountReference` from the object whose `currency` matches your
  intended payout.
</Note>

***

## Step 3 — Initiate the transfer

### One-time live setup

Two steps are required before your **first live** payout. They only need to be done once.

<AccordionGroup>
  <Accordion title="1. Whitelist your server IP address">
    Live transfers are blocked until your server IP is whitelisted.

    <Steps>
      <Step title="Log in to the Payaza dashboard" />

      <Step title="Go to Settings → Developers → IP Whitelisting" />

      <Step title="Add your server IP address(es) and save" />
    </Steps>

    <Warning>
      IP whitelisting is only required for **live** transfers. Test environment transfers are not IP-restricted.
    </Warning>
  </Accordion>

  <Accordion title="2. Set your transaction PIN">
    A 6-digit PIN is required to authorise every transfer. Set it once on the dashboard.

    <Steps>
      <Step title="Log in to the Payaza dashboard" />

      <Step title="Go to Settings → Profile → Security" />

      <Step title="Click &#x22;Edit Details&#x22; then &#x22;Update Transaction PIN&#x22;" />
    </Steps>

    **Your PIN must have a unique value for each digit and must not include:**

    * Repeated digits — e.g. `111111`
    * Sequential digits — e.g. `123456` or `654321`
    * Repeating patterns — e.g. `121212` or `123123`

    -

    After setting your PIN, email [support@payaza.africa](mailto:support@payaza.africa) from your Super Admin email to request the PND (Post No Debit) restriction be lifted on your account.

    <Accordion title="Sample email">
      ```
      Good day Payaza Team,

      This is to confirm that I have set up my Transaction PIN on my Payaza account.
      Kindly remove the PND restriction on my account so that I can make payouts.

      My Payaza business name is: {{Your Payaza Business Name}}

      Thank you.
      ```
    </Accordion>
  </Accordion>
</AccordionGroup>

### Request

```bash cURL theme={null}
curl --request POST \
  --url https://api.payaza.africa/live/payout-receptor/payout \
  --header 'Authorization: Payaza <Your public API key encoded in base 64>' \
  --header 'Content-Type: application/json' \
  --header 'X-TenantID: live' \
  --data '{
    "transaction_type": "nuban",
    "service_payload": {
      "payout_amount": 100,
      "transaction_pin": 419374,
      "account_reference": "1010000009",
      "currency": "NGN",
      "country": "NGA",
      "payout_beneficiaries": [
        {
          "credit_amount": 100,
          "account_number": "9207067319",
          "account_name": "John Doe",
          "bank_code": "000013",
          "narration": "Test",
          "transaction_reference": "TD93001234",
          "sender": {
            "sender_name": "Jane Doe",
            "sender_id": "",
            "sender_phone_number": "01234595",
            "sender_address": "123, Ace Street"
          }
        }
      ]
    }
  }'
```

### Request parameters

| Parameter                                           | Type      | Required | Description                                                            |
| --------------------------------------------------- | --------- | -------- | ---------------------------------------------------------------------- |
| `transaction_type`                                  | `string`  | Yes      | Payment rail — use `"nuban"` for Nigerian bank accounts                |
| `service_payload.payout_amount`                     | `double`  | Yes      | Total amount to send. Must equal the sum of all `credit_amount` values |
| `service_payload.transaction_pin`                   | `integer` | Yes      | Your 6-digit transaction PIN                                           |
| `service_payload.account_reference`                 | `string`  | Yes      | Your Payaza account reference from Step 2                              |
| `service_payload.currency`                          | `string`  | Yes      | ISO currency code — e.g. `"NGN"`, `"GHS"`                              |
| `service_payload.country`                           | `string`  | Yes      | ISO country code — e.g. `"NGA"`, `"GHA"`                               |
| `payout_beneficiaries[].credit_amount`              | `double`  | Yes      | Amount to send to this recipient                                       |
| `payout_beneficiaries[].account_number`             | `string`  | Yes      | Recipient's bank account number                                        |
| `payout_beneficiaries[].account_name`               | `string`  | Yes      | Recipient's account name (from Step 1)                                 |
| `payout_beneficiaries[].bank_code`                  | `string`  | Yes      | Recipient's bank code                                                  |
| `payout_beneficiaries[].transaction_reference`      | `string`  | Yes      | Your unique reference for this transfer                                |
| `payout_beneficiaries[].narration`                  | `string`  | Yes      | Transfer description shown on the recipient's statement                |
| `payout_beneficiaries[].sender`                     | `object`  | No       | Sender information block                                               |
| `payout_beneficiaries[].sender.sender_name`         | `string`  | Yes      | Full name of the sender                                                |
| `payout_beneficiaries[].sender.sender_id`           | `string`  | No       | Sender's identification number (optional)                              |
| `payout_beneficiaries[].sender.sender_phone_number` | `string`  | Yes      | Sender's phone number                                                  |
| `payout_beneficiaries[].sender.sender_address`      | `string`  | Yes      | Sender's physical address                                              |

### `transaction_type` values

The `transaction_type` field controls which payment rail is used for the payout. Pass the value that matches the recipient's currency and intended transfer method.

| Currency | `transaction_type` | Description                            |
| -------- | ------------------ | -------------------------------------- |
| NGN      | `nuban`            | Nigerian bank account (NUBAN standard) |
| GHS      | `mobile_money`     | Ghana mobile money wallet              |
| GHS      | `ghipps`           | Ghana bank transfer                    |
| UGX      | `mobile_money`     | Uganda mobile money wallet             |
| TZS      | `mobile_money`     | Tanzania mobile money wallet           |
| TZS      | `tiss`             | Tanzania bank transfer                 |
| KES      | `mobile_money`     | Kenya mobile money wallet              |
| KES      | `kepss`            | Kenya bank transfer                    |
| XOF      | `mobile_money`     | XOF mobile money wallet                |
| XOF      | `wave`             | XOF Wave payouts                       |
| XAF      | `mobile_money`     | Cameroon mobile money wallet           |
| SLE      | `mobile_money`     | Sierra Leone mobile money wallet       |
| ZAR      | `RTC`              | South Africa Real-Time Clearing        |

### Response

<CodeGroup>
  ```json Single transfer theme={null}
  {
    "response_code": 200,
    "response_message": "Request successfully submitted",
    "response_content": {
      "transaction_status": "09",
      "narration": "Payout",
      "transaction_time": "2023-10-19T14:37:35.517809",
      "amount": 100,
      "response_status": "TRANSACTION_INITIATED",
      "response_description": "Transaction has been successfully submitted for processing"
    },
    "resp_code": "09"
  }
  ```

  ```json Bulk transfer theme={null}
  {
    "response_code": 0,
    "response_content": {
      "message": "Processing Transaction",
      "batch_reference": "9M728C1QW5694OP2B098652C33623878",
      "response_code": "09"
    }
  }
  ```
</CodeGroup>

<Warning>
  Always generate a **unique** `transaction_reference` per transfer. Reusing a
  reference will return a duplicate transaction error and the transfer will be
  rejected.
</Warning>

***

### Creating Transfer API requests using a Signature Header.

Kindly follow the steps below to create Transfer API requests using a signature header

## Step 1:

* **Create an x-payaza signature value from your Tranfer API request body using the format below.**

<CodeGroup>
  ```json Creating A Signature Header theme={null}
  /**
   * PAYAZA PAYOUT ENCRYPTION GUIDE
   * --------------------------------
   * This script demonstrates how to securely hash your payout request payload
   * using HMAC-SHA512 before sending it to the Payaza API.
   */

  const crypto = require("crypto");

  // Define the request body
  const requestBody = JSON.stringify({{payoutRequestBody}});

  // Secret key used for hashing
  const secretKey = "{{secretKey}}"; //The secret key is not base 64 encoded

  // Function to generate HMAC-SHA512 signature
  function hmacSHA512(data, secretKey) {
   return crypto
    .createHmac("sha512", secretKey) // Use SHA512
    .update(data, "utf8") // Encode as UTF-8
    .digest("base64"); // Encode output as Base64
  }

  // Generate Computed signature
  const computedSignature = hmacSHA512(requestBody, secretKey);
  console.log("Payout: \n", computedSignature, "\n");
  ```

  ```json Example theme={null}
  const crypto = require("crypto");

  // Define the EXACT payload that will be sent to the API.
  // IMPORTANT: The string you hash here must be identical to the raw JSON body sent in your HTTP request. Even a difference in whitespace will result in an Invalid Signature.
  const requestBody = JSON.stringify({
      "transaction_type": "nuban",
      "service_payload": {
          "payout_amount": 50,
          "transaction_pin": "114299",
          "account_reference": "1010005557",
          "currency": "NGN",
          "country": "NGA",
          "payout_beneficiaries": [
              {
                  "credit_amount": 50,
                  "account_number": "8185809571",
                  "account_name": "John Doe",
                  "narration": "Test",
                  "transaction_reference": "Testsignature1",
                  "sender": {
                      "sender_name": "Jane Doe",
                      "sender_id": "383",
                      "sender_phone_number": "01234595",
                      "sender_address": "123, Ace Street"
                  }
              }
          ]
      }
  });

  const secretKey = "PZ78-SKLIVE-A0213D96-1B3E-4367-8EEE-AB94D6E70B3B"; //The secret key is not base 64 encoded

  // Function to generate HMAC-SHA512 signature
  function hmacSHA512(data, secretKey) {
   return crypto
    .createHmac("sha512", secretKey) // Use SHA512
    .update(data, "utf8") // Encode as UTF-8
    .digest("base64"); // Encode output as Base64
  }

  // Generate Computed signature
  const computedSignature = hmacSHA512(requestBody, secretKey);
  console.log("Payout: \n", computedSignature, "\n");
  ```
</CodeGroup>

## Step 2:

* **Pass the signature value created using the format in Step 1 as an Authorization header in your API request.**

| Authorization Header | Meaning                                          |
| -------------------- | ------------------------------------------------ |
| `Authorization`      | Payaza \[\[Public API Key encoded in base 64]]   |
| `X-TenantID`         | `live` or `test`                                 |
| `X-Payaza-Signature` | `The signature value that was created in Step 1` |

<Note>
  Kindly note that your Payaza account has to be activated before this API
  request method can be used. Kindly reach out to our team at
  [support@payaza.africa](mailto:support@payaza.africa) regarding this.
</Note>

## Step 4 — Verify the transfer

Use this endpoint to confirm the outcome of a transfer. Call it after initiating, or as a fallback if a webhook is not received.

```bash cURL theme={null}
curl --request GET \
  --url 'https://api.payaza.africa/live/payaza-account/api/v1/mainaccounts/transaction/status?transaction_reference=PBF452009112709121334899870' \
  --header 'Authorization: Payaza <Your public API key encoded in base 64>' \
  --header 'X-TenantID: test'
```

**Response**

```json theme={null}
{
  "message": "Transaction fetched",
  "status": true,
  "retry_count": 0,
  "data": {
    "transactionDateTime": "2025-11-27T09:31:21.334166",
    "transactionReference": "PBF452009112709121334899870",
    "creditAccount": "0148094596",
    "bankCode": "000013",
    "beneficiaryName": "BELLO  EMMANUEL EVESHODIAME",
    "transactionAmount": 13.0,
    "fee": 1.0,
    "transactionStatus": "NIP_SUCCESS",
    "transactionType": "DEBIT",
    "responseMessage": "Approved or Completely Successful",
    "responseCode": "00",
    "currency": "NGN",
    "balanceBefore": 177.1,
    "balanceAfter": 163.1,
    "duration": {}
  }
}
```

### Transaction statuses

| Status                  | Meaning                                                        |
| ----------------------- | -------------------------------------------------------------- |
| `TRANSACTION_INITIATED` | Received and queued for processing                             |
| `NIP_SUCCESS`           | Transfer successful — funds delivered                          |
| `NIP_PENDING`           | Still in progress                                              |
| `NIP_FAILURE`           | Transfer failed                                                |
| `ESCROW_SUCCESS`        | Amount deducted but awaiting bank processing — can be reversed |

***

## Webhook events

Payaza sends a webhook for every completed or failed transfer. Use webhooks as your primary notification mechanism and the status query endpoint only as a fallback.

| Event               | When it fires                          |
| ------------------- | -------------------------------------- |
| Successful transfer | Transfer processed and funds delivered |
| Failed transfer     | Transfer attempt failed                |

<CodeGroup>
  ```json Successful transfer theme={null}
  {
    "transaction_reference": "PTSA1220246261518348000",
    "transaction_type": "DEBIT",
    "transaction_status": "NIP_SUCCESS",
    "transaction_fee": 10.0,
    "amount_received": 20.0,
    "sent_to": {
      "account_name": "John Doe",
      "account_number": "1234567890",
      "bank_name": "EASTWE BANK"
    },
    "initiated_date": "2024-06-26 16:44:08.718",
    "current_status_date": "2024-06-26 16:44:08.718",
    "is_reversed": false,
    "response_message": "Approved or Completely Successful",
    "response_code": "00",
    "currency": "NGN",
    "country": "NGA",
    "session_id": "999999213419011273456123134124"
  }
  ```

  ```json Failed transfer theme={null}
  {
    "transaction_reference": "PTSA1220246261518348001",
    "transaction_type": "DEBIT",
    "transaction_status": "NIP_FAILURE",
    "transaction_fee": 100,
    "amount_received": 50000,
    "sent_to": {
      "account_name": "John Doe",
      "account_number": "1234567890",
      "bank_name": "EASTWE BANK"
    },
    "initiated_date": "2024-06-26 16:44:08.718",
    "current_status_date": "2024-06-26 16:44:08.718",
    "is_reversed": true,
    "response_message": "Invalid Account",
    "response_code": "07",
    "currency": "NGN",
    "country": "NGA"
  }
  ```
</CodeGroup>

<Note>
  Always verify the webhook signature before processing the payload. See the
  [Webhooks guide](/guides/webhooks) for signature verification steps.
</Note>

***

## Key reminders

* Your Payaza account must be funded before initiating a transfer
* Generate a unique `transaction_reference` for every transfer attempt
* Transfers are **not retried automatically** — your application must handle retry logic
* IP whitelisting is required for live payouts only — not enforced in test
* If `postNoDebit` is `true` on your account, transfers will be blocked — contact [support@payaza.africa](mailto:support@payaza.africa)

***

## What's next

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Set up signature verification and handle transfer event payloads.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/guides/errors">
    A reference for all transfer error responses and how to fix them.
  </Card>

  <Card title="Virtual accounts" icon="building-columns" href="/guides/virtual-accounts">
    Collect inbound payments via dynamic or reserved virtual account numbers.
  </Card>

  <Card title="Payaza account" icon="building" href="/guides/payaza-account">
    View your account balance and reference across all currencies.
  </Card>
</CardGroup>
