Bank Transfer API

Learn how to process bank transfers directly to bank accounts using . Transfers are processed instantly.

Overview

Bank Transfer allows you to move funds from your business balance to a bank account. Charges are calculated using your Wallet → Bank pricing: tiered flat charges per amount range, with optional business-specific overrides. You always pay transfer amount + charge; the recipient receives the transfer amount.

API vs Dashboard

  • API – You send recipient bank details (bank_name, bank_account_number, and optionally bank_account_name, bank_branch). The account is validated before the transfer; you can send to any supported bank. No verified business bank account is required.
  • Dashboard – From the Bank Transfer page you can send to your own account or to a different bank account (with validation).

How Bank Transfers Work

  1. You create a transfer with amount, description, recipient bank details, and optionally wallet_source (API: account is validated before sending).
  2. Charge is calculated from your Wallet → Bank pricing (tiered flat charges per amount range). Total deducted = amount + charge.
  3. Balance is deducted from the selected wallet (main or card) immediately before the provider call.
  4. Transfer is sent to the destination account.
  5. Status is set to pending, processing, completed, or failed.
  6. You can poll GET /bank-transfer/{reference} or use webhooks for the final result.

Wallet Selection

Every business has two wallets. Pass wallet_source to choose which wallet to debit:

Value Wallet Description
main (default) Main Wallet Mobile money collections (MTN, Airtel)
card Card Wallet Card payment collections

If omitted, wallet_source defaults to main. If a transfer fails, the refund goes back to the same wallet that was debited. See the Card Payments docs for how card wallet funds accumulate.

Wallet to Bank Pricing

Wallet → Bank (push to bank) transfers use tiered flat charges based on the transfer amount. For every transfer you pay amount + charge, and the recipient receives the transfer amount.

Default global tiers (UGX)

Amount (UGX) Charge (UGX)
2,500 – 250,000 5,000
250,001 – 500,000 6,000
500,001 – 1,000,000 9,000
1,000,001 – 2,000,000 13,500
2,000,001 – 50,000,000 16,500

These are the standard tiers used for Wallet → Bank transfers.

Per-business overrides

  • These charges apply to all businesses unless special pricing has been agreed.
  • Where custom pricing is agreed for a business, those rates are used instead of the standard tiers.
  • The correct charge is automatically selected based on the transfer amount.
  • The minimum transfer amount is usually 2,500 UGX (or your agreed minimum).

API Base URL

All bank transfer API requests should be made to the following base URL:

https://wallet.wearemarz.com/api/v1

API Endpoints

POST /bank-transfer Create bank transfer
GET /bank-transfer/services Get bank transfer availability & settings
GET /bank-transfer/{reference} Get bank transfer details
POST /bank-transfer/validate Validate bank account details
GET /bank-transfer/banks Get list of supported banks

Authentication

All bank transfer API requests require authentication using your API key. Include your API key in the request headers:

X-API-Key: your_api_key_here

Create Bank Transfer

Create a new bank transfer to any valid bank account. Send recipient bank_name and bank_account_number (and optionally bank_account_name, bank_branch). Subscription is handled internally. The recipient account is validated before the transfer; you do not need verified business bank details. The balance (transfer amount + charge) is deducted immediately and the transfer is sent. Status is processing or completed on success, or failed on error (balance is refunded on failure).

Validate before sending

We recommend calling POST /bank-transfer/validate first with bank_name and account_number to ensure the recipient account is valid and working before you create the transfer. This avoids failed transfers and refunds. The create-transfer endpoint also validates the account internally; validating first gives you early confirmation and the account holder name to display or store.

Request

POST https://wallet.wearemarz.com/api/v1/bank-transfer

Request Body

{
  "amount": 100000,
  "description": "Payment for services",
  "bank_name": "Equity Bank",
  "bank_account_number": "60001256421",
  "bank_account_name": "John Doe",
  "bank_branch": "Kampala",
  "wallet_source": "main"
}

bank_account_name, bank_branch, and wallet_source are optional. If bank_account_name is omitted, it is fetched from the validate-account step. If wallet_source is omitted, it defaults to main. Use card to withdraw from the card wallet. Use bank names from /bank-transfer/banks (e.g. Equity Bank, Stanbic Bank).

Request Parameters

Parameter Type Required Description
amount number Yes Transfer amount in UGX (required). Minimum: your business minimum_transfer_amount from /bank-transfer/services. Recipient receives this amount; you are charged on top according to your Wallet → Bank pricing.
description string Yes Description of the transfer (max 500 characters)
bank_name string Yes Recipient bank name (e.g. Equity Bank, Stanbic Bank). Use names from /bank-transfer/banks.
bank_account_number string Yes Recipient bank account number. Validated before the transfer.
bank_account_name string No Recipient account name. If omitted, obtained from the validate-account step.
bank_branch string No Recipient bank branch (optional)
wallet_source string No Which wallet to debit: main (default) or card. Use card to withdraw card payment collections. If omitted, defaults to main.

