Biklo for developersTESTsandbox-api.biklo.comchange

Wallet and payouts

From a buyer paying to money in the seller's bank.

First: can this seller be paid at all?#

Check before you build a withdrawal button, not after.

curl https://sandbox-api.biklo.com/v1/wallet/account -H "Authorization: Bearer $KEY"

can_withdraw: false means payout onboarding is unfinished, and you cannot fix that from here — Stripe collects identity documents in a browser flow. next_step tells you what to tell your user. See Setting up your sandbox payout account.

The same verdict is on GET /v1/me under payouts, from your very first call, so you find out on day one rather than at the first withdrawal.

Money is always minor units plus a currency#

There is no single balance. A seller trading cross-border holds several at once, so GET /v1/wallet/balance returns a list:

{
  "data": [
    {
      "currency": "RON",
      "pending": 123400,
      "available": 0,
      "blocked": 0,
      "paid_out": 0,
      "refunded": 0,
      "debt": 0,
      "under_review": 0
    }
  ],
  "can_withdraw": true
}

Every figure is an integer of the smallest unit — 123400 is 1,234.00 RON. Never a float, never a bare number without the currency beside it.

What the buckets mean:

Bucket Meaning
pending Order money still travelling: accepted, shipping, or inside the buyer's return window
available Cleared. This is what a withdrawal can move
blocked Cleared, but held because payout onboarding is incomplete
paid_out Already sent to the bank
refunded Went back to the buyer
settled_to_fees Terminal: the company's absorbed fees consumed the whole order, so nothing was transferred and nothing refunded
debt Negative: a refund landed after the seller had been paid
under_review We could not classify it automatically and a person is looking

If blocked is non-zero the balance response carries an onboarding block explaining why — that money is real and the seller cannot touch it, so it should never be a mystery.

Where the money is, order by order#

GET /v1/wallet/lots is one row per order. Each carries balance_bucket, so you can reconcile a lot against the balance totals without learning our internal statuses.

Reconcile against net_transferred_amount, not amount. When the company absorbs fees, the surcharge is withheld at transfer, so the money that actually reached the seller is smaller than the lot. Sizing anything on the gross figure is how a payout gets requested for money that was never there.

GET /v1/wallet/ledger is the append-only trail: every movement, in order, with the lot and order it belongs to. This is what you reconcile against, not the balance — balances are a current view, the ledger is the history.

Withdrawing#

curl -X POST https://sandbox-api.biklo.com/v1/wallet/withdrawals \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"amount": 50000, "currency": "RON"}'

Send both or neither. An amount without a currency is ambiguous the moment a seller holds two, so it is refused rather than guessed. With neither, everything available is withdrawn per currency.

The response is honest about what happened:

status Meaning
payout_created A payout is on its way to the bank
funds_settling Money was released and is settling at Stripe. Not a failure
released_settling Lots were released to the connected account this call
payout_failed Stripe refused. Check stripe_errors

funds_settling is the one to get right in your UI. Nothing is wrong; the money is simply not at the bank yet.

The currency trap#

payout_failed is almost always a bank account that cannot receive the balance currency. Stripe pays a balance out to an account in that currency. A RON balance with a EUR-only IBAN fails, and it fails quietly.

GET /v1/wallet/account lists the bank accounts Stripe holds against the balances they can serve, so you can detect this before attempting a withdrawal.

Hearing about it#

Register the payout.updated webhook rather than polling. It fires on the transitions that answer "has the money moved":

event Meaning
released Moved to the seller's connected account
payout_requested A payout was created
paid_out It arrived
blocked_pending_onboarding Cleared but held — the seller must finish onboarding
debt Negative balance recorded

blocked_pending_onboarding is the one worth wiring to a notification: it is the only signal that a human has to go and do something in a browser.

Testing the whole path#

Stripe test-mode payouts are real payouts — created, then pending until Stripe settles them, which takes days. That is correct and useless in a demo, so:

curl -X POST https://sandbox-api.biklo.com/v1/sandbox/withdrawals/advance \
  -H "Authorization: Bearer $KEY"

This skips the waiting and nothing else. Every payout in the sandbox is a real test-mode payout against a real test-mode connected account; only the settlement clock is short-circuited.

A complete run:

  1. POST /v1/sandbox/orders — an order to earn from
  2. POST /v1/sandbox/orders/{id}/advance with delivered
  3. POST /v1/sandbox/orders/{id}/advance with settled — closes the return window
  4. GET /v1/wallet/balance — the money is now available
  5. POST /v1/wallet/withdrawals
  6. POST /v1/sandbox/withdrawals/advance — skip the settlement wait
  7. GET /v1/wallet/withdrawals — status: paid