Skip to main content
FluxanDeveloper docs
简体中文

Create a disbursement

API contract v1.0.0

createDisbursement
POST/v1/disbursements
Required scopesdisbursements:writeIdempotency key required

The Developer role can't grant disbursements:write by default. Use a key created by an Owner or Admin.

Note

The disbursement endpoints require the platform to enable disbursements for the current environment; otherwise they return 403 capability_disabled. For funds rules, payee rules and handling outcomes, see Disbursements.

Pays a third-party payee from your settleable balance in the currency, on your instruction. The merchant and environment come only from the API key, and the platform calculates the fee, so the request has no merchant ID, fee or channel field. The disbursements capability must be enabled in the current environment; merchant_disbursement_no is unique within the merchant and environment, and an idempotency key is required. At acceptance, the amount plus fee are held in a single atomic operation; if settleable isn't enough, the API returns 422 insufficient_balance and creates nothing. Checks run in this order: request format, idempotency, merchant status and capability, currency and channel, whole-unit amount and payee details, unique merchant disbursement ID, fee, limits, platform pause, risk lists, settleable balance. The first failed check returns, and any response other than 201 creates no disbursement and holds no funds. 201 and 200 only mean accepted (status=processing). Whether the money was paid shows in status or in a disbursement.* event whose signature you've verified. After a timeout or a 5xx, only retry with the original idempotency key or look the disbursement up by its merchant disbursement ID; never resend with a new ID or a new key: money that has been paid out can't be pulled back. There is no cancel endpoint.

Request headers

Request headersRequiredFormatDescription
AuthorizationYesBearer <API_KEY>

Server-side key, to be used only on the merchant's server. The merchant and environment are derived from the key. A missing, invalid, revoked or expired key returns 401.

Content-TypeYesapplication/json

The request body is UTF-8 encoded JSON.

Idempotency-KeyYes1–255 visible ASCII characters

Idempotency key, 1–255 visible ASCII characters. The scope is merchant, environment, method and path: with the same key and matching core business parameters, the original business object is reused, and conflicting parameters return 409 idempotency_conflict. Use one key per submission, and reuse the same key when you retry after a network error; use a new key for a new submission, such as a card change. For concurrent requests with the same key, only one request gets to run, and the others wait for its transaction to commit and then return the same object (200). If the wait times out, the API returns 409 operation_in_progress (retryable=true); retry later with the original key. A request rejected with 400, 401, 403 or 422 doesn't consume the idempotency key; fix it and submit with a new key.

Request body

JSON object CreateDisbursementRequest

NameTypeRequiredDescription
merchant_disbursement_no
stringRequired

Merchant disbursement ID: 1-64 letters, numbers, periods, underscores, colons or hyphens. It's unique within one merchant and environment and case-sensitive. Generate and save it before you send the request, and use it to look the disbursement up after a timeout.

Constraints: Format ^[A-Za-z0-9._:-]{1,64}$

Example: DSB-DEMO-001

amount
integer(int64)Required

Disbursement amount, in the smallest currency unit. For a currency whose disbursement channel takes whole units only, it must be a whole number of currency units: 30000 is fine for PKR, 30050 isn't.

Constraints: ≥ 1

Example: 30000

currency
stringRequired

Uppercase three-letter currency code; only PKR for now.

Constraints: Format ^[A-Z]{3}$

Example: PKR

method
stringRequired

Receiving method.

  • easypaisa: EasyPaisa wallet
  • jazzcash: JazzCash wallet
  • bank: bank account
  • easypaisa: EasyPaisa wallet
  • jazzcash: JazzCash wallet
  • bank: Bank account
  • More values may be added in the future. Don't treat a status you don't recognize as success.

Example: easypaisa

payee
CreateDisbursementPayeeRequired

Payee. The full payee account appears only in the create request; later responses, events, the merchant portal and exports return only the last 4 digits. Don't record the full account in your logs, error reports or tickets either. A disbursement can't be pulled back once paid: if the account number is wrong and the name happens to match, the money goes into that account.

