Payouts (Outflow)

Payouts let you send money from your Capera balance to external accounts. Capera supports two payout channels:

ChannelDestinationEndpoint prefix
NGN Bank TransferAny Nigerian bank account/v1/transfers
Mobile Money TransferMobile wallet (Ghana, Kenya, Uganda, and more)/v1/momo/transfer

NGN Bank Transfers

Send NGN directly to any Nigerian bank account. The flow is: get banks → resolve account → initiate → track status.

Step 1: Get available banks

Returns all Nigerian banks supported for transfers. No authentication required. Cache this list — it changes infrequently.

GET /v1/banks

Response

[
  { "id": "bank_001", "name": "First Bank of Nigeria", "slug": "first-bank" },
  { "id": "bank_002", "name": "Guaranty Trust Bank",   "slug": "gtb" },
  { "id": "bank_003", "name": "United Bank for Africa", "slug": "uba" },
  { "id": "bank_004", "name": "Access Bank",            "slug": "access-bank" },
  { "id": "bank_005", "name": "Zenith Bank",            "slug": "zenith-bank" }
]

Use the slug value when specifying the destination bank.

Step 2: Resolve the destination account

Verify the account number is valid and retrieve the account holder's name. Always do this before initiating a transfer — it prevents sending to the wrong account.

GET /v1/bank/resolve?accountNumber=1234567890&bankSlug=first-bank
Authorization: Bearer <api-key>

Query parameters

ParameterRequiredDescription
accountNumberYes10-digit bank account number
bankSlugYesBank slug from the Get Banks response

Response

{
  "accountName": "Amara Okafor"
}

Show the accountName to the user before they confirm the payout. This is the most effective way to prevent wrong-account transfers.

Step 3: Initiate the transfer

POST /v1/transfers/initiate
Authorization: Bearer <api-key>
Content-Type: application/json
{
  "reference": "TRF-2024-001",
  "amount": 500000,
  "bankSlug": "first-bank",
  "accountNumber": "1234567890",
  "accountName": "Amara Okafor",
  "narration": "Salary payment - January 2024"
}

Request fields

FieldTypeRequiredDescription
referencestringYesYour unique identifier for this transfer — used for idempotency
amountintegerYesAmount in kobo. ₦1 = 100 kobo. Must be greater than 0.
bankSlugstringYesBank slug from the Get Banks response
accountNumberstringYesDestination 10-digit account number
accountNamestringYesAccount holder name — should match the resolve response
narrationstringNoDescription shown on the recipient's bank statement (max 100 chars)

Use a unique reference for every transfer. Retrying with the same reference returns the original result — no duplicate is created. See Idempotency.

Response

{
  "status": "PENDING"
}

The transfer is accepted and queued. Processing is asynchronous.

Step 4: Check transfer status

GET /v1/transfers/{reference}
Authorization: Bearer <api-key>

Response

{
  "reference": "TRF-2024-001",
  "status": "SUCCESS",
  "amount": 500000,
  "fee": 50
}

Transfer statuses

StatusMeaning
PENDINGQueued, not yet sent to the bank
PROCESSINGSubmitted to the bank, awaiting confirmation
SUCCESSFunds delivered to the destination account
FAILEDTransfer failed — funds are returned to your balance
CANCELLEDTransfer was cancelled before processing

Webhook events

Subscribe to these events to avoid polling:

EventTrigger
transfer.initiatedTransfer was accepted
transfer.pendingTransfer is being processed by the bank
transfer.successTransfer completed successfully
transfer.failedTransfer failed

transfer.success payload

{
  "id": "evt_abc123",
  "event": "transfer.success",
  "timestamp": "2024-01-15T12:00:00Z",
  "data": {
    "id": "trf_xyz789",
    "reference": "TRF-2024-001",
    "amount": 500000,
    "currency": "NGN",
    "status": "SUCCESSFUL",
    "destination": "Amara Okafor",
    "accountNumber": "1234567890",
    "bankName": "First Bank of Nigeria",
    "narration": "Salary payment - January 2024",
    "sessionId": "000001240115...",
    "createdAt": "2024-01-15T11:55:00Z",
    "updatedAt": "2024-01-15T12:00:00Z"
  }
}

