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

# Initiate Payout

> Create a payout to send funds to a beneficiary's bank account via API.

## Overview

Creates a payout for external partners via API. The system automatically handles FX conversion:

* **No pre-funding required** - source currency can differ from destination currency
* FX conversion happens automatically
* Payouts start in `PENDING` status

<Note>
  **Supporting documents:** For most payouts, attach an invoice so compliance can review the payment. Payouts **without** an invoice **will not be processed**, except **withdrawals**—transfers to your business's own account or to an account belonging to one of your Ultimate Beneficial Owners (UBOs).

  You can:

  * **Attach at creation** — upload via [Get Document Upload URL](/api-reference/endpoint/upload-document-url), then pass `documentUrl` as `supportingDocument`, or
  * **Attach later** — omit `supportingDocument` on initiate and use [Add Supporting Documents](/api-reference/endpoint/add-documents) before the payout is processed.

  See [Uploading Supporting Documents](/guides/uploading-supporting-documents) for the upload flow.

  If you have **multiple files** to attach at creation, merge them into **one PDF** before uploading to S3 and passing `documentUrl` as `supportingDocument`.
</Note>

## Required Headers

| Header            | Required | Description                                                                                                              |
| ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
| `Idempotency-Key` | **Yes**  | **Client-generated unique key** to prevent duplicate payouts. Generate using UUID or order ID before making the request. |
| `x-api-key`       | Yes      | Your API key                                                                                                             |

<Warning>
  **Idempotency-Key is required.** Missing header returns `400 Bad Request`. Generate the key client-side before making the API call.
</Warning>

## Request

```http theme={null}
POST /v1/payout-api/payouts/initiate HTTP/1.1
Host: gateway.staging.useyala.com
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
x-api-key: <YOUR_API_KEY>
```

### Request Body

```json theme={null}
{
  "sourceAmount": 10000,
  "sourceCurrency": "NGN",
  "destinationCurrency": "CNY",
  "destinationCountryCode": "CHN",
  "method": "ALIPAY",
      "beneficiary": {
        "accountName": "Li Wei",
        "accountCurrency": "CNY",
        "alipayId": "user@alipay.com"
      },
      "narration": "Payment for services",
      "supportingDocument": "https://yala-payout-api-docs-staging.s3.us-east-1.amazonaws.com/staging/payout-api/<businessId>/<documentId>/invoice.pdf"
    }
```

### Request Fields

