Refunds
- 2 endpoints
A refund is either requested by the customer (the hosted refund page, signed by the wallet that paid) and approved or rejected by the merchant, or sent by the merchant without a request; either way the merchant signs the transfer from the wallet that was paid, in the dashboard (non-custodial: QBitFlow never holds the money). There is no in-between state on the merchant's side: its own refund is recorded only once its transfer confirmed (approved); before that, nothing is stored and nothing blocks the customer. The SDK surface is GET /refunds and GET /refunds/:uuid (read only).
List refunds#
GET /refunds
A page of the refunds of the request's space and, from the organization's space, of its members by default (includeMembers; userUuid: one member's), newest first, with any status unless filtered (?status=pending for those to answer; repeat it for several). Each names its member (userUuid), whether the organization decides it (held), and, once sent, where its transfer stands (approval). A member needs the members.refunds policy (403 policy_disabled).
- Auth
- X-API-Key: any keyA member's key needs the members.refunds policy
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl https://api.qbitflow.app/v2/refunds \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a page of Refund ({ items, hasMore, nextCursor? })// The active ones: pending, waiting for an answer (no status: every status).const page = await client.refunds.list({ status: [RefundStatus.Pending] });for (const refund of page.items) { const { uuid, txUuid, initiatedBy, reason, amount, currency } = refund; console.log(`${uuid} of ${txUuid} (${initiatedBy}): ${amount} ${currency.symbol}, ${reason}`);}# The ones waiting for an answer (status None: every status).page = client. refunds. list( status=[qbitflow. RefundStatus. PENDING])for refund in page.items: print(refund.uuid, refund.tx_uuid, refund.initiated_by, refund.reason, refund.amount)// The active ones: pending, waiting for an answer (Status empty: every status).page, err := client.Refunds.List(ctx, &qbitflow.RefundListParams{ Status: []qbitflow. RefundStatus{qbitflow. RefundStatusPending},})if err != nil { log.Fatal(err)}for _, r := range page.Items { fmt.Println(r.UUID, r.TxUUID, r.InitiatedBy, r.Reason, r.Amount, r.Currency.Symbol)}// The active ones: pending, waiting for an answer (no status: every status).$page = $client->refunds->list(new \QBitFlow\Params\RefundListParams( status: [\QBitFlow\Enums\RefundStatus::PENDING],));foreach ($page->items as $refund) { echo "{$refund->uuid} {$refund->txUuid} ({$refund->initiatedBy}): {$refund->amount} {$refund->currency->symbol}, " . ($refund->reason ?? '-') . "\n";}Query parameters
limitintegerOptionalPage size: 20 when absent or ≤ 0, at most 100 (a larger one is clamped, not refused)
cursorstringOptionalThe previous page's nextCursor, verbatim; omit for the first page
max=200includeMembersbooleanOptionalThe organization's space only (the team, its keys without On-Behalf-Of): its members' refunds too, each naming its member (userUuid), the ones it decides marked held. Default: true from the organization's space, false from a member's (true there → 400)
userUuidstringOptionalThe organization's space only: this member's refunds only (their personal space in the request's mode; another organization's user → 404). includeMembers doesn't apply
heldbooleanOptionalOnly the refunds of payments held for a member (true: the ones the organization decides and sends), or only the others (false)
statusarray of stringOptionalOnly the refunds in one of these statuses: repeat the parameter for several (?status=approved&status=rejected). Absent: every status
max=5oneof=pending approved rejected failedOne of
pendingapprovedrejectedfailedDeclared, never set: an approval that fails leaves the refund pending, to approve again
txUuidstringOptionalOnly the refund of this transaction: a payment (pay@…) or a bill (sub-hist@…), or its bare uuid (either). A transaction that can't be refunded (a subscription, a refund) matches none
txuuid
Response
A page: { "items": Refund[], "hasMore": …, "nextCursor"?: … }. Pass nextCursor back as cursor for the next page; it is absent when hasMore is false.
27 fields of each Refund
uuidstringThe refund's id (refund@<uuid>)
A prefixed id (pay@…, sub@…)
txUuidstringThe refunded transaction: a payment (pay@<uuid>) or a subscription's bill (sub-hist@<uuid>)
reasonstringOptionalWhy the refund: the customer's reason, or the merchant's when it started the refund (absent when it gave none). Only for the merchant: never in the hosted reads (GET /hosted/transactions/:txUuid/refund, the answer of POST /hosted/refunds)
Only for the resource's owner
statusstringpending (a customer's request not answered yet), approved (its transfer confirmed), rejected (the merchant rejected the request). failed is declared but never set: a failed approval leaves the request pending
One of
pendingapprovedrejectedfailedDeclared, never set: an approval that fails leaves the refund pending, to approve again
createdAtstringWhen the refund was requested (or prepared by the merchant: its intent)
merchantMessagestringOptionalThe merchant's message to the customer: why it rejected the request, or a note on a refund it started. Absent when none
respondedAtstringOptionalWhen the refund was answered: rejected, or its transfer confirmed (approved). Absent while pending
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
txHashstringOptionalThe refund transfer's transaction, once approved. Absent before
initiatedBystringWho started the refund: customer (requested by the customer, the merchant answers) or merchant (sent by the merchant without a request: recorded once its transfer confirmed, approved; see POST /refunds/intent)
One of
customermerchantheldbooleanOptionalHeld: the refunded payment was paid to the organization's wallet, which holds the seller's funds (the trust layer): the organization decides the refund and sends it from its wallet; the seller (userUuid) sees it. Once sent, it is a negative line of the seller's held funds. False: the seller (the refund's space) decides it.
Only for the resource's owner
paidstringWhat the customer paid for the refunded transaction: its amount, fees included, and the network fee they paid on top. The base of refundPercent: a full refund sends all of it back. paid and paidMinUnits in the units of currency, paidUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (paidMinUnits / 10^decimals), a decimal string
paidMinUnitsstringIn the currency's smallest unit, a string of digits
paidUsdstringOptionalIn USD, a decimal string; absent when unknown
refundPercentnumberThe share of what the customer paid that the refund sends back, a percent string (100: all of it). A customer requests 100; the merchant sets it when it approves, or when it starts the refund. Written as refundPercent
amountstringWhat the refund sends back: refundPercent of what the customer paid, rounded down to a whole min unit (all of it for a customer's request, the merchant's share for its own refund, and the share sent once approved). amount in the currency's units, amountUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (amountMinUnits / 10^decimals), a decimal string
amountMinUnitsstringIn the currency's smallest unit, a string of digits
amountUsdstringOptionalIn USD, a decimal string; absent when unknown
currencyIdintegerThe currency paid, and sent back: written as the currency object under currency (GET /currencies/:id)
settlementTransferSettlementOptionalThe refund transfer's transaction (its block, what it cost), once approved; absent before
Only for the resource's owner
1 field of
TransferSettlementtransactionSettlementTransactionOptionalThe transfer's transaction. Absent before it was confirmed
6 fields of
SettlementTransactionblockNumberstringThe block (Ethereum, Base) or slot (Solana) it landed in, a string of digits
blockTimestringOptionalWhen its block was produced (UTC); when it was recorded if the chain's node didn't give it. Absent only on records made before it was recorded
gasUsedintegerOptionalThe gas it consumed (Ethereum, Base). Absent on Solana
feeTokenAmountWhat the transaction cost its sender, in the chain's native coin (ETH, SOL; its currency is the payment currency's chain): its gas and, on Base, its L1 data fee. amountUsd at nativePriceUsd
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
l1FeeTokenAmountOptionalThe part of fee that paid Base's L1 data fee, in the native coin. Absent off Base, and on transactions recorded before it was
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
nativePriceUsdstringOptionalNativePriceUSD is the USD price of the chain's native coin when the transaction was recorded (what its fee cost in USD). Absent when the price wasn't available then
chainstringOptionalThe chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)
explorerUrlstringOptionalIts transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash
approvalApprovalOptionalApproval is where the refund transfer stands while the request is pending and it was sent (POST /refunds/:uuid/approve, or the merchant's own refund answering it: POST /refunds): being confirmed, or its last attempt failed. Absent before it is sent, and once the refund is answered (respondedAt). Only for the merchant, in its reads (GET /refunds, GET /refunds/:uuid, GET /hosted/transactions/:txUuid/refund); never in webhooks
Only for the resource's owner
3 fields of
ApprovalstatusstringwaitingConfirmation: sent, being confirmed on-chain. created: its last attempt failed (lastAttempt): send it again (POST /refunds/:uuid/approve/intent)
One of:
createdWaiting for the customer: nothing sent yet, or the last attempt failed (lastAttempt)waitingConfirmationA transaction was sent: waiting for the network and its recordcompletedConfirmed and recorded. FinalexpiredThe checkout expired unpaid (at its expiresAt, or expired by its merchant). Final
txHashstringOptionalThe transfer being confirmed, once known
lastAttemptAttemptOptionalWith created: why the last attempt failed (its code and message)
5 fields of
Attemptstatusstringfailed
One of:
failedThe attempt's transaction wasn't sent, didn't confirm or failed on the network: nothing was paid
codestringOptionalThe error code (errors): what to do about it (insufficient_funds, token_not_approved, tx_not_sent…)
messagestringOptionalThe reason, for the customer
txHashstringOptionalThe attempt's transaction, if it was sent
atstringWhen it failed
customerUuidstringOptionalCustomerUUID is the refunded transaction's customer (a refund has none of its own); absent without one. Only for the merchant, in its reads (never in webhooks)
Only for the resource's owner
productNamestringOptionalWhat the refunded transaction paid for (its product's name, as on the payment or bill). Only for the merchant, in its reads (never in webhooks)
Only for the resource's owner
customerSummaryOptionalThe refunded transaction's customer, named (even once deleted); absent without one. Only for the merchant, in its reads (never in webhooks)
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
An item of the response
{ "amount": "10.0042", "amountMinUnits": "10004200", "amountUsd": "10.0042", "chain": "BASE", "createdAt": "2026-10-01T12:00:00Z", "currency": { "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "chain": "BASE", "code": "BASE:USDC", "decimals": 6, "id": 8, "mainCurrencyId": 3, "name": "USD Coin", "symbol": "USDC", "test": false }, "held": false, "initiatedBy": "customer", "paid": "10.0042", "paidMinUnits": "10004200", "paidUsd": "10.0042", "reason": "I was charged twice for the same order", "refundPercent": "100", "status": "pending", "test": false, "txUuid": "pay@01a0f755-f200-7000-8000-000000000001", "uuid": "refund@01a0ec3b-3ba0-7000-8000-000000000006"}Show all 29 linesShow less
Errors
- 403policy_
disabled a member doing what their organization's policy doesn't allow them, reads of what the policy governs included
Also the common codes, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Get refund#
GET /refunds/
A refund of the request's space or, from the organization's space, of one of its members (as GET /refunds lists them). Another organization's, another mode's or another member's → 404. A member needs the members.refunds policy (403 policy_disabled).
- Auth
- X-API-Key: any keyA member's key needs the members.refunds policy
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl "https://api.qbitflow.app/v2/refunds/$REFUND_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a Refundconst refund = await client.refunds.get(refundUuid); // refund@…// pending (a customer's request), approved or rejected.const { uuid, status, refundPercent, amount, currency } = refund;console.log(`${uuid}: ${status}, ${refundPercent}% = ${amount} ${currency.symbol}`);refund = client.refunds.get(refund_uuid) # refund@…# pending (a customer's request), approved or rejected.print(refund.uuid, refund.status, f"{refund.refund_percent}%", refund.amount)refund, err := client.Refunds.Get(ctx, refundUUID) // refund@…if err != nil { log.Fatal(err)}// pending (a customer's request), approved or rejected.fmt.Println(refund.UUID, refund.Status, refund.RefundPercent+"%", refund.Amount, refund.Currency.Symbol)$refund = $client->refunds->get( $refundUuid); // refund@…// pending (a customer's request), approved or rejected.echo "{$refund->uuid} {$refund->status} {$refund->refundPercent}% {$refund->amount} {$refund->currency->symbol}\n";Path parameters
uuidstringRequiredThe refund: refund@<uuid>, or its bare uuid
txuuid
Response
A Refund.
27 fields of Refund
uuidstringThe refund's id (refund@<uuid>)
A prefixed id (pay@…, sub@…)
txUuidstringThe refunded transaction: a payment (pay@<uuid>) or a subscription's bill (sub-hist@<uuid>)
reasonstringOptionalWhy the refund: the customer's reason, or the merchant's when it started the refund (absent when it gave none). Only for the merchant: never in the hosted reads (GET /hosted/transactions/:txUuid/refund, the answer of POST /hosted/refunds)
Only for the resource's owner
statusstringpending (a customer's request not answered yet), approved (its transfer confirmed), rejected (the merchant rejected the request). failed is declared but never set: a failed approval leaves the request pending
One of
pendingapprovedrejectedfailedDeclared, never set: an approval that fails leaves the refund pending, to approve again
createdAtstringWhen the refund was requested (or prepared by the merchant: its intent)
merchantMessagestringOptionalThe merchant's message to the customer: why it rejected the request, or a note on a refund it started. Absent when none
respondedAtstringOptionalWhen the refund was answered: rejected, or its transfer confirmed (approved). Absent while pending
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
txHashstringOptionalThe refund transfer's transaction, once approved. Absent before
initiatedBystringWho started the refund: customer (requested by the customer, the merchant answers) or merchant (sent by the merchant without a request: recorded once its transfer confirmed, approved; see POST /refunds/intent)
One of
customermerchantheldbooleanOptionalHeld: the refunded payment was paid to the organization's wallet, which holds the seller's funds (the trust layer): the organization decides the refund and sends it from its wallet; the seller (userUuid) sees it. Once sent, it is a negative line of the seller's held funds. False: the seller (the refund's space) decides it.
Only for the resource's owner
paidstringWhat the customer paid for the refunded transaction: its amount, fees included, and the network fee they paid on top. The base of refundPercent: a full refund sends all of it back. paid and paidMinUnits in the units of currency, paidUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (paidMinUnits / 10^decimals), a decimal string
paidMinUnitsstringIn the currency's smallest unit, a string of digits
paidUsdstringOptionalIn USD, a decimal string; absent when unknown
refundPercentnumberThe share of what the customer paid that the refund sends back, a percent string (100: all of it). A customer requests 100; the merchant sets it when it approves, or when it starts the refund. Written as refundPercent
amountstringWhat the refund sends back: refundPercent of what the customer paid, rounded down to a whole min unit (all of it for a customer's request, the merchant's share for its own refund, and the share sent once approved). amount in the currency's units, amountUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (amountMinUnits / 10^decimals), a decimal string
amountMinUnitsstringIn the currency's smallest unit, a string of digits
amountUsdstringOptionalIn USD, a decimal string; absent when unknown
currencyIdintegerThe currency paid, and sent back: written as the currency object under currency (GET /currencies/:id)
settlementTransferSettlementOptionalThe refund transfer's transaction (its block, what it cost), once approved; absent before
Only for the resource's owner
1 field of
TransferSettlementtransactionSettlementTransactionOptionalThe transfer's transaction. Absent before it was confirmed
6 fields of
SettlementTransactionblockNumberstringThe block (Ethereum, Base) or slot (Solana) it landed in, a string of digits
blockTimestringOptionalWhen its block was produced (UTC); when it was recorded if the chain's node didn't give it. Absent only on records made before it was recorded
gasUsedintegerOptionalThe gas it consumed (Ethereum, Base). Absent on Solana
feeTokenAmountWhat the transaction cost its sender, in the chain's native coin (ETH, SOL; its currency is the payment currency's chain): its gas and, on Base, its L1 data fee. amountUsd at nativePriceUsd
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
l1FeeTokenAmountOptionalThe part of fee that paid Base's L1 data fee, in the native coin. Absent off Base, and on transactions recorded before it was
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
nativePriceUsdstringOptionalNativePriceUSD is the USD price of the chain's native coin when the transaction was recorded (what its fee cost in USD). Absent when the price wasn't available then
chainstringOptionalThe chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)
explorerUrlstringOptionalIts transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash
approvalApprovalOptionalApproval is where the refund transfer stands while the request is pending and it was sent (POST /refunds/:uuid/approve, or the merchant's own refund answering it: POST /refunds): being confirmed, or its last attempt failed. Absent before it is sent, and once the refund is answered (respondedAt). Only for the merchant, in its reads (GET /refunds, GET /refunds/:uuid, GET /hosted/transactions/:txUuid/refund); never in webhooks
Only for the resource's owner
3 fields of
ApprovalstatusstringwaitingConfirmation: sent, being confirmed on-chain. created: its last attempt failed (lastAttempt): send it again (POST /refunds/:uuid/approve/intent)
One of:
createdWaiting for the customer: nothing sent yet, or the last attempt failed (lastAttempt)waitingConfirmationA transaction was sent: waiting for the network and its recordcompletedConfirmed and recorded. FinalexpiredThe checkout expired unpaid (at its expiresAt, or expired by its merchant). Final
txHashstringOptionalThe transfer being confirmed, once known
lastAttemptAttemptOptionalWith created: why the last attempt failed (its code and message)
5 fields of
Attemptstatusstringfailed
One of:
failedThe attempt's transaction wasn't sent, didn't confirm or failed on the network: nothing was paid
codestringOptionalThe error code (errors): what to do about it (insufficient_funds, token_not_approved, tx_not_sent…)
messagestringOptionalThe reason, for the customer
txHashstringOptionalThe attempt's transaction, if it was sent
atstringWhen it failed
customerUuidstringOptionalCustomerUUID is the refunded transaction's customer (a refund has none of its own); absent without one. Only for the merchant, in its reads (never in webhooks)
Only for the resource's owner
productNamestringOptionalWhat the refunded transaction paid for (its product's name, as on the payment or bill). Only for the merchant, in its reads (never in webhooks)
Only for the resource's owner
customerSummaryOptionalThe refunded transaction's customer, named (even once deleted); absent without one. Only for the merchant, in its reads (never in webhooks)
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
Errors
- 403policy_
disabled a member doing what their organization's policy doesn't allow them, reads of what the policy governs included
Also the common codes, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Edge cases
The contract's rules for these routes: Authentication & roles · The refund (Refund) · Reading refunds (GET /refunds, GET /) · What a refund pays back · Where a refund's transfer stands (approval, chain, explorerUrl) · Conflicts (409) · Webhooks.
Read all 7
Authentication & roles
- A member's refunds and decisions follow the organization's
members.refundspolicy (off →403policy_disabled, reads included:GET /refundsandGET /refunds/:uuid, with the member's session or their own key); the team always passes, acting in the member's space. A refund the member already holds stays the organization's to manage. GET /refundslists; aGETnever creates.
The refund (Refund)
uuidisrefund@<uuid>; every:uuidpath takes it or the bare uuid. The merchant's own refund's uuid is its transaction's (refund@<the payment's or bill's uuid>: one per transaction). Another kind of id (pay@…) isn't a refund →404; a value that isn't an id →400validation_failedonuuid.txUuidis the refunded transaction, always in lowercase: a one-time payment (pay@…) or one bill of a subscription (sub-hist@…, aBill'suuid).- Statuses:
pending(a customer's request not answered yet, or its approval being confirmed),approved(its transfer is confirmed on-chain:refund.approved),rejected(the merchant rejected the customer's request:refund.rejected). A merchant's own refund is only everapproved(initiatedBy: "merchant"): it is recorded once its transfer confirmed.failedis declared but never set: an approval whose transaction fails leaves the requestpending, and the merchant can approve it again. Clients with exhaustive switches handle all four. - Money (naming):
amount/amountMinUnits/amountUsdis what the refund sends back,paid/paidMinUnits/paidUsdwhat the customer paid (its base), in the refund'scurrency(the catalog's object: Currencies):amountandpaidin the token's units (exact decimal strings),…MinUnitsthe on-chain integers (strings of digits),…Usddecimal strings at the refunded transaction's time, absent when unknown (a request made before they were recorded), never"0".refundPercentis a percent string ("50"). - Absent until they have a value (never
null):reason(the merchant may give none),merchantMessage,respondedAt(set when the refund is answered: rejected, or its transfer confirmed),txHashandexplorerUrl(the refund transfer, once approved),settlement(itstransactiononly: block, time, what it cost),userUuid(the organization's own refund),approval(below),customer/customerUuid/productName(the refunded transaction's, merchant reads only). chain(ETH,BASE,SOL) is on every refund that names its currency.
Reading refunds (GET /refunds, GET /refunds/:uuid)
GET /refundsis one paginated list (pagination:limit20 by default, at most 100, opaquecursor), newest first on (createdAt,uuid), every status unless filtered:status(repeatable:?status=approved&status=rejected; any ofpending approved rejected failed; an unknown value →400validation_failedonstatus[0], the index of the bad value).?status=pendingis what is waiting for the merchant (the formerGET /transaction/refunds/all).txUuid(a prefixed id, any case, or the bare uuid of a payment or bill): the refund of that transaction (0 or 1 item). A subscription (sub@…) or a refund id matches none; a malformed value →400ontxUuid.held(true: the refunds the organization decides, of payments it held for its members;false: the others).includeMembers(the organization's space only): its members' refunds too, each naming its member (userUuid). Defaulttruefrom the organization's space (a marketplace decides its held sellers' refunds),falsefrom a member's;truefrom a member's space →400onincludeMembers.userUuid(the organization's space only, owners and admins): one member's refunds (a removed member's too); from a member's space →400onuserUuid; another organization's user →404. The cursor keeps no filter: pass the same query with it. A cursor the API didn't give →400oncursor.
GET /refunds/:uuidreads one refund the request may list: its space's, or, from the organization's space, one of its members' in the same mode. Another organization's, another mode's, the organization's own from a member's space, or another member's →404(existence is never revealed).- Both are completed like every read:
approval,customer,customerUuid,productName.
What a refund pays back
- The base is everything the customer paid for the transaction: its amount, QBitFlow's fee and the organization's fee (a marketplace's commission) included, plus the network fee the customer paid on top (
settlement.split.networkFeeof the payment or bill). It ispaidMinUnits/paid/paidUsd, read from what the transaction's confirmation recorded, never from the chain. - A refund sends back
refundPercentof that base (greater than 0, at most 100), rounded down to a whole smallest unit (and its USD value rounded down to 6 decimals): never more than the customer paid, and never nothing (a share that rounds to 0 →400onrefundPercent). At 100 %, the customer gets back exactly what their wallet sent. - Who bears the fees: the merchant. QBitFlow keeps its fee and the organization its commission (fee records are not reversed); the refund comes out of the wallet that was paid (the member's, or the organization's when it held the member's funds), which also pays the network fee of the refund's own transfer. A refund of a payment the organization holds for a member is a negative line of the member's held funds (Held funds).
- A customer's request is for everything (
refundPercent: "100"); the merchant chooses the share when approving. A merchant's own refund sends the share its intent prepared. A payment whose split wasn't recorded can't be refunded (500; the team repairs the record), never for 0.
Where a refund's transfer stands (approval, chain, explorerUrl)
- Between
POST /refunds/:uuid/approve(or a merchant's own refund answering the request) and its confirmation, the request stayspendingwithoutrespondedAt, like a request nobody answered.approvaltells them apart (merchant only, API reads, never in webhooks):- absent: nothing sent yet (only prepared, or not even that): it is the merchant's to sign;
{ "status": "waitingConfirmation", "txHash"? }: sent, being confirmed: don't send it again;{ "status": "created", "lastAttempt": { "code", "message", "txHash"?, "at" } }: the last attempt failed (code, errors): prepare and send it again. Once answered,respondedAtis set andapprovalis absent.
chainis on every refund;explorerUrl(the refund transfer on the chain's explorer, the testnet's in test mode) once it has atxHash.
Conflicts (409)
- One refund per transaction, whatever it became (pending, approved or rejected), whoever started it: a second one →
409refund_already_existswithdetails.refundUuid(the existing refund, or the merchant's refund being sent). A merchant's intent never sent is no refund. The transaction is matched whatever the case of its uuid. - A held payment already released to its member (or closed by a release that sent nothing: Held funds) can't be refunded: its money left the organization's wallet →
409held_funds_released, at the customer's request, at the merchant's intent, at the approval's intent and at the sends. - While a release of the member's held funds is being sent (however long its confirmation takes), the refund of one of its payments can't be prepared nor sent →
409held_funds_release_in_flight(once the release is confirmed, the payment is released:held_funds_released; once it failed, the refund can be approved).
Webhooks
refund.requested (a customer's request), refund.approved (the transfer confirmed: a request approved, or a merchant's refund sent) and refund.rejected (a request rejected), each with the Refund as its owner reads it, without approval, customer, customerUuid and productName (Webhook events). A merchant's refund is announced once, by refund.approved: there is no refund.requested for it.
The codes named here
- 400validation_
failed path/query/body failed binding or validation, or a business rule about one input (a field named in
errors) - 403policy_
disabled a member doing what their organization's policy doesn't allow them, reads of what the policy governs included