Connecting the Pieces - Afriex Business API

What this guide covers

If you’ve read the quickstart but aren’t sure exactly which ID goes where, or why you need to create three different things before money can move, this guide is for you. The Afriex Business API has three core building blocks. They must be created in order, and each one produces an ID that the next step needs. This guide explains what each building block is, what ID it gives you, and exactly where that ID is used.


The big picture

Think of it like this: a Customer is the person your business is acting on behalf of (the sender or originator). A Payment Method holds the account details of the person being paid (the recipient). A Transaction is the instruction to move money, and it needs to know on whose behalf you are acting and which recipient account to use. Here is how the IDs connect:

Step 1: Create Customer

POST /api/v1/customer

✦ Returns: customerId

Step 2: Create Payment Method

POST /api/v1/payment-method

↳ Input: customerId
✦ Returns: paymentMethodId

Step 3: Create Transaction

POST /api/v1/transaction

↳ Input: customerId
↳ Input: destinationId = paymentMethodId  (WITHDRAW)
↳ Input: sourceId      = paymentMethodId  (DEPOSIT)
✦ Returns: transactionId

ID quick-reference

ID field Belongs to Used in
customerId Customer Payment Method creation, Transaction creation
paymentMethodId Payment Method Transaction as destinationId or sourceId
destinationId Transaction field Equals a paymentMethodId (used in WITHDRAW)
sourceId Transaction field Equals a paymentMethodId (used in DEPOSIT)
transactionId Transaction Status lookups, webhook events

destinationId and sourceId in a Transaction are both Payment Method IDs. They are not special IDs. They are exactly the paymentMethodId value returned when you created the Payment Method. This is the single most common point of confusion.


Each building block explained

Customer

What it is: A Customer is the person or business your business is acting on behalf of. They are the originator or sender of the transaction, not the recipient. For example, if your platform processes payroll for a company, each employee whose salary you send out is a Customer. The Customer record stores their identity information (name, email, phone, country) and is used to attribute transactions to the right person in your system.
What ID it gives you: customerId
When you use that ID:

Fields to create a Customer:

Field Required Example
fullName Yes "Amara Osei"
email Yes "amara@example.com"
phone Yes "+233201234567" (E.164 format)
countryCode Yes "GH" (ISO 3166-1 alpha-2)

Payment Method

What it is: A Payment Method holds the account details of the person being paid or collected from: their bank account, mobile money wallet, or other financial account. It is linked to a Customer (the originator on whose behalf you are acting), but the account details it contains belong to the end recipient. You need a Payment Method before you can send or receive money because the Transaction needs to know exactly which account to credit or debit.
What ID it gives you: paymentMethodId
When you use that ID:

A Payment Method has a type field, either WITHDRAW (used to send funds to a recipient) or DEPOSIT (used to collect funds from a customer). Make sure you create the right type for the transaction you plan to run.

Supported channels:

Channel Description Supports Common use case
BANK_ACCOUNT Local bank transfer Withdraw Payout to Nigerian GTBank, Kenyan KCB
MOBILE_MONEY Mobile wallet Deposit & Withdraw MTN MoMo, M-Pesa, Airtel Money
SWIFT International wire transfer Withdraw Cross-border payouts to Europe, US
UPI Unified Payments Interface Withdraw Payouts to India
INTERAC Interac e-Transfer Deposit & Withdraw Collections and payouts in Canada
WE_CHAT WeChat Pay Withdraw Payouts to China
ALIPAY Alipay Withdraw Payouts to China
VIRTUAL_BANK_ACCOUNT Afriex-issued virtual account Deposit Receive collections (production only)
POOL_ACCOUNT Shared pool account Deposit Shared collection account (production only)
CRYPTO_WALLET Crypto wallet Deposit USDC / USDT deposits

Virtual bank accounts