| Field                    | Type         | Required | Description                                                                                                                                                                                                                                                                                                                                             |
| ------------------------ | ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sourceAmount`           | number       | Yes      | Amount to debit from source wallet                                                                                                                                                                                                                                                                                                                      |
| `sourceCurrency`         | string       | Yes      | Source wallet currency (e.g., "NGN", "USD"). **Debit is always from this currency's wallet**—the system debits the business wallet that matches this currency.                                                                                                                                                                                          |
| `destinationCurrency`    | string       | Yes      | Destination currency (e.g., "CNY", "NGN")                                                                                                                                                                                                                                                                                                               |
| `destinationCountryCode` | string       | Yes      | ISO country code (3-letter ISO 3166-1 alpha-3, e.g., "CHN" for China, "NGA" for Nigeria, "USA" for United States)                                                                                                                                                                                                                                       |
| `method`                 | string       | Yes      | Payment method (e.g., "ALIPAY", "SWIFT", "NIP")                                                                                                                                                                                                                                                                                                         |
| `beneficiary`            | object       | Yes      | Beneficiary details (fields depend on method - see `/methods` endpoint)                                                                                                                                                                                                                                                                                 |
| `narration`              | string       | Yes      | Payment description                                                                                                                                                                                                                                                                                                                                     |
| `supportingDocument`     | string (URL) | No\*     | HTTPS URL for an invoice. Use the `documentUrl` from [POST /documents/upload-url](/api-reference/endpoint/upload-document-url) after uploading the file to S3. Required for processing on most payouts; not required for withdrawals to your business account or a UBO's account. May be attached at initiate or later via PATCH.                       |
| `remitterAddress`        | string       | No       | Optional top-level field on `POST /initiate` (not inside `beneficiary`). Provide the payer's address when the **remitter is not the business onboarded on Yala**. When omitted, Yala assumes the onboarded business is the remitter and uses their verified address on file. Available for all payment methods; see `optionalFields` in `GET /methods`. |

**Beneficiary Fields:**

The beneficiary object has a **unified shape** - all fields are optional except `accountName` and `accountCurrency`. Required fields vary by payment method and are validated at the service level.

**Key Points:**

* ✅ **Same structure for all methods** - The beneficiary object always has the same fields available
* ✅ **Method-specific requirements** - Use `GET /methods` endpoint to discover which fields are required for your payment method
* ✅ **Automatic country derivation** - If `beneficiary.countryCode` is not provided, it will be automatically derived from `destinationCountryCode` in the request body
* ✅ **Service-level validation** - The API validates that all required fields for your selected method are present

**Example:** For ALIPAY, you only need `accountName`, `accountCurrency`, and `alipayId`. All other fields can be omitted.

## Response

```json theme={null}
{
  "id": "payout-id-uuid",
  "payoutRef": "PAY-2024-001",
  "businessId": "business-id-uuid",
  "sourceAmount": 10000,
  "sourceCurrency": "NGN",
  "destinationAmount": 42.5,
  "destinationCurrency": "CNY",
  "exchangeRate": 235.0,
  "transactionFees": 0,
  "status": "PENDING",
  "method": "ALIPAY",
  "narration": "Payment for services",
  "createdAt": "2024-01-20T10:00:00Z",
  "updatedAt": "2024-01-20T10:00:00Z"
}
```

### Response Fields

| Field                 | Type              | Description                                                                                                                                                                                                                                                                                    |
| --------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  | string (UUID)     | **Payout identifier - Save this ID** to track status via `GET /:id` or webhook notifications                                                                                                                                                                                                   |
| `payoutRef`           | string            | Human-readable payout reference                                                                                                                                                                                                                                                                |
| `sourceAmount`        | number            | Amount debited from source wallet                                                                                                                                                                                                                                                              |
| `sourceCurrency`      | string            | Source currency                                                                                                                                                                                                                                                                                |
| `destinationAmount`   | number            | Amount sent to beneficiary (after FX conversion)                                                                                                                                                                                                                                               |
| `destinationCurrency` | string            | Destination currency                                                                                                                                                                                                                                                                           |
| `exchangeRate`        | number            | **Actual exchange rate used for conversion** - This is the rate that was applied at the time of payout initiation. If you previously viewed rates via `GET /pairs` or `GET /methods`, those rates were for reference only. Always check this field to confirm the rate that was actually used. |
| `transactionFees`     | number            | Transaction fees (currently 0)                                                                                                                                                                                                                                                                 |
| `status`              | string            | One of: `PENDING`, `ADDITIONAL_INFO_REQUIRED`, `PROCESSING`, `SUCCESSFUL`, `FAILED`, `REJECTED`                                                                                                                                                                                                |
| `method`              | string            | Payment method used                                                                                                                                                                                                                                                                            |
| `narration`           | string            | Payment description                                                                                                                                                                                                                                                                            |
| `createdAt`           | string (ISO 8601) | Payout creation timestamp                                                                                                                                                                                                                                                                      |
| `updatedAt`           | string (ISO 8601) | Last update timestamp                                                                                                                                                                                                                                                                          |

<Tip>
  **Important:** Save the `id` field from this response. Use it to:

  * Query payout status via `GET /v1/payout-api/payouts/{id}`
  * Match webhook notifications (webhook payload includes `payoutId` matching this `id`)
</Tip>

## Idempotency

**How it works:**

1. **Generate key client-side** before making the request:
   ```javascript theme={null}
   const idempotencyKey = crypto.randomUUID();
   // OR
   const idempotencyKey = `order-${orderId}`;
   ```

2. **Include in header:**
   ```javascript theme={null}
   headers: {
     'Idempotency-Key': idempotencyKey,
   }
   ```

3. **Retry with same key** if request fails:
   * Network error/timeout: Retry with **same key + same body**
   * API returns original payout (no duplicate created)

**Behavior:**

* **Same key + same request body**: Returns existing payout (no duplicate)
* **Same key + different request body**: `409 Conflict` (use a new key)
* **Missing key**: `400 Bad Request`

## Error Responses

### 400 Bad Request - Missing Idempotency Key

```json theme={null}
{
  "statusCode": 400,
  "message": "Idempotency-Key header is required",
  "error": "MISSING_IDEMPOTENCY_KEY"
}
```

### 400 Bad Request - Missing Required Fields

```json theme={null}
{
  "statusCode": 400,
  "message": "Missing required fields: alipayId",
  "error": "MISSING_REQUIRED_FIELDS"
}
```

**Solution:** Use `GET /methods` endpoint to get required fields for your payment method. Required fields vary by method:

* **ALIPAY**: `accountName`, `alipayId`
* **WECHAT**: `accountName`, and one of: `openId` OR `wechatUserId`
* **SEPA**: `accountName`, `iban`
* **SWIFT**: `accountName`, `accountNumber`, `swiftCode` (required), `bankName`, `beneficiaryCountry` (required), `iban` (optional), `intermediarySwift` (optional), `address` (optional), `city` (optional), `postCode` (optional)
* **ACH**: `accountName`, `routingNumber`, `accountNumber`, `accountType` (enum: "checking" or "savings")
* **NIP**: `accountName`, `accountNumber`, `bankCode`
* **BACS/FASTER\_PAYMENTS**: `accountName`, `sortCode`, `accountNumber`, `iban` (beneficiary)
* **HONG\_KONG\_FPS**: `accountName`, and one of: `fpsId` OR `phoneNumber` OR `email` OR (`accountNumber` + `bankCode`)

**Optional top-level (all methods):** `remitterAddress` — use when the payer is not your onboarded business (see field table above).

### 400 Bad Request - Insufficient Funds

```json theme={null}
{
  "statusCode": 400,
  "message": "Insufficient balance in source wallet",
  "error": "INSUFFICIENT_FUNDS"
}
```

### 409 Conflict - Idempotency Key Reused

```json theme={null}
{
  "statusCode": 409,
  "message": "Idempotency key already used with a different request. Use a new key or retry with the same request body.",
  "error": "IDEMPOTENCY_KEY_CONFLICT"
}
```

**Solution:** Generate a new idempotency key for this payout, or retry with the exact same request body.

### 401 Unauthorized

```json theme={null}
{
  "statusCode": 401,
  "message": "Unauthorized"
}
```

## Usage Example

```javascript theme={null}
// 1. Generate idempotency key (client-side)
const idempotencyKey = crypto.randomUUID();

