Skip to main content

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:

ID quick-reference

ID fieldBelongs toUsed in
customerIdCustomerPayment Method creation, Transaction creation
paymentMethodIdPayment MethodTransaction as destinationId or sourceId
destinationIdTransaction fieldEquals a paymentMethodId (used in WITHDRAW)
sourceIdTransaction fieldEquals a paymentMethodId (used in DEPOSIT)
transactionIdTransactionStatus 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:
  • Pass it as customerId when creating a Payment Method (to link the account to this person)
  • Pass it as customerId when creating a Transaction (to identify whose account is involved)
Fields to create a Customer:
FieldRequiredExample
fullNameYes"Amara Osei"
emailYes"amara@example.com"
phoneYes"+233201234567" (E.164 format)
countryCodeYes"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:
  • As destinationId in a WITHDRAW transaction (the account the money is sent to)
  • As sourceId in a DEPOSIT transaction (the account the money is pulled from)
A Payment Method has a type field, either WITHDRAW (can receive funds) or DEPOSIT (can send funds). Make sure you create the right type for the transaction you plan to run.
Supported channels:
ChannelDescriptionCommon use case
BANK_ACCOUNTLocal bank transferPayout to Nigerian GTBank, Kenyan KCB
MOBILE_MONEYMobile walletPayout to MTN MoMo, M-Pesa, Airtel Money
SWIFTInternational wire transferCross-border payouts to Europe, US
UPIUnified Payments InterfacePayouts to India
INTERACInterac e-TransferPayouts to Canada
WE_CHATWeChat PayPayouts to China
VIRTUAL_BANK_ACCOUNTAfriex-issued virtual accountReceive deposits (production only)
CRYPTO_WALLETCrypto walletCrypto payouts (production only)

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:
  • To check the status: GET /api/v1/transaction/{transactionId}
  • It appears in webhook events so you can match notifications to records in your system
Three transaction types:
TypeWhat it doesPayment method needed
WITHDRAWSend money from your Afriex wallet to a customer’s accountdestinationId (= paymentMethodId)
DEPOSITPull money from a customer’s account into your Afriex walletsourceId (= paymentMethodId)
SWAPConvert currency within your Afriex walletNone
WITHDRAW: Your business wallet is the implicit source. Afriex debits your wallet automatically. You do not specify a source payment method.DEPOSIT: Your business wallet is the implicit destination. Afriex credits your wallet automatically. You do not specify a destination payment method.SWAP: Both source and destination are your business wallet. No customer or payment method IDs are needed at all.

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 '{
    "fullName": "Amara Osei",
    "email": "amara@example.com",
    "phone": "+233201234567",
    "countryCode": "GH"
  }'
Save the customerId from the response. You need it in the next step.
{
  "data": {
    "customerId": "69516dd0464b2213bd74cfad",
    "fullName": "Amara Osei",
    "email": "amara@example.com",
    "phone": "+233201234567",
    "countryCode": "GH"
  }
}
2

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 '{
    "customerId": "69516dd0464b2213bd74cfad",
    "type": "WITHDRAW",
    "channel": "MOBILE_MONEY",
    "accountName": "Amara Osei",
    "accountNumber": "0201234567",
    "countryCode": "GH",
    "institution": {
      "institutionCode": "MTN",
      "institutionName": "MTN"
    }
  }'
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"
  }
}
3

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 '{
    "customerId": "69516dd0464b2213bd74cfad",
    "type": "WITHDRAW",
    "sourceAmount": "10",
    "destinationAmount": "91.50",
    "sourceCurrency": "USD",
    "destinationCurrency": "GHS",
    "destinationId": "690df3281c11eea59108fcaf",
    "meta": {
      "idempotencyKey": "550e8400-e29b-41d4-a716-446655440001",
      "reference": "ORDER-98765"
    }
  }'
The response includes a transactionId. Use it to track status or match incoming webhook events.

Transaction type payloads side by side

{
  "customerId": "69516dd0464b2213bd74cfad",
  "type": "WITHDRAW",
  "sourceAmount": "10",
  "destinationAmount": "16500",
  "sourceCurrency": "USD",
  "destinationCurrency": "NGN",
  "destinationId": "690df3281c11eea59108fcaf",
  "meta": {
    "idempotencyKey": "550e8400-e29b-41d4-a716-446655440001",
    "reference": "ORDER-001"
  }
}

