Merchant API

Create a payout intent

Create an idempotent Payout and read its single closed public status.

Create one Payout to a bank account. The response exposes one stable public status. Merchant API creation starts execution without a separate Merchant approval command.

Endpoint

POST /payout_intents
curl -s -X POST "$API_BASE/payout_intents" \
  -u "$API_PUBLIC_KEY:$API_SECRET_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "50000",
    "currency": "MYR",
    "country": "MY",
    "merchant_reference": "payout_98765",
    "recipient": {
      "type": "bank_account",
      "account_number": "1234567890",
      "bank_code": "MBBEMYKL",
      "holder_name": "Alice Tan"
    }
  }'

The API validates the request and resolves Operational Configuration before dispatch. After durable insert and acknowledged Workflow dispatch, it returns 201 with the latest Payout projection. An uncertain dispatched create remains processing; it never invites a second Payout.

{
  "object": "payout_intent",
  "id": "word_01HZX...",
  "amount": "50000",
  "currency": "MYR",
  "country": "MY",
  "environment": "test",
  "merchant_reference": "payout_98765",
  "status": "processing",
  "reserved_amount": "0",
  "completed_amount": "0",
  "returned_amount": "0",
  "next_action": null,
  "failure_code": null,
  "failure_message": null,
  "recipient": {
    "type": "bank_account",
    "account_last4": "7890",
    "bank_code": "MBBEMYKL",
    "holder_name": "Alice Tan"
  },
  "created_at": "2026-05-09T12:00:00.000Z",
  "updated_at": "2026-05-09T12:00:00.000Z"
}

status is one of processing, requires_action, requires_approval, succeeded, failed, cancelled, or expired. approval_status is present only as pending while operational approval is pending. Resolve an operational approval hold through an authorised Operator. Internal execution and Provider states are not public fields.

Replay and status

merchant_reference is a unique business reference. Reusing it returns 409 duplicate_merchant_reference. Idempotency-Key is required. Reusing the same key with the same complete request replays the stored response; changing any field returns 409 idempotency_conflict.

GET /payout_intents/:id is side-effect-free and never initiates Provider work. Consume webhooks or poll this projection for later truth.

Errors

Every error uses the error envelope with request_id. The usual refusals for Payout create are:

StatusCodeWhat to do
400validation_failedFix the fields in issues, then send with a new Idempotency-Key.
422country_not_enabledAsk your Operator to enable the country.
422insufficient_available_balanceTop up, then retry with the same key.
422amount_below_minimum, amount_above_maximumChange the amount.
422outside_operating_hoursSend again inside the operating window, with a new key.
422payout_configuration_unavailableContact your Operator. reason gives the cause.
409duplicate_merchant_reference, idempotency_conflictReconcile with the existing Payout.
503internal_errorWait for Retry-After, then retry with the same key and body.

Recipient bank codes

For Thailand, recipient.bank_code uses a canonical code: BAAC, BAY, BBL, CIMB, CLICX, EXIM, GHB, GSB, ISLAMIC, KBANK, KKP, KTB, LHBANK, SCB, SCBT, SME, TCR, TISCO, TTB, or UOB. Use promptpay for a PromptPay destination and put the recipient identifier in account_number. A recognised bank code does not guarantee an eligible route. Do not send Provider-local codes such as kkb. account_number and holder_name are required; holder_name_th supplies the Thai name when required by the configured route. For other countries, use the bank code agreed for the configured country and route.

Status events

Payout webhooks expose data.status with the same public values as GET. The payout_intent.returned event reports returned funds; its resource status is failed. It does not introduce a separate returned public status. Merchant approval commands are not available through this API; an operational approval hold must be resolved by an authorised Operator.

If Workflow dispatch cannot be acknowledged, creation returns 503. Retry with the same Idempotency-Key and the same body. Init reuses the stored Payout and dispatch identity; it does not create another Payout. A 503 is not proof that the Provider did not receive a request.

On this page