Response (Success) – HTTP 201

The reference is a UUID; use it with GET /bank-transfer/{reference} to check status. Possible status: processing, completed.

{
  "status": "success",
  "message": "Bank transfer is being processed. Reference: b0faa118-3e00-40ee-9513-67e371f9a32f. It may take a few minutes to complete.",
  "data": {
    "bank_transfer": {
      "id": 1,
      "reference": "b0faa118-3e00-40ee-9513-67e371f9a32f",
      "transaction_uuid": "b0faa118-3e00-40ee-9513-67e371f9a32f",
      "amount": {
        "formatted": "100,000.00",
        "raw": 100000,
        "currency": "UGX"
      },
      "charge_amount": {
        "formatted": "50,000.00",
        "raw": 50000,
        "currency": "UGX"
      },
      "total_amount": {
        "formatted": "150,000.00",
        "raw": 150000,
        "currency": "UGX"
      },
      "description": "Payment for services",
      "status": "processing",
      "wallet_source": "main",
      "bank_details": {
        "bank_name": "Equity Bank",
        "account_name": "John Doe",
        "account_number": "60001256421",
        "branch": "Kampala"
      },
      "balance": {
        "current": "1,000,000.00",
        "after_transaction": "850,000.00"
      },
      "provider": {
        "transaction_id": "TXN-123456",
        "status_code": "122",
        "status_description": "Processing"
      },
      "created_at": "2026-02-20 16:14:46"
    }
  }
}

Get Bank Transfer Availability & Settings

Check if bank transfer is available for your business and retrieve charge and minimum amount settings. Subscription details are handled internally and are not exposed.

Request

GET https://wallet.wearemarz.com/api/v1/bank-transfer/services

Response (Success)

{
  "status": "success",
  "data": {
    "bank_transfer_available": true,
    "bank_transfer_settings": {
      "amount_type": "fixed",
      "charge_percentage": 3.5,
      "minimum_transfer_amount": 2500,
      "fixed_transfer_charge": 50000
    },
    "service": {
      "name": "Bank Transfer Service",
      "provider": "Banks"
    }
  },
  "message": "Bank transfer is available."
}

Get Transfer Details

Retrieve a specific bank transfer by the reference returned from the create endpoint (UUID format). Use this to poll status. Possible status values: pending, processing, completed, failed.

Request

GET https://wallet.wearemarz.com/api/v1/bank-transfer/{reference}

Replace {reference} with the UUID from the create response (e.g. b0faa118-3e00-40ee-9513-67e371f9a32f).

Response (Success)

{
  "status": "success",
  "data": {
    "bank_transfer_request": {
      "id": 1,
      "reference": "b0faa118-3e00-40ee-9513-67e371f9a32f",
      "amount": { "formatted": "100,000.00", "raw": 100000, "currency": "UGX" },
      "charge_amount": { "formatted": "50,000.00", "raw": 50000, "currency": "UGX" },
      "total_amount": { "formatted": "150,000.00", "raw": 150000, "currency": "UGX" },
      "description": "Payment for services",
      "status": "processing",
      "bank_details": {
        "bank_name": "Equity Bank",
        "account_name": "John Doe",
        "account_number": "60001256421",
        "branch": "Kampala"
      },
      "balance": { "current": "1,000,000.00", "after_transaction": "850,000.00" },
      "service": { "id": 45, "name": "Bank Transfer Service", "provider": "Banks" },
      "timeline": { "created_at": "2026-02-20 16:14:46", "approved_at": "2026-02-20 16:14:46", "rejected_at": null },
      "provider": { "transaction_id": "TXN-123456", "status_code": "122", "status_description": "Processing" }
    }
  }
}

Validate Bank Account

Validate the recipient account before creating a transfer to ensure it is valid and working. Call this endpoint with bank_name and account_number to get the account holder name and confirm the account exists. Use it when building UIs (e.g. dashboard “Send to a different bank account”) or in your flow before calling create transfer. The create-transfer API also validates internally, but validating first gives you early confirmation and avoids failed transfers. No verified business bank is required.

Request

POST https://wallet.wearemarz.com/api/v1/bank-transfer/validate

Request Body

{
  "bank_name": "Equity Bank",
  "account_number": "60001256421"
}

Request Parameters

Parameter Type Required Description
bank_name string Yes Name of the bank (e.g., "Equity Bank", "Centenary Bank", "DFCU Bank")
account_number string Yes Bank account number to validate

Response (Success)

