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

# Initiate Transfer

> Initiates a funds transfer from one of your virtual accounts to any Nigerian bank account.

**Before calling this endpoint, you must:**
1. Call `/api/v1/payments/account-enquiry` to validate the destination account and get a `nameEnquirySessionId`
2. Attach an mTLS client certificate (required for all payment operations)
3. Sign the request body with HMAC-SHA256 and include in `X-Signature` header

**Transfer types:**
- **NIP (NIP Instant Payment)**: Transfers to other banks. Near-instant (usually seconds). Requires BVN of both sender and recipient.
- **NEFT**: For larger, non-time-sensitive transfers.

**Fee modes** control how charges appear on the statement:
- `LUMP_FEE_VAT` (default): One entry for the principal, one for fees+VAT combined
- `LUMP_ALL`: A single combined debit for everything
- `SPLIT_FEE_VAT`: Three separate entries: principal, fees, and VAT

Transfers are processed asynchronously. This endpoint returns `202 Accepted` -
not `200 OK`. Poll `/api/v1/payments/{transactionRef}/status` to get the final result.

**Idempotency:** Include a unique `X-Idempotency-Key`. If your network fails after
sending but before receiving a response, resend with the same key, you will get
the original result, not a duplicate transfer.




## OpenAPI

````yaml POST /api/v1/payments/transfer
openapi: 3.0.3
info:
  title: Union Bank BaaS API
  version: '1.0'
  description: >
    **Union Bank Nigeria, Banking as a Service (BaaS) Platform**


    Build financial products on top of Union Bank's core banking infrastructure.

    This API gives your application the ability to open virtual accounts,
    initiate

    payments, collect money, verify identities, and manage webhooks, all in one
    place.


    ## Before You Start


    You must complete the **partner onboarding journey** before your API keys
    work:

    1. Register at [ubn-ui.onecluster.co](https://ubn-ui.onecluster.co)

    2. Verify your business email

    3. Upload your business documents (KYB)

    4. Pass identity verification (KYC)

    5. Receive your sandbox API key


    ## Base URLs


    | Environment | Base URL |

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

    | Sandbox (for testing) | `https://api-partner.onecluster.co` |

    | Production (for live apps) | `https://api.onecluster.co` |


    ## How to Authenticate


    Add your API key to every request as a header:


    ```

    Authorization: ApiKey ubn_sb_your_key_here

    ```


    For payments, you also need to attach a **digital signature** (HMAC-SHA256)
    and an

    **mTLS client certificate**. See the [Authentication
    Guide](/api-reference/authentication) for details.


    ## Standard Request Headers


    | Header | Required | Description |

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

    | `Authorization` | Yes | Your API key. Format: `ApiKey ubn_sb_xxx` |

    | `X-Correlation-ID` | No | A UUID you choose, used to trace this request in
    logs. We generate one if you omit it. |

    | `X-Idempotency-Key` | Yes (write ops) | A UUID you choose. If you send the
    same request twice with the same key, we return the original result, not a
    duplicate. |

    | `X-Signature` | Yes (payments, KYC) | HMAC-SHA256 signature of the request
    body. See Authentication guide. |


    ## Standard Response Headers


    | Header | Description |

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

    | `X-Correlation-ID` | The trace ID for this request (echoed from your
    request or generated by us) |

    | `X-Quota-Remaining` | How many API calls you have left in this billing
    period |

    | `X-Quota-Limit` | Your total API call quota for this billing period |

    | `X-Quota-Reset` | When your quota resets (Nigeria time, WAT +01:00) |

    | `X-Circuit-Breaker-Open` | Present and `true` if our system is temporarily
    pausing requests to protect stability |

    | `X-Key-Rotation-Warning` | Present when your API key is about to expire
    (within 72 hours of rotation) |
  contact:
    name: UBN BaaS Support
    email: baas-support@unionbank.ng
    url: https://dash.onecluster.co/resources/support
  license:
    name: Proprietary
    url: https://unionbank.ng/terms
servers:
  - url: https://api-partner.onecluster.co
    description: Sandbox - use this for testing. No real money moves.
  - url: https://api.onecluster.co
    description: Production - live environment. Real transactions.
security:
  - ApiKeyAuth: []
tags:
  - name: Accounts
    description: Open and manage virtual bank accounts
  - name: Payments
    description: Send money to any Nigerian bank account
  - name: Collections
    description: Receive money into virtual collection accounts
  - name: KYC
    description: Verify customer identities (BVN, NIN, CAC)
  - name: Health
    description: Check if the API is up and running
paths:
  /api/v1/payments/transfer:
    post:
      tags:
        - Payments
      summary: Send a payment
      description: >
        Initiates a funds transfer from one of your virtual accounts to any
        Nigerian bank account.


        **Before calling this endpoint, you must:**

        1. Call `/api/v1/payments/account-enquiry` to validate the destination
        account and get a `nameEnquirySessionId`

        2. Attach an mTLS client certificate (required for all payment
        operations)

        3. Sign the request body with HMAC-SHA256 and include in `X-Signature`
        header


        **Transfer types:**

        - **NIP (NIP Instant Payment)**: Transfers to other banks. Near-instant
        (usually seconds). Requires BVN of both sender and recipient.

        - **NEFT**: For larger, non-time-sensitive transfers.


        **Fee modes** control how charges appear on the statement:

        - `LUMP_FEE_VAT` (default): One entry for the principal, one for
        fees+VAT combined

        - `LUMP_ALL`: A single combined debit for everything

        - `SPLIT_FEE_VAT`: Three separate entries: principal, fees, and VAT


        Transfers are processed asynchronously. This endpoint returns `202
        Accepted` -

        not `200 OK`. Poll `/api/v1/payments/{transactionRef}/status` to get the
        final result.


        **Idempotency:** Include a unique `X-Idempotency-Key`. If your network
        fails after

        sending but before receiving a response, resend with the same key, you
        will get

        the original result, not a duplicate transfer.
      operationId: initiateTransfer
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
        - $ref: '#/components/parameters/IdempotencyKey'
        - name: X-Signature
          in: header
          required: true
          description: >
            HMAC-SHA256 signature of the request body.

            Format: `sha256=<hex-encoded-signature>`

            See the [Authentication
            guide](/api-reference/authentication#signing-requests) for how to
            compute this.
          schema:
            type: string
            example: >-
              sha256=a3f4b2c1d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransferRequest'
            example:
              sourceAccount: '0123456789'
              destinationAccount: '0987654321'
              destinationBankCode: '058'
              amount: 50000
              currency: NGN
              narration: Payment for invoice INV-2026-001
              senderBvn: '22211111111'
              beneficiaryBvn: '22299999999'
              senderKycLevel: '3'
              beneficiaryKycLevel: '2'
              nameEnquirySessionId: NES-20260325-ABC123
              ftSessionId: FT-20260325-XYZ789
              transactionLocation: 6.5244,3.3792
              channelCode: API
              feeEntryMode: LUMP_FEE_VAT
      responses:
        '202':
          description: Transfer accepted, poll the status endpoint to get the final result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'
              example:
                success: true
                responseCode: '00'
                message: Transfer accepted and queued for processing
                data:
                  transactionRef: TXN-20260325-001
                  status: PENDING
                  estimatedCompletionTime: '2026-03-25T10:00:30+01:00'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/QuotaExhausted'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
      security:
        - ApiKeyAuth: []
components:
  parameters:
    CorrelationId:
      name: X-Correlation-ID
      in: header
      required: false
      description: >
        A UUID v4 you generate to trace this request through our systems.

        If you don't provide one, we generate it for you.

        Always include it in support requests, it helps us find your request in
        logs.
      schema:
        type: string
        format: uuid
        example: 7f3a9c21-4e8b-4a12-b6d1-3c8a7f2e1b09
    IdempotencyKey:
      name: X-Idempotency-Key
      in: header
      required: true
      description: >
        A UUID you generate. If you send the same request twice with the same
        key

        (within 24 hours), we return the original response, not a duplicate
        action.

        Generate a new UUID for every new operation.
      schema:
        type: string
        format: uuid
        example: 550e8400-e29b-41d4-a716-446655440000
  schemas:
    TransferRequest:
      type: object
      required:
        - sourceAccount
        - destinationAccount
        - destinationBankCode
        - amount
        - currency
        - narration
        - nameEnquirySessionId
        - ftSessionId
        - transactionLocation
        - channelCode
      properties:
        sourceAccount:
          type: string
          description: The virtual account to debit (must belong to your partner account)
        destinationAccount:
          type: string
          description: The destination account number (any Nigerian bank)
        destinationBankCode:
          type: string
          description: 3-digit CBN bank sort code (e.g. `058` for GTBank, `033` for UBA)
        amount:
          type: number
          format: double
          minimum: 0.01
          description: Amount in Naira (NGN)
          example: 50000
        currency:
          type: string
          default: NGN
          description: Must be `NGN`
        narration:
          type: string
          maxLength: 100
          description: Description that appears on the recipient's bank statement
          example: Payment for invoice INV-2026-001
        senderBvn:
          type: string
          description: Sender's BVN. Required for inter-bank (NIP) transfers.
        beneficiaryBvn:
          type: string
          description: Recipient's BVN. Required for inter-bank (NIP) transfers.
        senderKycLevel:
          type: string
          description: CBN KYC level of the sender (1, 2, or 3)
        beneficiaryKycLevel:
          type: string
          description: CBN KYC level of the recipient (1, 2, or 3)
        nameEnquirySessionId:
          type: string
          description: The session ID from the `/account-enquiry` call. Required.
        ftSessionId:
          type: string
          description: >-
            Funds Transfer session ID (provided by your UBN relationship
            manager)
        transactionLocation:
          type: string
          description: >-
            GPS coordinates of the transaction origin. Required for NIP
            transfers. Format, `latitude,longitude`
          example: 6.5244,3.3792
        channelCode:
          type: string
          description: The originating channel. Use `API` for programmatic transfers.
          example: API
        feeEntryMode:
          type: string
          enum:
            - LUMP_ALL
            - LUMP_FEE_VAT
            - SPLIT_FEE_VAT
          default: LUMP_FEE_VAT
          description: >
            How transfer fees appear on the statement:

            - `LUMP_ALL`: One combined debit for principal + fees + VAT

            - `LUMP_FEE_VAT` (default): Two debits, principal, then fees+VAT
            combined

            - `SPLIT_FEE_VAT`: Three debits, principal, fees, and VAT separately
    SuccessEnvelope:
      type: object
      description: Standard response wrapper for all successful API calls
      required:
        - success
        - responseCode
        - message
        - data
      properties:
        success:
          type: boolean
          description: Always `true` for successful responses
          example: true
        responseCode:
          type: string
          description: '`00` means success'
          example: '00'
        message:
          type: string
          description: Human-readable description of the result
          example: Request processed successfully
        data:
          type: object
          description: The actual response data (varies by endpoint)
    ProblemDetails:
      type: object
      description: >
        Standard error response following [RFC
        7807](https://tools.ietf.org/html/rfc7807).

        All errors use this format.
      required:
        - type
        - title
        - status
        - detail
      properties:
        type:
          type: string
          format: uri
          description: A link to the specific error type documentation
          example: https://api.onecluster.co/errors/insufficient-funds
        title:
          type: string
          description: Short, human-readable error name
          example: Insufficient Funds
        status:
          type: integer
          description: HTTP status code
          example: 422
        detail:
          type: string
          description: Full explanation of what went wrong and how to fix it
          example: >-
            The account balance is ₦0.00. You need at least ₦50,000.00 to
            complete this transfer.
        instance:
          type: string
          format: uri
          description: The API path where the error occurred
          example: /api/v1/payments/transfer
        correlationId:
          type: string
          format: uuid
          description: The trace ID, include this when contacting support
        code:
          type: string
          description: Machine-readable error code
          example: INSUFFICIENT_FUNDS
  responses:
    BadRequest:
      description: Bad request, check your request body against the schema
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    Unauthorized:
      description: >
        Authentication failed. Check that your `Authorization` header is
        correct.

        Format: `ApiKey ubn_sb_your_key_here`
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    QuotaExhausted:
      description: >
        You have used all your API calls for this billing period.

        Check `X-Quota-Reset` in the response header to see when your quota
        resets.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    Conflict:
      description: Duplicate request, an idempotent replay was detected
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    UnprocessableEntity:
      description: Request structure is valid but the data fails business rules
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: |
        Your API key, formatted as: `ApiKey ubn_sb_your_key_here`

        - Sandbox keys start with `ubn_sb_`
        - Production keys start with `ubn_pk_`
        - Never use production keys during testing, they will charge real money

````