Fynlo API Documentation

Welcome to Fynlo's developer hub. Our API lets you process payments and mock complex scenarios in sandbox using V4 headers.

Authentication

Use OAuth 2.0 Client Credentials to obtain a Bearer Token.

POST /api/v1/oauth/token
curl -X POST "https://api.fynlo.makylegacy.com/api/v1/oauth/token" \
-d '{ "client_id": "...", "client_secret": "...", "grant_type": "client_credentials" }'

Step 1: Initiate Direct Charge

Initiate a white-label direct charge.

POST /api/v1/charges/card

Initiate a white-label direct charge.

Step 2: Handle Validation (OTP)

If the response status is pending and suggests OTP, use the validate endpoint.

POST /api/v1/charges/validate
{
  "otp": "123456",
  "flw_ref": "FLW-...",
  "type": "card"
}

Collections - Inflow

Accept payments seamlessly across multiple channels. Our Orchestrator & General flows support all methods.

Card Payments

Accept Debit/Credit cards. (This is the default type: card in our Orchestrator).

Integration Methods

Mobile Money

Collection via Mobile Money (MOMO). Supports Push Notifications and Redirect flows.

POST /api/v1/charges/orchestrate
{
  "amount": 1500,
  "currency": "GHS",
  "email": "user@example.com",
  "name": "Jane Doe",
  "reference": "MOMO-123",
  "payment_method": {
    "type": "mobile_money",
    "mobile_money": {
        "network": "MTN",
        "country_code": "233",
        "phone_number": "054xxxxxxx"
    }
  }
}

Note: Response will contain meta.authorization.mode: "redirect" or "callback".

Pay With Bank Transfer

Generate a virtual bank account for your customer to transfer funds to.

Create Dynamic Account (One-time)

POST /api/v1/virtual-accounts
{
  "email": "user@example.com",
  "name": "John Doe",
  "amount": 5000,
  "currency": "NGN",
  "reference": "VA-12345",
  "is_permanent": false,
  "expires_in": 3600
}

USSD

Charge customers via USSD string (Offline).

POST /api/v1/charges/orchestrate
{
  "amount": 1000,
  "currency": "NGN",
  "email": "user@example.com",
  "payment_method": {
      "type": "ussd",
      "ussd": {
          "account_bank": "057" // e.g., Zenith Bank
      }
  }
}

Note: You must get the list of banks first (see Flutterwave generic docs).

OPay

Direct charge via OPay wallet.

POST /api/v1/charges/orchestrate
{
  "amount": 500,
  "currency": "NGN",
  "email": "user@example.com",
  "payment_method": {
      "type": "opay",
      "opay": {
          "phone_number": "08012345678"
      }
  }
}

General Payment Flow (Advanced)

For maximum control, use the 5-step flow:

  1. Create Customer:POST /customers

    Returns customer_id (e.g., cus_...)

  2. Create Payment Method:POST /payment-methods

    Returns payment_method_id (e.g., pmd_...)

  3. Initiate Charge:POST /charges

    Use IDs from Step 1 & 2. Returns auth instructions.

  4. Authorize (Validation):POST /charges/validate

    Submit OTP/PIN if required.

  5. Verify:GET /payments/verify/:id

Orchestrator Flow (Simple)

For quick, one-off charges, use our Orchestrator endpoint. It automatically encrypts your raw card data, creates the customer, and initiates the charge in a single request.

Step 1: Initiate Charge

POST /api/v1/charges/orchestrate
{
  "amount": 5000,
  "currency": "NGN",
  "reference": "TX-123456789",
  "email": "user@example.com",
  "name": "John Doe",
  "card_number": "5399...",
  "cvv": "123",
  "expiry_month": "12",
  "expiry_year": "25"
}

Note: Send raw card details. We handle V4 encryption securely.

Step 2: Handle Authorization (PIN/OTP)

If the response suggests next_action: requires_pin or requires_otp, use the Authorize endpoint. This handles V4 encryption for you.

For PIN (First Step)

PUT /api/v1/charges/:id/authorize
{
  "mode": "pin",
  "pin": "1234"
}

For OTP (Second Step)

PUT /api/v1/charges/:id/authorize
{
  "mode": "otp",
  "otp": "123456"
}

Step 3: Verify Status

Always verify the final status before giving value.

GET /api/v1/payments/verify/:transaction_id

USSD Payments

Generate USSD strings for offline payments. USSD payments provide a reliable, internet-free option for customers to complete transactions using their mobile devices.

Feature Availability: USSD payments are currently only available for NGN (Nigerian Naira) collections.

How it Works