{
  "status": "success",
  "message": "SUCCESS",
  "data": {
    "account_number": "60001256421",
    "bank_name": "Equity Bank",
    "bank_code": "EQU",
    "account_name": "JOHN DOE",
    "network": "EQU",
    "is_registered": "ACTIVE",
    "valid": true,
    "status_code": 0,
    "status_description": "SUCCESS"
  }
}

Response Fields

Field Type Description
account_number string The validated account number
bank_name string The bank name provided in the request
bank_code string The bank code (e.g., "EQU", "CENT", "DFCU")
account_name string Account holder name as returned by the validation service (auto-filled from validation)
network string Bank network code (usually same as bank_code)
is_registered string Registration status ("ACTIVE" if fully registered, may be empty if not fully registered)
valid boolean True if account is valid and fully registered (status_code is 0 and is_registered is "ACTIVE")
status_code integer Status code (0 = success)
status_description string Status description (e.g., "SUCCESS")

Response (Error)

{
  "status": "error",
  "message": "Bank account validation failed",
  "error_code": "VALIDATION_FAILED",
  "data": {
    "account_number": "60001256421",
    "bank_name": "Equity Bank",
    "bank_code": "EQU",
    "status_code": 30,
    "status_description": "INVALID PHONE NUMBER"
  }
}

Validation errors: BANK_NOT_SUPPORTED (400) – bank name not in supported list; VALIDATION_FAILED (400) – provider rejected the account; SERVICE_NOT_AVAILABLE (400) – no active bank transfer subscription.

Important Notes

  • The account_name field is populated from the validation response and can be used to pre-fill account holder information
  • Use /bank-transfer/banks for the exact bank names/codes (e.g. Stanbic = STANBIC, Stanchart = STAN) to avoid UNSUPPORTED/UNKNOWN BANK from the provider
  • If valid is false, the account may still exist but might not be fully registered; you can still attempt a transfer after verifying with the account holder

Get Supported Banks

Retrieve a list of all supported banks for bank transfers and account validation. This endpoint returns bank codes, display names, and common aliases that can be used when validating accounts or creating transfer requests.

Request

GET https://wallet.wearemarz.com/api/v1/bank-transfer/banks

Response (Success)

{
  "status": "success",
  "message": "Supported banks retrieved successfully",
  "data": {
    "banks": [
      {
        "code": "EQU",
        "name": "Equity Bank",
        "aliases": ["Equity Bank", "Equity", "EQU"]
      },
      {
        "code": "CENT",
        "name": "Centenary Bank",
        "aliases": ["Centenary Bank", "Centenary", "CENT"]
      },
      {
        "code": "DFCU",
        "name": "DFCU Bank",
        "aliases": ["DFCU", "DFCU Bank"]
      },
      {
        "code": "STANBIC",
        "name": "Stanbic Bank",
        "aliases": ["Stanbic Bank", "Stanbic", "STANBIC"]
      },
      {
        "code": "STAN",
        "name": "Stanchart Bank",
        "aliases": ["Stanchart Bank", "Stanchart", "Standard Chartered", "STAN"]
      },
      {
        "code": "ABSA",
        "name": "ABSA Bank",
        "aliases": ["ABSA Bank", "ABSA"]
      }
    ],
    "count": 25
  }
}

Response Fields

Field Type Description
banks array Array of supported bank objects
count integer Total number of supported banks

Bank Object Structure

Field Type Description
code string Bank code identifier (e.g., "EQU", "CENT", "DFCU")
name string Display name of the bank (e.g., "Equity Bank", "Centenary Bank")
aliases array List of accepted bank name variations that can be used in validation requests

Stanbic vs Stanchart

Stanbic Bank (code STANBIC) and Stanchart Bank / Standard Chartered (code STAN) are different banks. Use the exact bank name from this list when validating or transferring to avoid provider errors.

Usage Tips

  • Use any of the bank names from the aliases array when calling the /bank-transfer/validate endpoint
  • The code field is a unique identifier for each bank that can be used for reference or filtering
  • Bank names are case-insensitive, so "Equity Bank", "equity bank", or "EQUITY BANK" will all work
  • Always use /bank-transfer/banks to get the current list; bank codes must match exactly for the provider to accept the transfer

Transfer Statuses

When you create or query a transfer, status can be:

  • pending – Created, not yet sent to provider
  • processing – Sent to provider; completion pending
  • completed – Successfully completed
  • failed – Provider or system error; balance is refunded on create failure

Prerequisites

For the API: provide recipient bank details (bank_name, bank_account_number) in each request. The account is validated before the transfer; no verified business bank account is required.

An active bank transfer service (subscription is handled internally)

Sufficient balance in the selected wallet (transfer amount + charge). Use wallet_source = main or card.

Account must not be frozen. Only one transfer can be processing or pending at a time.

Best Practices

1. Check bank transfer availability and settings

Use GET /bank-transfer/services to see if bank transfer is available and to get bank_transfer_settings (minimum transfer amount and pricing details).

