Fiat Payout

The Fiat Payout flow allows you to send funds from your wallet to an external bank account. This uses the Quotes API with quote_type: "WITHDRAWAL" for same-currency payouts, or quote_type: "EXCHANGE" when the source and target currencies differ.

Overview

  1. Create a Beneficiary — Register the recipient's bank details
  2. Create a WITHDRAWAL Quote — Lock in fees and create a payout quote
  3. Accept the Quote — Execute the payout
  4. Webhook Notification — Receive confirmation when the payout completes

Step 1: Create a Beneficiary

Before initiating a payout, you need to register the recipient as a beneficiary. If you've already created the beneficiary, skip to Step 2.

Endpoint

POST /api/v1/client/beneficiaries

Authentication

http
x-api-key: <your-api-key>x-timestamp: <rfc3339-timestamp>x-signature: <hmac-sha256-signature>

Example: NGN Beneficiary

json
{  "first_name": "John",  "last_name": "Doe",  "email": "john.doe@example.com",  "phone": "+2348012345678",  "bank_name": "Access Bank",  "account_number": "0123456789",  "bank_code": "044",  "country_code": "NG",  "currency_code": "NGN"}

Example: USD Beneficiary (ACH)

json
{  "first_name": "Jane",  "last_name": "Smith",  "email": "jane.smith@example.com",  "phone": "+12025551234",  "bank_name": "Chase Bank",  "account_number": "123456789",  "routing_number": "021000021",  "account_type": "Checking",  "payment_method": "ACH",  "country_code": "US",  "currency_code": "USD"}

Example: CAD Beneficiary (Interac)

CAD beneficiaries use Interac e-Transfer. Only the recipient's email is required — no bank account details needed.

For non-autodeposit recipients, security question and answer are not returned in the accept-quote response. They arrive asynchronously on a transaction webhook while the payout is still PROCESSING — see interac_payout_instruction in the Transaction Webhook docs.

json
{  "first_name": "Alex",  "last_name": "Tremblay",  "email": "alex.tremblay@example.com",  "bank_name": "Interac",  "country_code": "CA",  "currency_code": "CAD"}

Example: TZS Beneficiary (Mobile money)

Mobile money payouts use the same beneficiary and quote flow as bank payouts. List operators with GET /banks/TZ?type=momo and pass the operator code as bank_code.

json
{  "first_name": "Amina",  "last_name": "Hassan",  "email": "amina.hassan@example.com",  "phone": "255712345678",  "bank_name": "HALOTEL",  "bank_code": "HALTZA",  "country_code": "TZ",  "currency_code": "TZS"}

Then create a WITHDRAWAL or EXCHANGE quote with beneficiary_id and accept it — same as Step 2 below.

Currency-Specific Requirements

CurrencyRequired Fields
NGNaccount_number, bank_code, bank_name
TZS / UGX / SLE (MoMo)bank_code (operator from GET /banks/{country}?type=momo), phone, bank_name
GHS (MoMo)bank_code (operator), phone, bank_name
GHS (Bank)account_number, bank_code, bank_name
USD (ACH)account_number, routing_number, payment_method: "ACH", account_type
USD (Wire)account_number, routing_number, bic_swift_code, payment_method: "Wire"
CADemail (Interac e-Transfer)
OtherVaries by country — see Beneficiaries API

Mobile money payouts: Use international phone format without + (e.g. 255712345678). The API rejects traditional bank codes when a mobile money operator is required. Payout execution uses the standard quote accept flow — status progresses through PROCESSING_PAYOUT to COMPLETED via the same transaction webhook as bank payouts.

Supported mobile money operators (pass code as bank_code; see Banks API for the live list):

CountryCurrencyOperators (bank_code)Phone prefix
TZTZSTIGTZA, AIRTZA, HALTZA, VODTZA255
UGUGXMTNUGA, AIRUGA256
GHGHSMTN, VOD, AIR (MoMo); banks via ?type=banks233
SLSLEORASLE, AFRISLE232

Response

json
{  "id": "550e8400-e29b-41d4-a716-446655440000",  "first_name": "John",  "last_name": "Doe",  "bank_name": "Access Bank",  "account_number": "0123456789",  "country_code": "NG",  "currency_code": "NGN",  "status": "pending",  "is_verified": false}

Store the id — you'll use it as beneficiary_id in the next step.

For full details on creating beneficiaries including address objects, validation rules, and all supported currencies, see the Beneficiaries API.


Step 2: Create a WITHDRAWAL Quote

Create a quote to lock in fees and initiate the payout.

Endpoint

POST /api/v1/client/quotes

Authentication

http
x-api-key: <your-api-key>x-timestamp: <rfc3339-timestamp>x-signature: <hmac-sha256-signature>

Request Body (Same-Currency Withdrawal)

Use quote_type: "WITHDRAWAL" when the source and target currencies are the same (e.g., NGN to NGN):