account
stringRequired

Payee account. If it's invalid, the API returns 400 invalid_request (field is payee.account).

  • easypaisa, jazzcash: 11 digits starting with 03. The platform doesn't normalize anything: your system removes spaces and hyphens, or changes +92 to 0
  • bank: 9–24 letters and digits after removing spaces and converting to uppercase; an account starting with PK is validated as an IBAN (24 characters, with valid mod 97 check digits) The platform doesn't run a card number (Luhn) check on payee accounts; an account that's well formed but doesn't exist fails after acceptance.

Constraints: Length 1–64

Example: 03001234567

name
stringRequired

Payee name. It must match the real name on the account: if it doesn't, the request is accepted and the disbursement fails later (payee_account_invalid). It can't contain digits that look like a card number (400 sensitive_data_rejected).

Constraints: Length 2–64

Example: Test Payee

bank_code
stringNo

Payee bank code; required when method is bank, and not allowed for other receiving methods.

  • HBL: Habib Bank Limited
  • UBL: United Bank Limited
  • MCB: MCB Bank Limited
  • MCB_ARIF_HABIB: MCB Arif Habib
  • NBP: National Bank of Pakistan
  • ALFALAH: Bank Alfalah Limited
  • ALLIED: Allied Bank Limited
  • ASKARI: Askari Bank Limited
  • FAYSAL: Faysal Bank Limited
  • MEEZAN: Meezan Bank
  • AL_HABIB: Bank Al Habib Limited
  • HABIB_METRO: Habib Metropolitan Bank
  • BANK_ISLAMI: Bank Islami Pakistan Limited
  • DUBAI_ISLAMIC: Dubai Islamic Bank Pakistan
  • PUNJAB_BANK: The Bank of Punjab
  • SINDH_BANK: Sindh Bank Limited
  • SONERI: Soneri Bank Limited
  • SUMMIT: Summit Bank Limited
  • SILK: Silk Bank Limited
  • SAMBA: Samba Bank Limited
  • JS: JS Bank Limited
  • NIB: NIB Bank Limited
  • APNA: Apna Microfinance Bank
  • CITI: Citi Bank NA
  • NRSP: NRSP Microfinance Bank
  • CDNS: Central Directorate of National Savings
  • HBL: Habib Bank Limited
  • UBL: United Bank Limited
  • MCB: MCB Bank Limited
  • MCB_ARIF_HABIB: MCB Arif Habib
  • NBP: National Bank of Pakistan
  • ALFALAH: Bank Alfalah Limited
  • ALLIED: Allied Bank Limited
  • ASKARI: Askari Bank Limited
  • FAYSAL: Faysal Bank Limited
  • MEEZAN: Meezan Bank
  • AL_HABIB: Bank Al Habib Limited
  • HABIB_METRO: Habib Metropolitan Bank
  • BANK_ISLAMI: Bank Islami Pakistan Limited
  • DUBAI_ISLAMIC: Dubai Islamic Bank Pakistan
  • PUNJAB_BANK: The Bank of Punjab
  • SINDH_BANK: Sindh Bank Limited
  • SONERI: Soneri Bank Limited
  • SUMMIT: Summit Bank Limited
  • SILK: Silk Bank Limited
  • SAMBA: Samba Bank Limited
  • JS: JS Bank Limited
  • NIB: NIB Bank Limited
  • APNA: Apna Microfinance Bank
  • CITI: Citi Bank NA
  • NRSP: NRSP Microfinance Bank
  • CDNS: Central Directorate of National Savings
  • More values may be added in the future. Don't treat a status you don't recognize as success.

Example: MEEZAN

customer_ip
stringNo

The IPv4 or IPv6 address the payee (your user) used to submit this payout request in your system. Resolve it through a trusted proxy chain, and don't send your server's IP address. Used for risk checks; optional.

Constraints: Length 1–45

Example: 203.0.113.10

description
stringNo

Disbursement description, for your own reconciliation. It can't contain digits that look like a card number.

Constraints: Max length 256