VIRTUAL_BANK_ACCOUNT is not created through the standard POST /api/v1/payment-method endpoint. Instead, use the dedicated GET /api/v1/payment-method/virtual-account endpoint, which retrieves an existing account or creates a new one automatically. This endpoint is production only and does not work on staging.

Reserved Time-sensitive
Triggered by Omitting amount Passing amount
Expires Never Yes (see expiresInMinutes in response)
Amount-specific No Yes
BVN required (NGN) Yes No

POOL_ACCOUNT returns the business’s shared pool account for a country. Requires country; pass customerId to scope the response to a specific end-user. Use the reference on the response to reconcile incoming deposits. Virtual accounts are for your end customers. Link each one to a customerId so payments are attributed to the right person.


Transaction

What it is: A Transaction is the actual instruction to move money. It references a Customer and (depending on the type) one of their Payment Methods.
What ID it gives you: transactionId
When you use that ID:

Three transaction types:

Type What it does Payment method needed
WITHDRAW Send money from your Afriex wallet to a customer’s account destinationId (= paymentMethodId)
DEPOSIT Pull money from a customer’s account into your Afriex wallet sourceId (= paymentMethodId)
SWAP Convert currency within your Afriex wallet None

The 3-step integration sequence

Here is the exact order of operations, with the IDs flowing from one step to the next.

  1. Create the Customer
    Register the person on whose behalf the transaction is being made (the originator or sender).
   curl -X POST https://sandbox.api.afriex.com/api/v1/customer \
     -H "Content-Type: application/json" \
     -H "x-api-key: YOUR_API_KEY" \
     -d '{\n       "fullName": "Amara Osei",\n       "email": "amara@example.com",\n       "phone": "+233201234567",\n       "countryCode": "GH"\n     }'

Save the customerId from the response. You need it in the next step.

   {  
     "data": {  
       "customerId": "69516dd0464b2213bd74cfad",  
       "name": "Amara Osei",  
       "email": "amara@example.com",  
       "phone": "+233201234567",  
       "countryCode": "GH"  
     }  
   }
  1. Create the Payment Method
    Register the recipient’s bank account or mobile wallet (the account the money will be sent to or collected from). Link it to the Customer from Step 1 by passing their customerId. First, look up the correct institution code for the customer’s bank or mobile provider:
   curl "https://sandbox.api.afriex.com/api/v1/payment-method/institution?channel=MOBILE_MONEY&countryCode=GH" \
     -H "x-api-key: YOUR_API_KEY"

Then create the payment method using the institutionCode and institutionName from that response:

   curl -X POST https://sandbox.api.afriex.com/api/v1/payment-method \
     -H "Content-Type: application/json" \
     -H "x-api-key: YOUR_API_KEY" \
     -d '{\n       "customerId": "69516dd0464b2213bd74cfad",\n       "type": "WITHDRAW",\n       "channel": "MOBILE_MONEY",\n       "accountName": "Amara Osei",\n       "accountNumber": "0201234567",\n       "countryCode": "GH",\n       "institution": {\n         "institutionCode": "MTN",\n         "institutionName": "MTN"\n       }\n     }'

Save the paymentMethodId from the response. This is what goes into destinationId (or sourceId) in your transaction.

   {  
     "data": {  
       "paymentMethodId": "690df3281c11eea59108fcaf",  
       "customerId": "69516dd0464b2213bd74cfad",  
       "channel": "MOBILE_MONEY",  
       "accountName": "Amara Osei",  
       "accountNumber": "0201234567",  
       "countryCode": "GH"  
     }  
   }
  1. Create the Transaction
    Now create the transaction using both IDs from the previous steps.
    • customerId = the ID from Step 1
    • destinationId = the paymentMethodId from Step 2 (for a WITHDRAW)
   curl -X POST https://sandbox.api.afriex.com/api/v1/transaction \
     -H "Content-Type: application/json" \
     -H "x-api-key: YOUR_API_KEY" \
     -d '{\n       "customerId": "69516dd0464b2213bd74cfad",\n       "type": "WITHDRAW",\n       "sourceAmount": "10",\n       "destinationAmount": "91.50",\n       "sourceCurrency": "USD",\n       "destinationCurrency": "GHS",\n       "destinationId": "690df3281c11eea59108fcaf",\n       "meta": {\n         "idempotencyKey": "550e8400-e29b-41d4-a716-446655440001",\n         "reference": "ORDER-98765"\n       }\n     }'