What changes between each type

FieldWITHDRAWDEPOSITSWAP
customerIdRequiredRequiredNot required
destinationIdRequired (= paymentMethodId)Not usedNot used
sourceIdNot usedRequired (= paymentMethodId)Not used
destinationAmountRequiredRequiredOptional (API calculates it at live rate)
meta.idempotencyKeyRequiredRequiredRequired
meta.referenceRequiredRequiredRequired

Payment method fields by channel

Different payment channels require different fields. Use the sections below to find the exact payload shape for your channel.
Required fields: customerId, channel, accountName, accountNumber, countryCode, institution.institutionCode, institution.institutionNameLook up valid institution codes first: List InstitutionsYou can also verify the account holder name before creating: Resolve Payment Method
{
  "customerId": "69516dd0464b2213bd74cfad",
  "type": "WITHDRAW",
  "channel": "BANK_ACCOUNT",
  "accountName": "John Doe",
  "accountNumber": "0123456789",
  "countryCode": "NG",
  "institution": {
    "institutionCode": "058",
    "institutionName": "GTBank"
  }
}
Required fields: customerId, channel, accountName, accountNumber (the mobile number), countryCode, institution.institutionCode, institution.institutionNameLook up valid institution codes for the country: List Institutions
{
  "customerId": "69516dd0464b2213bd74cfad",
  "type": "WITHDRAW",
  "channel": "MOBILE_MONEY",
  "accountName": "Amara Osei",
  "accountNumber": "0201234567",
  "countryCode": "GH",
  "institution": {
    "institutionCode": "MTN",
    "institutionName": "MTN"
  }
}
Required fields: customerId, channel, accountName, accountNumber (IBAN or account number), countryCode, institution.institutionCode (SWIFT/BIC), institution.institutionName, institution.institutionAddress, recipient.recipientAddressUse Institution Codes to resolve a SWIFT code to a bank name.
{
  "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"
  }
}
Required fields: customerId, channel, accountName, accountNumber (UPI ID / VPA), countryCode, institution.institutionCode ("UPI"), institution.institutionName ("UPI"), recipient.recipientPhone
{
  "customerId": "69516dd0464b2213bd74cfad",
  "type": "WITHDRAW",
  "channel": "UPI",
  "accountName": "Raj Kumar",
  "accountNumber": "rajkumar@upi",
  "countryCode": "IN",
  "institution": {
    "institutionCode": "UPI",
    "institutionName": "UPI"
  },
  "recipient": {
    "recipientPhone": "+919876543210"
  }
}
Required fields: customerId, channel, accountName, accountNumber (the email address registered for Interac), countryCode, institution.institutionCode ("INTERAC"), institution.institutionName ("INTERAC"), recipient.recipientEmail
{
  "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"
  }
}
Required fields: customerId, channel, accountName, accountNumber (phone number), countryCode, institution.institutionCode ("WECHAT"), institution.institutionName ("WECHAT"), recipient.recipientPhone
{
  "customerId": "69516dd0464b2213bd74cfad",
  "type": "WITHDRAW",
  "channel": "WE_CHAT",
  "accountName": "Zhang Wei",
  "accountNumber": "+8613812345678",
  "countryCode": "CN",
  "institution": {
    "institutionCode": "WECHAT",
    "institutionName": "WECHAT"
  },
  "recipient": {
    "recipientPhone": "+8613812345678"
  }
}

Institution fields: which one to use and when

The institution object has several fields that look similar. Here is exactly what each one is and when you need it.
FieldWhat it isWhen you need it
institutionCodeShort identifier for the bank or provider (e.g. "058" for GTBank Nigeria, "MTN" for MTN Ghana)Always required when creating a Payment Method
institutionNameHuman-readable display name (e.g. "GTBank", "MTN")Always required when creating a Payment Method
institutionIdAfriex’s internal database ID for the institutionOutput only. Never pass this as input. It appears in responses but is not needed for creation.
institutionAddressPhysical address of the institutionOnly for SWIFT transfers. Skip it for all other channels.

How to find the right institutionCode

  1. Call GET /api/v1/payment-method/institution?channel=BANK_ACCOUNT&countryCode=NG
  2. The response returns a list of institutions, each with institutionCode and institutionName
  3. Copy those values directly into your Payment Method creation request