To initiate a USSD payment, a USSD code is generated for the customer. The customer dials this code to authorize the transaction with their PIN.

Step 1: Create a Customer

First, create a customer profile to link the transaction to.

POST /api/v1/customers
{
  "email": "johndoe@example.com",
  "name": {
    "first": "John",
    "last": "Doe"
  },
  "phone": {
    "country_code": "234",
    "number": "08012345678"
  }
}

Step 2: Get Account Bank Code

Retrieve a list of supported banks to show to the customer.

GET /api/v1/banks?country=NG

Step 3: Create USSD Payment Method

Create the payment method specifying the user's bank code.

POST /api/v1/payment-methods
{
  "type": "ussd",
  "ussd": {
    "account_bank": "044" 
  }
}

Note: "044" is the code for Access Bank (example).

Step 4: Create the Charge

Initiate the charge using the IDs from previous steps.

POST /api/v1/charges
{
  "reference": "TX-USSD-001",
  "currency": "NGN",
  "amount": 250,
  "customer_id": "cus_...",
  "payment_method_id": "pmd_...",
  "redirect_url": "https://yoursite.com/callback",
  "meta": {
    "order_id": "12345"
  }
}

Step 5: User Action

The response will contain a payment_instruction with a USSD code (e.g., *1414#). Display this note to the user so they can dial it and verify.

"Please dial *1414# to complete this transaction"


Payouts - Outflows

Introduction

Learn how to transfer funds globally across multiple channels. We provide a suite of services that enable you to send money seamlessly to bank accounts, mobile numbers, and wallets.

We support two main flows:

  • Transfer Orchestrator: Best for one-time transfers. Quicker to initiate as all info is collected at once.
  • General Transfer Flow: Best for recurring transfers. Customizable flow offering more control (Create Recipient → Transfer).

Transfer Orchestrator

Unlike the general transfer flow, the direct transfer flow lets you set up both recipient and sender in a single request.

This flow has two key steps:

  1. Initiate the transfer (Instant, Deferred, or Scheduled).
  2. Verify the transfer status.

Step 1: Initiate Transfer (Instant)

POST /api/v1/transfers/orchestrate
{
  "action": "instant",
  "type": "bank",
  "reference": "TRF-INSTANT-001",
  "payment_instruction": {
    "source_currency": "NGN",
    "amount": {
        "value": 50000,
        "applies_to": "destination_currency"
    },
    "destination_currency": "NGN",
    "recipient": {
        "bank": {
             "account_number": "0122333334",
             "code": "044"
        }
    }
  }
}

Optional: Scheduled Transfer

POST /api/v1/transfers/orchestrate
{
  "action": "scheduled",
  "type": "bank",
  "reference": "TRF-FUTURE-001",
  "disburse_option": {
      "date_time": "2025-12-25 09:00:00",
      "timezone": "UTC"
  },
  "payment_instruction": {
    "source_currency": "NGN",
    "amount": { "value": 5000, "applies_to": "destination_currency" },
    "destination_currency": "NGN",
    "recipient": {
        "bank": {
             "account_number": "0122333334",
             "code": "044"
        }
    }
  }
}

Step 2: Verify Status

GET /api/v1/transfers/:id

General Transfer Flow

Create reusable beneficiaries and then transfer.

Step 1: Create Recipient

POST /api/v1/transfers/recipients
{
  "type": "bank_ngn",
  "bank": {
    "account_number": "0690000031",
    "code": "044"
  }
}

Step 2: Initiate Transfer

POST /api/v1/transfers
{
  "action": "instant",
  "reference": "TRF-MULTI-001",
  "payment_instruction": {
      "source_currency": "NGN",
      "amount": { "value": 5000, "applies_to": "source_currency" },
      "recipient_id": "rcb_12345"
  }
}

Bank Account Transfers

Transfer funds directly to a bank account. This example uses the Orchestrator Flow to send funds Internationally (USD to AUD).

POST /api/v1/transfers/orchestrate
{
  "action": "instant",
  "type": "bank",
  "reference": "TRF-AUD-001",
  "payment_instruction": {
    "amount": {
      "value": 1000,
      "applies_to": "destination_currency"
    },
    "source_currency": "USD",
    "destination_currency": "AUD",
    "recipient": {
      "bank": {
        "routing_number": "062123",
        "swift_code": "BANK123",
        "name": "Commonwealth Bank of Australia",
        "account_number": "123456789"
      },
      "name": {
        "first": "Bob",
        "last": "Builder"
      },
      "email": "user@example.com",
      "address": {
        "line1": "7A, Awesome Apartments",
        "city": "Cool City",
        "country": "AU"
      }
    }
  }
}

Note: Use source_currency and destination_currency for cross-currency transfers.

Mobile Money Transfers

Transfer funds directly to a mobile money wallet. This example uses the Orchestrator Flow to send GH Cedis to an MTN wallet.

POST /api/v1/transfers/orchestrate
{
  "action": "instant",
  "type": "mobile_money",
  "reference": "TRF-MOMO-GHS-001",
  "narration": "GHS MOMO TRANSFER EXAMPLE",
  "payment_instruction": {
    "source_currency": "NGN",
    "destination_currency": "GHS",
    "amount": {
        "value": 1000,
        "applies_to": "destination_currency"
    },
    "recipient": {
        "name": {
            "first": "John",
            "last": "Doe"
        },
        "mobile_money": {
            "network": "MTN",
            "msisdn": "2339012345678"
        }
    }
  }
}
Important: The msisdn must include the country code (e.g., 233... for Ghana).

Wallet-to-Wallet Transfers

Transfer funds directly to another Flutterwave Merchant (Identifier/MID).

POST /api/v1/transfers/orchestrate
{
  "action": "instant",
  "type": "wallet",
  "reference": "TRF-W2W-001",
  "narration": "WALLET TRANSFER EXAMPLE",
  "payment_instruction": {
    "source_currency": "NGN",
    "destination_currency": "NGN",
    "amount": {
        "value": 100,
        "applies_to": "destination_currency"
    },
    "recipient": {
        "wallet": {
            "provider": "flutterwave",
            "identifier": "00118468" // Merchant ID
        }
    }
  }
}

Note: Use your own MID as the identifier for Intra-wallet transfers (moving funds between your own currency balances).

Settlements

Track the funds settled to your bank account or wallet.

GET /api/v1/operations/settlements

Query params: page, from, to

Refunds

Initiate full or partial refunds for transactions.

Initiate Refund

POST /api/v1/operations/refunds
{
  "amount": 2000,
  "reason": "duplicate",
  "charge_id": "chg_Jwb2Y7ZbJQ"
}

Chargebacks

Manage disputes. Accept liability or decline with proof.

Accept/Decline Dispute

PUT /api/v1/operations/chargebacks/:id
{
  "status": "declined", // or "accepted"
  "comment": "Value provided. See proof.",
  "uploaded_proof": "https://example.com/receipt.pdf"
}

Use Cases

Fynlo is built to scale with you. Whether you are a freelancer just starting out or a growing enterprise, our tools are designed to fit your unique needs.

Sole Entrepreneurs

Freelancers, Consultants, and Creators.

  • Quick Collections: Accept payments via Payment Links or simple invoices (No coding required).
  • Mobile Wallet Payouts: Get paid instantly to your MoMo wallet or Personal Bank Account.
  • Low Fees: Pay as you go with no monthly maintenance fees.

Small Businesses

Retail Stores, Boutiques, and Service Providers.

  • Unified Payments: Accept Card, Mobile Money, and Bank Transfers at checkout.
  • Chargeback Protection: Our fraud detection helps protect your revenue.
  • Easy Reconciliation: View all your sales in one simple Dashboard.

Medium Businesses

Growing E-commerce and Tech Startups.

  • Automated Operations: Use our /operations API to automate refunds and settlements.
  • Bulk Payouts: Pay suppliers and staff instantly using our Transfer Orchestrator.
  • Global Reach: Accept international payments (USD, GBP, EUR) and settle in local currency.
  • Developer First: Robust Webhooks and Sandbox environment for testing specific scenarios.

Deployment Information

Production Domains

  • Frontend: https://fynlo.makylegacy.com
  • API: https://api.fynlo.makylegacy.com (Recommended)

When deploying, ensure your NEXT_PUBLIC_API_URL is set to the API domain.

Testing & Scenarios

In Sandbox, use the X-Scenario-Key header to simulate specific outcomes. Format: scenario:<value>&issuer:<value>

Card Scenarios

  • scenario:auth_pin - Mock PIN auth.
  • scenario:auth_3ds - Mock 3DS flow.
  • scenario:auth_avs - Mock No-Auth/AVS.

Issuer Responses (Simulate Failures)

issuer:approvedissuer:insufficient_fundsissuer:incorrect_pinissuer:expired_cardissuer:do_not_honorissuer:suspected_fraud

Example: Simulate 3DS Success

-H "X-Scenario-Key: scenario:auth_3ds&issuer:approved"

Errors

Standard V4 codes (10400, 10401, etc.) are returned.