2. Verify Balance Before Requesting

Ensure you have sufficient balance in the selected wallet: you pay transfer amount plus charge; the recipient receives the transfer amount. Check available_balance (main wallet) or card_balance (card wallet) from GET /balance.

3. Handle Webhooks

Set up webhooks to receive real-time notifications when your bank transfers are processed, completed, or failed.

4. Store Reference Numbers

Save the reference number returned in the response. Use it to check the status of your transfer later.

5. Check Transfer Status

Transfers can have statuses: "processing" (being processed), "completed" (successful), or "failed" (error occurred). Use the reference to check status via the GET endpoint.

6. Validate the bank account first (recommended)

Validate before sending: Call POST /bank-transfer/validate with bank_name and account_number before creating a transfer. This ensures the recipient account is valid and working, avoids failed transfers and refunds, and returns the account holder name. Use bank names from /bank-transfer/banks (e.g. Stanbic = STANBIC, Stanchart = STAN).

Debugging & Logging

Bank transfer requests and responses are logged for troubleshooting. Check storage/logs/laravel.log and storage/logs/bank-YYYY-MM-DD.log for request/response entries.

Error Responses

Insufficient Balance

{
  "status": "error",
  "message": "Insufficient main wallet balance. Available: UGX 50,000, Required: UGX 103,500...",
  "error_code": "INSUFFICIENT_BALANCE",
  "data": {
    "wallet_source": "main",
    "current_balance": 50000,
    "required_amount": 103500,
    "transfer_amount": 100000,
    "recipient_amount": 100000,
    "charge_amount": 3500
  }
}

Business Account Frozen

{
  "status": "error",
  "message": "Your business account is currently frozen...",
  "error_code": "ACCOUNT_FROZEN"
}

Service Not Subscribed / Invalid Bank Account

403: SERVICE_NOT_SUBSCRIBED – No active bank transfer subscription. 400: BANK_CODE_NOT_FOUND – Bank name not supported. INVALID_BANK_ACCOUNT – Recipient account could not be validated (check account number and bank).

{
  "status": "error",
  "message": "Account could not be validated. Please check the account number and bank.",
  "error_code": "INVALID_BANK_ACCOUNT"
}

Validation Error / Invalid Subscription

422: VALIDATION_ERROR – Invalid or missing fields (see errors). INVALID_SERVICE_SUBSCRIPTION – Subscription invalid or not active.

{
  "status": "error",
  "message": "Please check your input and try again.",
  "error_code": "VALIDATION_ERROR",
  "errors": { "amount": ["Transfer amount must be at least 2500 UGX."] }
}

Request Not Found

{
  "status": "error",
  "message": "Bank transfer request not found.",
  "error_code": "REQUEST_NOT_FOUND"
}

API Connection Failed

500: API_CONNECTION_FAILED – Provider request failed; balance is refunded.

{
  "status": "error",
  "message": "Unable to process bank transfer at this time. Your balance has been refunded...",
  "error_code": "API_CONNECTION_FAILED"
}

Processing Transfer Exists

{
  "status": "error",
  "message": "You already have a bank transfer being processed. Please wait for it to complete before submitting a new transfer.",
  "error_code": "PROCESSING_TRANSFER_EXISTS",
  "data": {
    "processing_transfer": {
      "reference": "BTR-1771595000-7772",
      "amount": "100,000.00",
      "status": "processing",
      "created_at": "2026-02-20 16:14:46"
    }
  }
}

Transfer Failed

{
  "status": "error",
  "message": "Bank transfer could not be completed. Your balance has been refunded. Please try again later or contact support if the issue persists.",
  "error_code": "TRANSFER_FAILED",
  "data": {
    "status_code": "30",
    "status_description": "INVALID ACCOUNT"
  }
}

Unsupported or Unknown Bank

The provider may return UNSUPPORTED/UNKNOWN BANK if the bank code is not recognised. Ensure you use a bank name/code from /bank-transfer/banks. Stanbic Bank uses code STANBIC; Stanchart (Standard Chartered) uses STAN.

{
  "status": "error",
  "message": "UNSUPPORTED/UNKNOWN BANK",
  "error_code": "TRANSFER_FAILED",
  "data": { "status_description": "UNSUPPORTED/UNKNOWN BANK" }
}

Invalid Account (From/To)

Errors such as Invalid Account (From/To) (SCB Code: 78) usually mean the account number does not match the bank (e.g. wrong bank selected for the account). Verify the account with /bank-transfer/validate using the correct bank name from /bank-transfer/banks.

{
  "status": "error",
  "message": "Invalid Account (From/To) (SCB Code: 78)",
  "error_code": "TRANSFER_FAILED",
  "data": { "status_description": "Invalid Account (From/To) (SCB Code: 78)" }
}
Chat on WhatsApp