json
{  "source_currency": "NGN",  "target_currency": "NGN",  "source_amount": "50000.00",  "beneficiary_id": "550e8400-e29b-41d4-a716-446655440000",  "quote_type": "WITHDRAWAL",  "tz": "UTC",  "narration": "Vendor payment - Invoice #1234",  "origin_reference": "PAYOUT-2026-001",  "on_behalf_of": "660e8400-e29b-41d4-a716-446655440001"}

Request Body (Cross-Currency Payout)

Use quote_type: "EXCHANGE" when the source and target currencies differ (e.g., USD to NGN). This converts funds and sends to the beneficiary in a single operation:

json
{  "source_currency": "USD",  "target_currency": "NGN",  "source_amount": "100.00",  "beneficiary_id": "550e8400-e29b-41d4-a716-446655440000",  "quote_type": "EXCHANGE",  "tz": "UTC",  "narration": "Salary payment - March 2026",  "origin_reference": "PAYOUT-2026-002",  "on_behalf_of": "660e8400-e29b-41d4-a716-446655440001"}

Required Parameters

ParameterTypeDescription
source_currencystringCurrency to send from (e.g., "NGN", "USD")
target_currencystringCurrency to deliver (e.g., "NGN", "USD")
source_amountstringAmount to send (decimal string)
beneficiary_idstring (UUID)ID of the beneficiary created in Step 1
quote_typestring"WITHDRAWAL" (same-currency) or "EXCHANGE" (cross-currency)
tzstringTimezone (IANA format, e.g., "UTC")
origin_referencestringYour unique reference for idempotency (max 40 chars)
on_behalf_ofstring (UUID)Customer ID on whose behalf this payout is made

Optional Parameters

ParameterTypeDescription
narrationstringDescription for the payout

Success Response (201 Created)

json
{  "quote_id": "123e4567-e89b-12d3-a456-426614174000",  "source": {    "currency": "NGN",    "amount": "50000.00"  },  "target": {    "currency": "NGN",    "amount": "50000.00"  },  "exchange_rate": "1.00",  "fees": {    "processing_fee": "50.00",    "deposit_fee": "0.00",    "payout_fee": "25.00",    "total_fees": "75.00",    "fee_currency": "NGN"  },  "total_payable": "50075.00",  "valid_until": "2026-03-16T15:40:53Z",  "quote_status": "CREATED",  "type": "WITHDRAWAL"}

Store the quote_id — you'll need it to accept the quote.

Quotes expire after approximately 30 minutes. Accept before the valid_until timestamp.


Step 3: Accept the Quote

Accept the quote to execute the payout. Funds will be deducted from your wallet and sent to the beneficiary.

Endpoint

POST /api/v1/client/quotes/{id}/accept

Authentication

http
x-api-key: <your-api-key>x-timestamp: <rfc3339-timestamp>x-signature: <hmac-sha256-signature>

Success Response (200 OK)

json
{  "quote_id": "123e4567-e89b-12d3-a456-426614174000",  "transaction_id": "987e6543-e21b-43d2-b456-426614174111",  "transaction_reference": "TXN-REF-56789",  "total_payable": "50075.00",  "timeline": {    "created_at": "2026-03-16T15:10:53Z",    "accepted_at": "2026-03-16T15:12:30Z",    "estimated_completion": "2026-03-16T15:45:30Z"  },  "status": "PROCESSING"}

Store the transaction_id and transaction_reference for reconciliation.


Step 4: Webhook Notification

Once the payout is processed, you'll receive a transaction webhook with the status:

json
{  "event_type": "transaction",  "transaction_id": "987e6543-e21b-43d2-b456-426614174111",  "transaction_type": "WITHDRAWAL",  "status": "COMPLETED",  "reference": "TXN-REF-56789",  "user_id": "660e8400-e29b-41d4-a716-446655440001",  "created_at": "2026-03-16T15:10:53Z",  "timestamp": "2026-03-16T15:30:00Z",  "source_currency": "NGN",  "source_amount": "50000.00",  "target_currency": "NGN",  "target_amount": "50000.00",  "amount_with_fees": "50075.00",  "fee": "75.00",  "fee_currency": "NGN",  "recipient": {    "account_number": "0123456789",    "account_name": "John Doe",    "bank_name": "Access Bank",    "beneficiary_id": "550e8400-e29b-41d4-a716-446655440000"  }}

For full details on webhook handling and signature verification, see the Webhooks documentation.

CAD Interac (non-autodeposit)

After accept, you receive the usual PROCESSING payout flow. When the provider returns Interac security credentials, you get an additional transaction webhook with status: "PROCESSING" and interac_payout_instruction:

json
{  "event_type": "transaction",  "transaction_type": "WITHDRAWAL",  "status": "PROCESSING",  "payment_completed": false,  "target_currency": "CAD",  "target_amount": "100.00",  "interac_payout_instruction": {    "question": "What is your reference number?",    "answer": "48291"  },  "recipient": {    "email": "alex.tremblay@example.com",    "account_name": "Alex Tremblay",    "beneficiary_id": "550e8400-e29b-41d4-a716-446655440000"  }}