// 2. Get required fields for method (optional, if you need to validate)
const methodsResponse = await fetch(
  'https://gateway.staging.useyala.com/v1/payout-api/payouts/methods?sourceCurrency=NGN&destinationCurrency=CNY&destinationCountryCode=CHN',
  {
    headers: { 'x-api-key': apiKey }
  }
);
const { methods } = await methodsResponse.json();
const alipayMethod = methods.find(m => m.code === 'ALIPAY');

// 3. Create payout
const response = await fetch(
  'https://gateway.staging.useyala.com/v1/payout-api/payouts/initiate',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey,  // Client-generated
      'x-api-key': apiKey,
    },
    body: JSON.stringify({
      sourceAmount: 10000,
      sourceCurrency: 'NGN',
      destinationCurrency: 'CNY',
      destinationCountryCode: 'CHN',
      method: 'ALIPAY',
      beneficiary: {
        accountName: 'Li Wei',
        accountCurrency: 'CNY',
        alipayId: 'user@alipay.com',
      },
      narration: 'Payment for services',
      supportingDocument: 'https://example.com/invoice.pdf',
    }),
  }
);

const payout = await response.json();
console.log(`Payout created: ${payout.id}, Status: ${payout.status}`);

// 4. Store payout.id for tracking
// Use payout.id to query status via GET /:id or receive webhook notifications