For SWIFT and US routing numbers, use GET /api/v1/payment-method/institution/codes to resolve a known SWIFT/BIC code to a bank name.
Cache the institution list per country and channel. It changes infrequently and there is no need to fetch it on every Payment Method creation.

The meta object

Every transaction must include a meta object with two required fields. Without them, the API returns a 400 error.
"meta": {
  "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
  "reference": "ORDER-12345"
}
FieldRequiredWhat it does
idempotencyKeyYesA unique string you generate per transaction attempt. If you retry a failed request with the same key, Afriex returns the original transaction instead of creating a duplicate. Use a UUID v4.
referenceYesYour own internal ID for this transaction (e.g. order number, payment ID, or invoice reference). Used for reconciliation.
narrationNoA human-readable description (e.g. "Salary payment for March 2025").
merchantIdNoYour internal merchant or business unit identifier.
invoiceNoA base64-encoded invoice document to attach to the transaction.
Never reuse an idempotencyKey across different transaction attempts. Use a fresh UUID for every new transaction. Reusing a key on a different transaction will cause Afriex to return the original transaction, not the new one.

Common mistakes

1. Using a customerId as destinationId or sourceId

destinationId and sourceId must be a paymentMethodId, not a customerId. Both look like hex strings, which makes it easy to mix them up.
// Wrong: this is a customerId
"destinationId": "69516dd0464b2213bd74cfad"

// Correct: this is the paymentMethodId from Step 2
"destinationId": "690df3281c11eea59108fcaf"

2. Skipping the 3-step sequence

You cannot create a Transaction without first creating a Customer and a Payment Method. The API returns a 404 or 400 if either does not exist. Always follow: Customer → Payment Method → Transaction.

3. Mixing up DEPOSIT and WITHDRAW fields

  • WITHDRAW uses destinationId (where the money goes)
  • DEPOSIT uses sourceId (where the money comes from)
Passing destinationId on a DEPOSIT (or sourceId on a WITHDRAW) will cause the API to reject the request.

4. Assuming SWAP needs a payment method

SWAP does not touch any external account. It converts currency inside your Afriex business wallet. No customerId, destinationId, or sourceId is needed. Only the currency pair and the meta object are required.
{
  "type": "SWAP",
  "sourceAmount": "500",
  "sourceCurrency": "USD",
  "destinationCurrency": "NGN",
  "meta": {
    "idempotencyKey": "unique-uuid-here",
    "reference": "SWAP-001"
  }
}

5. Omitting meta.idempotencyKey or meta.reference

Both are required on every transaction. The API returns a 400 if either is missing. Always generate a fresh UUID for idempotencyKey and use your own internal order or payment ID as reference.

6. Hardcoding institution codes without looking them up

Do not guess institution codes. Use GET /api/v1/payment-method/institution?channel=BANK_ACCOUNT&countryCode=NG to get the correct code for each country and channel. The same bank can have a different code in different countries.

7. Passing institutionId as input

institutionId is an output field. It appears in API responses but you never need to supply it when creating a Payment Method. The fields you pass are institutionCode and institutionName.

Tracking what happens after you create a transaction

Transactions do not complete instantly. After creation the status will be PENDING or PROCESSING. Here is how to know when it finishes. Option A (recommended): Webhooks Subscribe to TRANSACTION.UPDATED events. When the transaction reaches a terminal status, Afriex sends an HTTP POST to your webhook URL with the full transaction payload. See Webhook setup for configuration steps and payload examples. Option B: Poll the status endpoint
curl "https://sandbox.api.afriex.com/api/v1/transaction/TRANSACTION_ID" \
  -H "x-api-key: YOUR_API_KEY"
Prefer webhooks over polling. Polling adds latency and uses API quota. Webhooks notify you the moment the status changes.

Terminal statuses

StatusMeaning
COMPLETED / SUCCESSTransaction finished successfully
FAILEDTransaction could not be processed
CANCELLEDCancelled before processing started
REFUNDEDFunds returned to the sender
REJECTEDRejected after manual review

Next steps

Quickstart

Make your first API call in under 5 minutes.

Create a Customer

Full parameter reference for customer creation.

Create a Payment Method

All channels and required fields in the API reference.

Create a Transaction

Full transaction creation reference with all fields.

Data Model Reference

Complete field-by-field documentation for all models.

Webhooks

Receive real-time transaction status updates.