Example: Sample disbursement

metadata
objectNo

Custom key-value pairs set by the merchant: up to 20 keys. A key can be up to 40 characters, and a value is a string of up to 200 characters. Don't put sensitive data such as card numbers in it. The platform checks for such data and rejects the request.

Constraints: Up to 20 keysMax value length 200

Example: {"business_order_type":"one_time_purchase"}

Request example

Wallet disbursement: a PKR amount must be a multiple of 100

curl
curl -X POST 'https://api.fluxan.co/v1/disbursements' \
  -H 'Authorization: Bearer <TEST_SERVER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: <UNIQUE_OPERATION_KEY>' \
  -d '{
  "merchant_disbursement_no": "DSB-DEMO-001",
  "amount": 30000,
  "currency": "PKR",
  "method": "easypaisa",
  "payee": {
    "account": "03001234567",
    "name": "Test Payee"
  },
  "customer_ip": "203.0.113.10",
  "description": "Sample disbursement",
  "metadata": {
    "customer_ref": "CUSTOMER-DEMO-001"
  }
}'

Placeholders to replace

  • <TEST_SERVER_API_KEY>: Test-environment server API key, created on the "API Key" page of the merchant portal
  • <UNIQUE_OPERATION_KEY>: The idempotency key you generate for this request (for example, a UUID); reuse the same key when you retry after a network error

Bank account disbursement: bank_code is required when method=bank

curl
curl -X POST 'https://api.fluxan.co/v1/disbursements' \
  -H 'Authorization: Bearer <TEST_SERVER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: <UNIQUE_OPERATION_KEY>' \
  -d '{
  "merchant_disbursement_no": "DSB-DEMO-002",
  "amount": 30000,
  "currency": "PKR",
  "method": "bank",
  "payee": {
    "account": "012345678901",
    "name": "Test Payee",
    "bank_code": "MEEZAN"
  }
}'

Placeholders to replace

  • <TEST_SERVER_API_KEY>: Test-environment server API key, created on the "API Key" page of the merchant portal
  • <UNIQUE_OPERATION_KEY>: The idempotency key you generate for this request (for example, a UUID); reuse the same key when you retry after a network error

Note

Server API keys must only be used on your own server, so the docs site doesn't offer interactive API testing. Get a test key: API Key page in the merchant portal (opens the merchant portal in a new tab) (Test environment; Requires Owner, Admin or Developer)

Responses

An idempotent replay with the same key; returns the latest state of the original disbursement.

Replay with the same key: the latest state of the original disbursement is returned, and nothing is paid again
{
  "id": "dsb_demo_001",
  "version": 1,
  "merchant_disbursement_no": "DSB-DEMO-001",
  "environment": "test",
  "amount": 30000,
  "currency": "PKR",
  "fee": 600,
  "method": "easypaisa",
  "payee": {
    "account_last4": "4567",
    "name": "Test Payee",
    "bank_code": null,
    "bank_name": null,
    "utr": null
  },
  "customer_ip": "203.0.113.10",
  "status": "processing",
  "resolution_status": "known",
  "failure": null,
  "paid_amount": null,
  "succeeded_at": null,
  "failed_at": null,
  "reversed_at": null,
  "description": "Sample disbursement",
  "metadata": {
    "customer_ref": "CUSTOMER-DEMO-001"
  },
  "source": "api",
  "batch_id": null,
  "created_at": "2026-10-29T08:00:00Z",
  "updated_at": "2026-10-29T08:00:00Z",
  "request_id": "req_demo_103"
}

Response fields

Returns an object. See the object section for field descriptions.

NameTypeRequiredDescription
id
stringRequired

Disbursement ID (dsb_).

Example: dsb_demo_001

version
integer(int64)Required

Object version, which increases with every effective change. Compare it when you process events, and don't order events by status name.

Constraints: ≥ 1

Example: 1

merchant_disbursement_no
stringRequired

Merchant disbursement ID, unique within one merchant and environment and case-sensitive. When a disbursement created in the merchant portal didn't set one, it equals the disbursement ID.