The response includes a transactionId. Use it to track status or match incoming webhook events.


Transaction type payloads side by side

WITHDRAW: Pay out to a customer's account

{
  "customerId": "69516dd0464b2213bd74cfad",
  "type": "WITHDRAW",
  "sourceAmount": "10",
  "destinationAmount": "16500",
  "sourceCurrency": "USD",
  "destinationCurrency": "NGN",
  "destinationId": "690df3281c11eea59108fcaf",
  "meta": {
    "idempotencyKey": "550e8400-e29b-41d4-a716-446655440001",
    "reference": "ORDER-001"
  }
}
{
  "customerId": "69516dd0464b2213bd74cfad",
  "type": "DEPOSIT",
  "sourceAmount": "500",
  "destinationAmount": "12",
  "sourceCurrency": "KES",
  "destinationCurrency": "USD",
  "sourceId": "690df3281c11eea59108fcc1",
  "meta": {
    "idempotencyKey": "550e8400-e29b-41d4-a716-446655440002",
    "reference": "COLLECTION-001"
  }
}
{
  "type": "SWAP",
  "sourceAmount": "500",
  "sourceCurrency": "USD",
  "destinationCurrency": "NGN",
  "meta": {
    "idempotencyKey": "550e8400-e29b-41d4-a716-446655440003",
    "reference": "SWAP-001"
  }
}

What changes between each type

Field WITHDRAW DEPOSIT SWAP
customerId Required Required Not required
destinationId Required (= paymentMethodId) Not used Not used
sourceId Not used Required (= paymentMethodId) Not used
destinationAmount Required Required Optional (API calculates it at live rate)
meta.idempotencyKey Required Required Required
meta.reference Required Required Required

Payment method fields by channel