// 5. If request fails, retry with SAME key + SAME body
// API will return the original payout (no duplicate)
```

<Tip>
  **Save the `id` field from the response** - Use this payout ID to track status via `GET /:id` or receive webhook notifications when status changes.
</Tip>

## Webhooks

When a payout's status changes, we send a webhook notification to your configured endpoint. The webhook payload includes the payout `id` so you can track which payout was updated.

**To enable webhooks:**

1. Activate webhook notifications in your Yala dashboard
2. Configure your webhook endpoint URL
3. You'll receive `payout.status.changed.v1` events when status changes

**Webhook payload includes:**

* `payoutId` - The payout ID (use this to match with your records)
* `oldStatus` - Previous status
* `newStatus` - New status (one of `PENDING`, `ADDITIONAL_INFO_REQUIRED`, `PROCESSING`, `SUCCESSFUL`, `FAILED`, `REJECTED`)
* `changedAt` - Timestamp of status change
* `reason` - Human-readable reason for the change

## Exchange Rate Behavior

**Important:** The exchange rate used for conversion is determined at the time of payout initiation.

* **Rate is fetched fresh** - When you call `POST /initiate`, the system fetches the current exchange rate from our rate database
* **Rate may differ from preview** - If you previously viewed rates via `GET /pairs` or `GET /methods`, those rates were for reference only. The actual rate applied may be different if rates changed between viewing and initiating
* **Check the response** - The `exchangeRate` field in this response shows the rate that was actually used for your payout
* **Rate is not locked** - There is no rate guarantee or locking mechanism. The rate used is whatever is current at initiation time

**Best Practice:** Always display the `exchangeRate` from the `/initiate` response to your users to show them the actual rate that was applied, rather than relying on rates shown in preview endpoints.

## Notes

* **FX Conversion:** Happens automatically (no pre-funding required)
* **Status:** Payouts start in `PENDING` status
* **Field Validation:** Beneficiary fields are validated against method requirements
* **Tracking:** Save the `id` field from the response to track payout status
* **Fees:** Currently disabled
* **Exchange Rate:** Always check the `exchangeRate` field in the response to see the rate that was actually applied


## OpenAPI

````yaml POST /v1/payout-api/payouts/initiate
openapi: 3.0.3
info:
  title: Yala Payout APIs
  description: >-
    Yala Payout API documentation. This comprehensive API suite enables seamless
    integration with our financial services platform, providing secure and
    efficient access to cross-border payment solutions.


    <details>

    <summary><b>✨ Key Features</b></summary>


    - Real-time payment processing

    - Secure wallet management

    - Comprehensive exchange rate calculations

    - Robust transaction monitoring

    </details>


    <details>

    <summary><b>💰 Payment Methods and Fees</b></summary>


    ## USD Payments


    | Destination | Method | Fees | Settlement Time |

    |------------|---------|------|-----------------|

    | United States | Wire | $20 | 1 day |

    | | ACH | $5.00 | 1 to 3 days |

    | China, Hong Kong | China wire/HK FPS | $20 | 24 to 72 hours |

    | Other Countries | Swift | $50 (<$2k)<br>$40 ($2k-$10k)<br>$0 (≥$10k) | 24
    to 72 hours |


    ### EUR Payments


    | Destination | Method | Fees | Settlement Time |

    |------------|---------|------|-----------------|

    | EU Countries | SEPA | $5.00 | < 48 hrs |

    | Other Countries | Swift | $50 (<$2k)<br>$40 ($2k-$10k)<br>$0 (≥$10k) | 24
    to 72 hours |


    ### GBP Payments


    | Destination | Method | Fees | Settlement Time |

    |------------|---------|------|-----------------|

    | United Kingdom | Faster Payments/BACS | $2.00 | < 24 hrs |

    | Other Countries | Swift | $50 (<$2k)<br>$40 ($2k-$10k)<br>$0 (≥$10k) | 24
    to 72 hours |


    ### Other Currencies


    | Currency | Destination | Method | Fees | Settlement Time |

    |----------|------------|---------|------|-----------------|

    | NGN | Nigeria | NIP | N100 | < 24 hrs |

    | CNY | All countries | Swift | $50 (<$2k)<br>$40 ($2k-$10k)<br>$0 (≥$10k) |
    24 to 72 hours |

    | HKD | Hong Kong | FPS | $20 | < 24 hrs |

    | Multiple* | Any Country | Swift | $50 (<$2k)<br>$40 ($2k-$10k)<br>$0
    (≥$10k) | 24 to 72 hours |


    *Includes: GHS, XAF, ZAR, XOF, CAD, KES, UGX, INR

    </details>


    For additional support or integration assistance, please contact our
    developer support team.
  version: 1.0.0
  termsOfService: https://useyala.com/
  contact:
    name: API Support
    email: developers@useyala.com
servers:
  - url: https://gateway.useyala.com
    description: Production server
  - url: https://gateway.staging.useyala.com
    description: Staging (Sandbox) server
security:
  - ApiKeyAuth: []
tags:
  - name: Payouts
    description: Operations related to payouts
  - name: Countries
    description: Operations to fetch supported countries and currency information
  - name: Transactions
    description: Operations to fetch transaction history and details
  - name: Webhooks
    description: Register and manage webhook URLs for payout status notifications
paths:
  /v1/payout-api/payouts/initiate:
    post:
      tags:
        - Payouts
      summary: Initiate payout
      description: Create a payout. Idempotency-Key header is required.
      operationId: initiatePayout
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            example: 550e8400-e29b-41d4-a716-446655440000
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PayoutInitiateRequest'
      responses:
        '201':
          description: Payout initiated successfully
        '400':
          description: Bad Request - Invalid input data
        '401':
          description: Unauthorized - Invalid API key
components:
  schemas:
    PayoutInitiateRequest:
      type: object
      required:
        - businessId
        - sourceAmount
        - sourceCurrency
        - destinationCurrency
        - method
        - destinationCountryCode
        - narration
        - beneficiary
      properties:
        businessId:
          type: string
          format: uuid
          example: 6fd2b69f-c529-4fe4-b680-29256f860553
          description: Unique identifier for the business
        sourceAmount:
          type: number
          example: 1000
          description: Amount in the source currency
        sourceCurrency:
          type: string
          example: USD
          description: Source currency code
        destinationCurrency:
          type: string
          example: NGN
          description: Destination currency code
        method:
          type: string
          enum:
            - SWIFT
            - NIP
            - HK_FPS
            - CHINA_WIRE
            - FASTER_PAYMENTS
            - WIRE
            - ACH
            - SEPA
          example: NIP
          description: Payment method
        destinationCountryCode:
          type: string
          example: NGA
          description: Destination country code
        supportingDocument:
          type: string
          format: uri
          example: https://sample.com/doc
          description: URL to supporting document
        narration:
          type: string
          example: Andromeda Galaxy
          description: Transaction description
        remitterAddress:
          type: string
          example: 10 Downing Street, London, SW1A 2AA
          description: >-
            Optional for all payment methods. Top-level payer address on the
            initiate body (not inside beneficiary). Provide when the remitter is
            not the business onboarded on Yala; when omitted, Yala uses the
            onboarded business verified address on file.
        beneficiary:
          $ref: '#/components/schemas/Beneficiary'
    Beneficiary:
      type: object
      required:
        - countryCode
        - bankCountryCode
        - accountNumber
        - accountName
        - bankName
        - accountCurrency
        - payoutMethod
        - isIndividual
      properties:
        countryCode:
          type: string
          example: NGA
          description: Country code of beneficiary
        bankCountryCode:
          type: string
          example: NGA
          description: Country code of beneficiary's bank
        accountNumber:
          type: string
          example: '8068487823'
          description: Beneficiary's account number
        accountName:
          type: string
          example: Will Wonker
          description: Beneficiary's account name
        bankName:
          type: string
          example: OPay
          description: Name of beneficiary's bank
        accountCurrency:
          type: string
          example: NGN
          description: Currency of beneficiary's account
        address:
          type: string
          example: 2, 4th Avenue, Off Prince Ademola Eletu way, Jakande, Lekki
          description: Beneficiary's address
        city:
          type: string
          example: Lekki
          description: Beneficiary's city
        postCode:
          type: string
          example: SG1 5LH
          description: Beneficiary's postal code
        payoutMethod:
          type: string
          enum:
            - SWIFT
            - NIP
            - HK_FPS
            - CHINA_WIRE
            - FASTER_PAYMENTS
            - WIRE
            - ACH
            - SEPA
          example: SWIFT
          description: Payment method for this beneficiary
        swiftNumber:
          type: string
          example: '34323232'
          description: SWIFT/BIC code
        routingNumber:
          type: string
          example: ''
          description: Routing number (for US banks)
        sortCode:
          type: string
          example: '111111'
          description: Sort code (for UK banks)
        iban:
          type: string
          example: GB82WEST12345698765432
          description: >-
            IBAN (required for SEPA, BACS, and FASTER_PAYMENTS; optional for
            SWIFT)
        phoneNumber:
          type: string
          example: ''
          description: Beneficiary's phone number
        email:
          type: string
          format: email
          example: ''
          description: Beneficiary's email address
        bankBranchCode:
          type: string
          example: ''
          description: Bank branch code
        bankAddress:
          type: string
          example: ''
          description: Bank address
        bankCode:
          type: string
          example: '34323232'
          description: Bank code
        bicNumber:
          type: string
          example: '34323232'
          description: BIC number
        isIndividual:
          type: boolean
          example: true
          description: Whether beneficiary is an individual (true) or business (false)
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````