Example: DSB-DEMO-001

environment
stringRequired

Environment: test is the test environment and live is the live environment. Test and live objects are isolated from each other.

  • test
  • live
  • More values may be added in the future. Don't treat a status you don't recognize as success.

Example: test

amount
integer(int64)Required

Disbursement amount, in the smallest currency unit.

Constraints: ≥ 1

Example: 30000

currency
stringRequired

Uppercase three-letter currency code.

Constraints: Format ^[A-Z]{3}$

Example: PKR

fee
integer(int64)Required

Disbursement fee, in the smallest currency unit. It's calculated and held at acceptance with the rate version in effect then; later rate changes don't affect it.

Constraints: ≥ 0

Example: 600

method
stringRequired

Receiving method.

  • easypaisa: EasyPaisa wallet
  • jazzcash: JazzCash wallet
  • bank: bank account
  • easypaisa: EasyPaisa wallet
  • jazzcash: JazzCash wallet
  • bank: Bank account
  • More values may be added in the future. Don't treat a status you don't recognize as success.

Example: easypaisa

payee
DisbursementPayeeRequired

Payee. The full payee account appears only in the create request; responses, events, the merchant portal and exports return only the last 4 digits.

account_last4
stringRequired

Last 4 digits of the payee account.

Example: 4567

name
stringRequired

Payee name, as sent in the request.

Example: Test Payee

bank_code
DisbursementBankCodeNullableRequired

Bank code; null when the receiving method isn't bank.

  • HBL
  • UBL
  • MCB
  • MCB_ARIF_HABIB
  • NBP
  • ALFALAH
  • ALLIED
  • ASKARI
  • FAYSAL
  • MEEZAN
  • AL_HABIB
  • HABIB_METRO
  • BANK_ISLAMI
  • DUBAI_ISLAMIC
  • PUNJAB_BANK
  • SINDH_BANK
  • SONERI
  • SUMMIT
  • SILK
  • SAMBA
  • JS
  • NIB
  • APNA
  • CITI
  • NRSP
  • CDNS
  • More values may be added in the future. Don't treat a status you don't recognize as success.
bank_name
stringNullableRequired

Name of the payee's bank, as returned by the disbursement channel; may be null.

Example: null

utr
stringNullableRequired

Reference number from the payee's wallet or bank, only after success; the payee uses it to trace the money with the wallet or bank. Kept after a reversal.

Example: null

customer_ip
stringNullableRequired

The IP address the payee used to submit the payout request in your system; null for disbursements created in the merchant portal.

Example: 203.0.113.10

status
stringRequired

Disbursement status. Clients must not treat an unknown status as success; treat it as not finished and query it.

  • processing: accepted, and the amount plus fee are held; the money is being paid or the outcome isn't confirmed yet. This isn't a failure
  • succeeded: the disbursement succeeded; the amount paid may be less than amount, and the difference is back in settleable
  • failed: the disbursement clearly failed, and the amount and fee are back in settleable; terminal state
  • reversed: sent back by the payee's wallet or bank after it succeeded, so the payee didn't receive the money; the principal and fee are back in settleable; terminal state
  • processing: Accepted, and the amount plus fee are held; the money is being paid or the outcome isn't confirmed yet. This isn't a failure
  • succeeded: The disbursement succeeded; the amount paid may be less than amount, and the difference is back in settleable
  • failed: The disbursement clearly failed, and the amount and fee are back in settleable; terminal state
  • reversed: Sent back by the payee's wallet or bank after it succeeded, so the payee didn't receive the money; the principal and fee are back in settleable; terminal state
  • More values may be added in the future. Don't treat a status you don't recognize as success.

Example: processing

resolution_status
stringRequired

Outcome confirmation.

  • known: outcome confirmed
  • needs_query: outcome unknown; the platform is running a status query against the provider using the original identifiers
  • manual_review: past the automatic query window and handed over to manual follow-up; never marked as failed automatically
  • known: Outcome confirmed
  • needs_query: Outcome unknown; the platform is running a status query against the provider using the original identifiers
  • manual_review: Past the automatic query window and handed over to manual follow-up; never marked as failed automatically
  • More values may be added in the future. Don't treat a status you don't recognize as success.