Mobile Money Transfers

Send funds directly to a mobile wallet. The flow mirrors NGN transfers: list operators → resolve account → initiate → track status.

Step 1: List MoMo operators

GET /v1/momo/operators/{country_code}

No authentication required. Use the ISO 3166-1 alpha-2 country code for the destination country.

GET /v1/momo/operators/GH?type=transfer

Response

{
  "success": true,
  "data": [
    { "operator": "MTN Ghana",          "operatorCode": "MTN_GH" },
    { "operator": "Vodafone Ghana",     "operatorCode": "VOD_GH" },
    { "operator": "AirtelTigo Ghana",   "operatorCode": "ATL_GH" }
  ]
}

Step 2: Resolve the MoMo account (recommended)

Verify the phone number before sending.

GET /v1/momo/resolve-account
Authorization: Bearer <api-key>
Content-Type: application/json
{
  "operatorCode": "MTN_GH",
  "phoneNumber": "+233241234567"
}

Response

{
  "success": true,
  "data": {
    "firstName": "Kwame",
    "lastName": "Mensah",
    "operatorCode": "MTN_GH"
  }
}

Step 3: Initiate the MoMo transfer

POST /v1/momo/transfer
Authorization: Bearer <api-key>
Content-Type: application/json
{
  "reference": "MOMO-TRF-001",
  "amount": 5000,
  "operatorCode": "MTN_GH",
  "phoneNumber": "+233241234567",
  "narration": "Vendor payment - January 2024",
  "counterparty": {
    "firstName": "Kwame",
    "lastName": "Mensah"
  }
}

Request fields

FieldTypeRequiredDescription
referencestringYesYour unique identifier for this transfer
amountintegerYesAmount in the currency's minor unit. Must be greater than 0.
operatorCodestringYesOperator code from the List Operators response
phoneNumberstringYesRecipient's phone number in E.164 format
narrationstringNoTransfer description
counterparty.firstNamestringYesRecipient's first name
counterparty.lastNamestringYesRecipient's last name

Response

{
  "success": true,
  "data": {
    "reference": "MOMO-TRF-001",
    "status": "PENDING",
    "amount": 5000,
    "fee": 0
  }
}

Step 4: Check MoMo transfer status

GET /v1/momo/transfer/{reference}
Authorization: Bearer <api-key>

Response

{
  "success": true,
  "data": {
    "reference": "MOMO-TRF-001",
    "status": "SUCCESS",
    "amount": 5000,
    "fee": 0
  }
}

Error handling

ErrorCauseResolution
Bank not foundInvalid bankSlugUse a slug from GET /v1/banks
Account not foundAccount number does not exist at the given bankVerify the account number
Amount must be greater than 0Zero or negative amountUse a positive integer
Reference already existsDuplicate referenceCheck the transfer status — it may already exist
Session not foundMissing or invalid API keyCheck your Authorization header

Best practices

Always resolve before sending — Run GET /v1/bank/resolve or GET /v1/momo/resolve-account before every transfer. Account name verification is the most reliable way to prevent sending to the wrong destination.

Cache the bank list — Banks change infrequently. Cache GET /v1/banks for at least 24 hours to avoid redundant calls.

Use webhooks over polling — Subscribe to transfer.success and transfer.failed instead of polling GET /v1/transfers/:reference. This reduces latency and API load.

Handle FAILED gracefully — When a transfer fails, the amount is returned to your balance. Notify the initiator and allow them to retry with a new reference.


What's next

  • Collections — receive payments via virtual accounts and MoMo
  • Deposits — query your incoming deposit history
  • Idempotency — safely retry failed requests

Did this page help you?