Different payment channels require different fields. Use the sections below to find the exact payload shape for your channel.

  1. BANK_ACCOUNT: Local bank transfer
    • Required fields: customerId, channel, accountName, accountNumber, countryCode, institution.institutionCode, institution.institutionName
    {
      "customerId": "69516dd0464b2213bd74cfad",
      "type": "WITHDRAW",
      "channel": "BANK_ACCOUNT",
      "accountName": "John Doe",
      "accountNumber": "0123456789",
      "countryCode": "NG",
      "institution": {
        "institutionCode": "058",
        "institutionName": "GTBank"
      }
    }
    ```

2. **MOBILE_MONEY**: M-Pesa, MTN MoMo, Airtel, etc.
    - Required fields: `customerId`, `channel`, `accountName`, `accountNumber` (the mobile number), `countryCode`, `institution.institutionCode`, `institution.institutionName`

```json
    {
      "customerId": "69516dd0464b2213bd74cfad",
      "type": "WITHDRAW",
      "channel": "MOBILE_MONEY",
      "accountName": "Amara Osei",
      "accountNumber": "0201234567",
      "countryCode": "GH",
      "institution": {
        "institutionCode": "MTN",
        "institutionName": "MTN"
      }
    }
    ```

3. **SWIFT**: International wire transfer
    - Required fields: `customerId`, `channel`, `accountName`, `accountNumber` (IBAN or account number), `countryCode`, `institution.institutionCode` (SWIFT/BIC), `institution.institutionName`, `institution.institutionAddress`, `recipient.recipientAddress`

```json
    {
      "customerId": "69516dd0464b2213bd74cfad",
      "type": "WITHDRAW",
      "channel": "SWIFT",
      "accountName": "John Doe",
      "accountNumber": "DE89370400440532013000",
      "countryCode": "DE",
      "institution": {
        "institutionCode": "DEUTDEDB",
        "institutionName": "Deutsche Bank",
        "institutionAddress": "Taunusanlage 12, 60262 Frankfurt am Main, Germany"
      },
      "recipient": {
        "recipientAddress": "Musterstrasse 1, 10115 Berlin, Germany"
      }
    }
    ```

4. **UPI**: India
    - Required fields: `customerId`, `channel`, `accountName`, `accountNumber` (UPI ID / VPA), `countryCode`, `institution.institutionCode` (`"UPI"`), `institution.institutionName` (`"UPI"`), `recipient.recipientPhone`

```json
    {
      "customerId": "69516dd0464b2213bd74cfad",
      "type": "WITHDRAW",
      "channel": "UPI",
      "accountName": "Raj Kumar",
      "accountNumber": "rajkumar@upi",
      "countryCode": "IN",
      "institution": {
        "institutionCode": "UPI",
        "institutionName": "UPI"
      },
      "recipient": {
        "recipientPhone": "+919876543210"
      }
    }
    ```

5. **INTERAC**: Canada
    - Required fields: `customerId`, `channel`, `accountName`, `accountNumber` (the email address registered for Interac), `countryCode`, `institution.institutionCode` (`"INTERAC"`), `institution.institutionName` (`"INTERAC"`), `recipient.recipientEmail`

```json
    {
      "customerId": "69516dd0464b2213bd74cfad",
      "type": "WITHDRAW",
      "channel": "INTERAC",
      "accountName": "John Doe",
      "accountNumber": "john.doe@email.com",
      "countryCode": "CA",
      "institution": {
        "institutionCode": "INTERAC",
        "institutionName": "INTERAC"
      },
      "recipient": {
        "recipientEmail": "john.doe@email.com"
      }
    }
    ```

6. **WE_CHAT**: China
    - Required fields: `customerId`, `channel`, `accountName`, `accountNumber` (phone number), `countryCode`, `institution.institutionCode` (`"WECHAT"`), `institution.institutionName` (`"WECHAT"`), `recipient.recipientPhone`

```json
    {
      "customerId": "69516dd0464b2213bd74cfad",
      "type": "WITHDRAW",
      "channel": "WE_CHAT",
      "accountName": "Zhang Wei",
      "accountNumber": "+8613812345678",
      "countryCode": "CN",
      "institution": {
        "institutionCode": "WECHAT",
        "institutionName": "WECHAT"
      },
      "recipient": {
        "recipientPhone": "+8613812345678"
      }
    }
    ```

7. **ALIPAY**: China
    - Required fields: `customerId`, `channel`, `accountName`, `accountNumber` (Alipay-linked phone number or account ID), `countryCode`, `institution.institutionName`

```json
    {
      "customerId": "69516dd0464b2213bd74cfad",
      "type": "WITHDRAW",
      "channel": "ALIPAY",
      "accountName": "Zhang Wei",
      "accountNumber": "+8613812345678",
      "countryCode": "CN",
      "institution": {
        "institutionName": "ALIPAY"
      }
    }
    ```

8. **VIRTUAL_BANK_ACCOUNT**: Receive deposits
    - Virtual bank accounts are not created through the standard `POST /api/v1/payment-method` endpoint. Use the dedicated endpoint instead:
GET /api/v1/payment-method/virtual-account
```
This endpoint retrieves an existing virtual account for the customer or creates one automatically. It is **production only** and does not work on staging. **Two sub-types**, determined by whether you pass an `amount`:
Reserved Time-sensitive
Pass amount? No Yes
Expires Never Yes, see expiresInMinutes in response
Amount-specific No Yes
BVN required (NGN) Yes No

See the Virtual Account endpoint for the full request and response reference.