Example: known

failure
DisbursementFailureNullableRequired

The reason when status is failed; otherwise null.

code
stringRequired

Failure reason code; see x-failure-codes.disbursement for the values. Tolerate new values, and handle any code you don't recognize as upstream_failed.

Example: payee_account_invalid

message
stringRequired

Sanitized description.

Example: Invalid payee account or name mismatch.

paid_amount
integer(int64)NullableRequired

Amount paid, only after success. It may be less than amount, and the difference is back in settleable; kept after a reversal.

Constraints: ≥ 0

Example: null

succeeded_at
string(date-time)NullableRequired

The time the disbursement succeeded; kept after a reversal.

Example: null

failed_at
string(date-time)NullableRequired

The time the disbursement failed.

Example: null

reversed_at
string(date-time)NullableRequired

The time the disbursement was reversed.

Example: null

description
stringNullableRequired

Disbursement description, for your own reconciliation.

Constraints: Max length 256

Example: Sample disbursement

metadata
objectNo

Custom key-value pairs set by the merchant: up to 20 keys. A key can be up to 40 characters, and a value is a string of up to 200 characters. Don't put sensitive data such as card numbers in it. The platform checks for such data and rejects the request.

Constraints: Up to 20 keysMax value length 200

Example: {"business_order_type":"one_time_purchase"}

source
stringRequired

Disbursement source.

  • api: API
  • portal: merchant portal, single
  • batch: merchant portal, batch
  • api: API
  • portal: Merchant portal (single)
  • batch: Merchant portal (batch)
  • More values may be added in the future. Don't treat a status you don't recognize as success.

Example: api

batch_id
stringNullableRequired

Batch ID (dsbb_), only for disbursements created by a batch in the merchant portal.

Example: null

created_at
string(date-time)Required

The time the request was accepted.

Example: 2026-10-29T08:00:00Z

updated_at
string(date-time)Required

The time of the latest change.

Example: 2026-10-29T08:00:00Z

request_id
stringRequired

Unique identifier of this request, used for troubleshooting and support.

Example: req_demo_001

Possible error codes

