Payouts

A payout is the merchant's withdrawal of accumulated revenue from their Storlaunch balance to a bank account they control. Storlaunch supports two backing modes:

  1. Local manual payouts — merchant requests a payout, operator marks it in-transit, then paid. Used on Forjio's bookkeeping flow.
  2. Plugipay-backed payouts — once the merchant enables the Plugipay module, every payout endpoint here transparently proxies through to the Plugipay payouts API, which handles Xendit disbursement under the hood.

Storlaunch decides which backend to use per-request based on whether the payment module is active for the workspace.

All endpoints require an sk_* key.

Endpoints

Bank account

Method Path Purpose
GET /v1/payouts/bank-account Read the configured bank account
PATCH /v1/payouts/bank-account Update the bank account

Balance

Method Path Purpose
GET /v1/payouts/balance Available balance (settled revenue minus paid-out + pending)

Payouts

Method Path Purpose
GET /v1/payouts List payouts
GET /v1/payouts/:id Retrieve a payout
POST /v1/payouts Request a new payout
POST /v1/payouts/:id/cancel Cancel a pending payout
POST /v1/payouts/:id/mark-in-transit Mark in-transit (manual mode only)
POST /v1/payouts/:id/mark-paid Mark paid (manual mode only)
POST /v1/payouts/:id/mark-failed Mark failed (manual mode only)

Bank account

GET /v1/payouts/bank-account
PATCH /v1/payouts/bank-account

PATCH body

Field Type Required Description
bankCode string (≤32) no Bank code recognised by the disbursement provider (bca, mandiri, bni, ...). Required in Plugipay mode.
bankName string (1–100) yes Human-readable bank name.
bankAccountNumber string (1–50) yes The account number. We don't validate — Xendit does at disbursement time.
bankAccountHolder string (1–100) yes Account holder's legal name.

Response200 OK. Returns the configured bank account.

Balance

GET /v1/payouts/balance
{
  "data": {
    "available": 5230000,
    "pending": 750000,
    "currency": "IDR"
  },
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}
  • available — settled, withdrawable.
  • pending — in payment-settlement (waiting for the provider to release funds, typically T+1 to T+3).

List / retrieve

GET /v1/payouts?status=…&limit=50&cursor=…
GET /v1/payouts/:id

Standard cursor pagination. Filter by status: pending, in_transit, paid, failed, cancelled.

Create

POST /v1/payouts

Request body

Field Type Required Description
amount integer yes Smallest-unit. Must be > 0 and <= available.
currency string (3) yes ISO 4217. Must match the balance currency.
bankCode string no Override the configured bank code.
bankName / bankAccountNumber / bankAccountHolder string no One-off override of the configured account (rare).
note string (≤500) no Free text.

Response201 Created. Returns the new payout row.

Errors specific to this endpoint

Status error.code When
409 INSUFFICIENT_BALANCE amount > available.
400 VALIDATION_ERROR Missing required field or wrong shape.
409 BANK_ACCOUNT_NOT_SET No bank account configured and no one-off override supplied.
curl -X POST https://storlaunch.forjio.com/api/v1/payouts \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"amount":5000000,"currency":"IDR","note":"April revenue"}'

Cancel

POST /v1/payouts/:id/cancel

Only valid while the payout is pending. Cancellation returns the amount to available immediately.

Status transitions (manual mode only)

POST /v1/payouts/:id/mark-in-transit
POST /v1/payouts/:id/mark-paid
POST /v1/payouts/:id/mark-failed

These exist for workspaces on the manual payouts backend, where a human operator is the disbursement layer. In Plugipay mode they're rejected with 409 — status comes from the disbursement provider's webhook instead.

Body Required Description
reference (in-transit / paid) no Bank transfer reference number.
failureReason (failed) yes Free-text reason (Wrong account number, Customer suspected fraud, ...).

The payout object

Field Type Nullable Description
id string no pout_ + 26-char ULID.
accountId string no The workspace.
amount integer no Smallest-unit.
currency string no ISO 4217.
status enum no pending / in_transit / paid / failed / cancelled.
bankCode string yes Bank code at issue time.
bankName / bankAccountNumber / bankAccountHolder string no Snapshot of the bank account at request time.
reference string yes Provider-side reference. Set when in_transit or paid.
failureReason string yes Why it failed.
note string yes Merchant note from creation.
requestedAt string no Creation time.
inTransitAt / paidAt / failedAt / cancelledAt string yes Lifecycle stamps.

Events

Payout lifecycle events come from Plugipay when the payment module is active — subscribe to:

  • payout.initiated
  • payout.paid
  • payout.failed

on your Plugipay endpoint, not Storlaunch's. The Storlaunch outbox does not mirror these events.

In manual mode, no public webhook events fire — poll the API.

Next

  • Ledger — per-order entries that build up the available balance.
  • Modules — enable the Plugipay module to switch from manual to disbursed payouts.
  • Billing — Storlaunch's own subscription — settles via Plugipay, not via this payout flow.