Bank Transfer API

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

Overview

Bank Transfer allows you to move funds from your business balance to a bank account. Charges are calculated from your business settings: fixed (a set charge per transfer) or percentage (a percentage of the transfer amount). 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, and recipient bank details (API: account is validated before sending).
  2. Charge is calculated from your amount type (fixed or percentage). Total deducted = amount + charge.
  3. Balance is deducted 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.

Amount Type Settings

Your business can configure how bank transfer charges are calculated. You can choose between "fixed" (default) or "percentage" amount types. This setting is managed in your Bank Settings page.

Fixed Amount Type (Default)

When using fixed amount type, a fixed charge amount is applied per transaction. You pay transfer amount plus the fixed charge; the recipient receives the transfer amount.

Example: Transfer 100,000 UGX with fixed charge of 50,000 UGX
• Transfer amount: 100,000 UGX
• Fixed charge: 50,000 UGX
• Total deducted: 150,000 UGX
• Recipient receives: 100,000 UGX

Percentage Amount Type

When using percentage type, the charge is calculated as a percentage of the transfer amount. You specify the transfer amount, and the charge is calculated and added on top.

Example: Transfer 100,000 UGX with 2% charge
• Transfer amount: 100,000 UGX
• Percentage charge (2%): 2,000 UGX
• Total deducted: 102,000 UGX
• Recipient receives: 100,000 UGX

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"
}

bank_account_name and bank_branch are optional. If bank_account_name is omitted, it is fetched from the validate-account step. 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 (fixed or percentage).
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)

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",
      "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": 1000,
      "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 (transfer amount + charge). Charge comes from your settings (fixed or percentage).

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 (amount type, charge, minimum transfer amount).

2. Verify Balance Before Requesting

Ensure you have sufficient balance: you pay transfer amount plus charge; the recipient receives the transfer amount.

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 balance. Available: UGX 50,000, Required: UGX 103,500...",
  "error_code": "INSUFFICIENT_BALANCE",
  "data": {
    "current_balance": 50000,
    "required_amount": 103500,
    "transfer_amount": 100000,
    "recipient_amount": 100000,
    "charge_amount": 3500,
    "charge_percentage": 3.5
  }
}

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 1000 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