HTTPError codeWhen it occursHow to handle
400invalid_requestIdempotency-Key is missing; the order ID contains invalid characters; payment_method_data and payment_method_id are submitted together; confirm=false carries card details; confirm=true has no return_url; a checkout session request sends both groups of identity parameters, or neither; a browser_info / billing_details field the provider requires is missing; the currency's provider takes whole units only (such as PKR) but amount has a fraction; a disbursement's payee details (payee.account, payee.name, payee.bank_code) or customer_ip are invalid; the cursor, time format or limit is invalidFix the request according to error.field and resubmit it; don't charge again automatically
400sensitive_data_rejectedDigits that look like a card number, or similar data, appear in description or metadata; a disbursement's payee.name is checked the same wayRemove the sensitive content and resubmit; check why your system wrote card data into these fields
403capability_disabledDirect card isn't enabled; the payment or refund request capability has been turned off; the currency isn't enabled; the disbursements capability isn't enabled in the current environmentUse hosted checkout instead, or contact platform Operations
403merchant_not_activeThe merchant has been suspended or closed; callbacks, queries and notifications for business that was already accepted aren't affectedContact platform Operations
409idempotency_conflictThe key was reused but the amount, currency, order ID or similar parameters differ; for disbursements, the same key with any request field different (payee details included)An integration defect: check how idempotency keys are generated; don't replay the original key with changed parameters; use a new key for a new operation. A disbursement conflict carries related_resource_id, pointing to the disbursement already accepted with this key: retrieve it first, and don't pay another one
409operation_in_progressRequests with the same key were sent concurrentlyRetry later with the same key, or retrieve related_resource_id; don't switch to a new key
422currency_not_supportedThe intersection of the merchant's settings and the provider's capabilities doesn't include the currency; for disbursements: the currency isn't enabled for the merchant, has no available disbursement channel, or has no disbursement limits or disbursement rate configuredUse an enabled currency or contact platform Operations
409disbursement_no_conflictThe same merchant disbursement ID is submitted again with a different idempotency keyRetrieve the disbursement that related_resource_id points to; this ID has already been paid or is being paid, so don't resend it under a new ID
422fee_exceeds_amountThe amount is too small, and the fee calculated from the rate exceeds it; for disbursements, the amount isn't greater than the fee calculated from the disbursement rateIncrease the amount or contact the platform to adjust the configuration
422amount_out_of_rangeBelow the minimum or above the maximum amount per payment, or over the daily cumulative limit; for disbursements, below the per-disbursement minimum, above the per-disbursement maximum, or over the daily amount or daily count; the error doesn't return the specific thresholdsAdjust the amount or contact the platform
503temporarily_unavailableThe platform has paused new transactions on this channel, or paused disbursements; the response doesn't give the internal reasonRetry later with the original idempotency key; this request created no Payment or charge; if it keeps happening, contact platform Operations
422risk_rejectedThe request matched a basic rule such as a risk list, which disbursements are checked against too; the specific rule and threshold aren't disclosedDon't try to work around it; contact the platform if you have questions
422insufficient_balanceThe settleable balance in the currency is less than the disbursement amount plus the disbursement feemessage and error.details (shortfall, currency) give the shortfall; this request creates no disbursement and holds no funds; once the settleable balance is enough, submit again with the same merchant disbursement ID and a new idempotency key; the platform never reduces the amount for you
401unauthenticatedCommonNo Authorization header was sent; the key was copied incompletely; the old key expired after a rotation; the key was revokedCheck your credentials: on the API Key page in the merchant portal, verify the key's status and environment
403forbiddenCommonCreating a refund with a read-only key; creating or retrieving a disbursement with a key that lacks the disbursement scope; calling getEvent without the read scope for the event's business area; if the request's source IP isn't on the key's allowlist, the more specific ip_not_allowed is returnedUse a key that has the required scope; don't work around the restriction
429rate_limitedCommonA rate limit per merchant and per key; switching keys doesn't get around the merchant's overall limit; disbursement creation is counted separately per merchantWait for Retry-After seconds, then retry with the original idempotency key
500internal_errorCommon—Retry with the original idempotency key, or query by merchant order ID / refund ID / merchant disbursement ID; never charge again with a new key
502 / 503upstream_unavailableCommonA provider or platform dependency failed, and the platform can't confirm whether this request took effect; not the same as the disbursement failure code upstream_unavailable, despite the nameSame as above

Notes

  • The platform must enable the disbursements capability for the current environment. Otherwise the API returns 403 capability_disabled. A disbursement pays a third-party payee from your settleable balance on your instruction. It isn't a payout, and it isn't a withdrawal.
  • merchant_disbursement_no is unique within the merchant and environment. Resubmitting the same ID with a different idempotency key returns 409 disbursement_no_conflict (related_resource_id is the existing disbursement ID), and no second disbursement is created.
  • The request has no merchant ID, fee or channel fields: the API key determines the merchant and environment, and the platform calculates the fee. PKR amounts must be multiples of 100.
  • On acceptance, the amount plus fee is held. If the settleable balance falls short, the API returns 422 insufficient_balance (error.details carries shortfall and currency) and creates nothing. Going over a limit returns 422 amount_out_of_range without the threshold.
  • 201 and 200 only mean the disbursement was accepted (status=processing). Whether the money was paid depends on status or the disbursement.* events. There's no cancel endpoint.
  • For the acceptance order and payee rules, see Disbursements: errors and rate limits and payee rules.

Warning

After a timeout or 5xx, only retry with the original idempotency key or query by merchant disbursement ID. Never resend with a new ID or a new key: money that has been paid out can't be pulled back.

Was this page helpful?