Forward the question and answer to the beneficiary so they can accept the e-Transfer. A final webhook with COMPLETED or FAILED follows when the payout settles. interac_payout_instruction is omitted on all non-CAD payouts and on CAD payouts where credentials are not required.


Verify Payout Status

You can check the payout status at any time by polling the quote:

Endpoint

GET /api/v1/client/quotes/{id}

Success Response

The response includes the current quote_status which progresses through:

CREATEDAWAITING_PAYMENTPAYMENT_CONFIRMEDPROCESSING_PAYOUTCOMPLETED

For full details, see the Quotes API.


Example: Complete Fiat Payout Flow

cURL

Step 1: Create beneficiary

bash
curl -X POST https://api.transfaar.com/api/v1/client/beneficiaries \  -H "Content-Type: application/json" \  -H "x-api-key: your-api-key" \  -H "x-timestamp: 2026-03-16T10:30:00Z" \  -H "x-signature: your-hmac-signature" \  -d '{    "first_name": "John",    "last_name": "Doe",    "email": "john.doe@example.com",    "phone": "+2348012345678",    "bank_name": "Access Bank",    "account_number": "0123456789",    "bank_code": "044",    "country_code": "NG",    "currency_code": "NGN"  }'

Step 2: Create withdrawal quote

bash
curl -X POST https://api.transfaar.com/api/v1/client/quotes \  -H "Content-Type: application/json" \  -H "x-api-key: your-api-key" \  -H "x-timestamp: 2026-03-16T10:31:00Z" \  -H "x-signature: your-hmac-signature" \  -d '{    "source_currency": "NGN",    "target_currency": "NGN",    "source_amount": "50000.00",    "beneficiary_id": "550e8400-e29b-41d4-a716-446655440000",    "quote_type": "WITHDRAWAL",    "tz": "UTC",    "narration": "Vendor payment",    "origin_reference": "PAYOUT-2026-001",    "on_behalf_of": "660e8400-e29b-41d4-a716-446655440001"  }'

Step 3: Accept the quote

bash
curl -X POST https://api.transfaar.com/api/v1/client/quotes/123e4567-e89b-12d3-a456-426614174000/accept \  -H "Content-Type: application/json" \  -H "x-api-key: your-api-key" \  -H "x-timestamp: 2026-03-16T10:32:00Z" \  -H "x-signature: your-hmac-signature"

JavaScript

javascript
async function processFiatPayout(apiKey, secretKey, payoutData) {  const timestamp = new Date().toISOString();
  // Step 1: Create beneficiary (if not already created)  const beneficiary = await apiCall('POST', '/api/v1/client/beneficiaries', apiKey, secretKey, {    first_name: payoutData.recipientFirstName,    last_name: payoutData.recipientLastName,    email: payoutData.recipientEmail,    phone: payoutData.recipientPhone,    bank_name: payoutData.bankName,    account_number: payoutData.accountNumber,    bank_code: payoutData.bankCode,    country_code: payoutData.countryCode,    currency_code: payoutData.currency  });
  // Step 2: Create withdrawal quote  const quote = await apiCall('POST', '/api/v1/client/quotes', apiKey, secretKey, {    source_currency: payoutData.currency,    target_currency: payoutData.currency,    source_amount: payoutData.amount,    beneficiary_id: beneficiary.id,    quote_type: 'WITHDRAWAL',    tz: 'UTC',    narration: payoutData.narration,    origin_reference: payoutData.reference,    on_behalf_of: payoutData.customerId  });
  // Step 3: Accept the quote  const result = await apiCall('POST', `/api/v1/client/quotes/${quote.quote_id}/accept`, apiKey, secretKey);
  return {    transactionId: result.transaction_id,    transactionReference: result.transaction_reference,    status: result.status  };}
async function apiCall(method, path, apiKey, secretKey, body = null) {  const timestamp = new Date().toISOString();  const bodyStr = body ? JSON.stringify(body) : '';  const signature = generateHMACSignature(bodyStr, timestamp, secretKey);
  const response = await fetch(`https://api.transfaar.com${path}`, {    method,    headers: {      'Content-Type': 'application/json',      'x-api-key': apiKey,      'x-timestamp': timestamp,      'x-signature': signature    },    ...(body && { body: bodyStr })  });
  return await response.json();}

Best Practices

  1. Create Beneficiaries Once — Beneficiaries are reusable. Create them once and store the id for future payouts.
  2. Use Unique Origin References — Each origin_reference must be unique. Duplicate references will be rejected, preventing accidental double payouts.
  3. Accept Quotes Promptly — Quotes expire after ~30 minutes. Accept before the valid_until timestamp.
  4. Handle Webhooks — Always verify payout completion via webhooks rather than relying solely on polling.
  5. Validate Balances — Ensure your wallet has sufficient balance to cover the payout amount plus fees before creating a quote.
  6. Store References — Save quote_id, transaction_id, transaction_reference, and your origin_reference for reconciliation.