Settlements
List settlements, view settlement summary totals, and look up a settlement by order or redemption.
Settlements track payouts owed to the merchant for completed sales or
redemptions, depending on the merchant's settlement mode. All three
endpoints require an authenticated merchant session with the
portal.reports permission, checked inline via assertMerchantPermission
(not requireBffMerchantAuth).
Response shape differs from every other sub-area
These three endpoints do not use the { "ok": true, "data": { ... } }
envelope. Successful responses put fields directly at the top level
alongside "ok": true (for example { "ok": true, "rows": [...], "total": 0 }).
Error responses from the inline auth check use
{ "ok": false, "error": { "message": "...", "code": "..." } } (401/403),
but a few hand-written validation errors on these routes use a bare
{ "error": "..." } with no ok field at all (for example the 400 on
settlements/by-source below). Treat each response shape as documented
per endpoint rather than assuming the shared convention applies here.
Filtered by settlement mode, same as Analytics
Like the Sales/Redemptions analytics endpoints, results here are filtered by
the merchant's settlementMode: SETTLEMENT_ON_SALE merchants only see
settlements sourced from orders, SETTLEMENT_ON_REDEMPTION merchants only
see settlements sourced from redemptions. A merchant with no mode set sees
both. See Analytics for the mode
reference.
List settlements
GET /api/bff/settlementsQuery parameters
| Param | Type | Default | Notes |
|---|---|---|---|
take | number | 50 | Capped at 200. |
skip | number | 0 | Floored at 0. |
Response
{
"ok": true,
"rows": [
{
"id": "...",
"merchantId": "...",
"sourceType": "ORDER",
"sourceId": "...",
"orderId": "...",
"giftCardId": null,
"redemptionId": null,
"grossAmountMinor": 2500,
"feeAmountMinor": 125,
"netAmountMinor": 2375,
"currency": "EUR",
"feeSnapshot": {},
"status": "PENDING",
"payoutBatchId": null,
"eligibleAt": "2026-06-26T00:00:00.000Z",
"createdAt": "2026-06-26T00:00:00.000Z",
"updatedAt": "2026-06-26T00:00:00.000Z",
"statusLabel": "Pending payout"
}
],
"total": 1
}Rows are the raw MerchantSettlement record plus a statusLabel field.
status is one of PENDING, IN_BATCH, PAID, VOID; VOID rows are
always excluded. statusLabel maps these to "Pending payout",
"In progress", "Paid", "Void" respectively. sourceType is ORDER or
REDEMPTION. Ordered by eligibleAt descending.
Errors
| Status | Cause |
|---|---|
| 401 | No session, or session user has no associated merchant. |
| 403 | Session user's role lacks the portal.reports permission. |
Settlement summary
GET /api/bff/settlements/summaryAggregate totals for the current calendar month (UTC server time), used for a dashboard summary card.
Response
{
"ok": true,
"pendingNetMinor": 0,
"paidThisPeriodNetMinor": 0,
"unsettledCount": 0,
"currency": "EUR"
}pendingNetMinor: sum ofnetAmountMinorfor settlements with statusPENDINGorIN_BATCH(not limited to the current period).paidThisPeriodNetMinor: sum ofnetAmountMinorfor settlements with statusPAIDandeligibleAton or after the 1st of the current month.unsettledCount: count of settlements with statusPENDINGorIN_BATCH.currency: the merchant'sdefaultCurrency, falling back toEURif unset.
Errors
Same as List settlements.
Look up a settlement by order or redemption
GET /api/bff/settlements/by-sourceNaming note
Despite the path, this is not a "grouped by source type" report. It looks up a single settlement by a specific order or redemption ID.
Query parameters
Exactly one of the following is required:
| Param | Type |
|---|---|
orderId | string |
redemptionId | string |
If both are supplied, orderId takes precedence.
Response
If a matching, non-void settlement exists:
{
"ok": true,
"settlement": {
"id": "...",
"status": "PENDING",
"netAmountMinor": 2375,
"currency": "EUR",
"statusLabel": "Pending payout"
}
}If none exists:
{ "ok": true, "settlement": null }Errors
| Status | Cause |
|---|---|
| 401 | No session, or session user has no associated merchant. |
| 403 | Session user's role lacks the portal.reports permission. |
| 400 | Neither orderId nor redemptionId was supplied. Response is a bare { "error": "orderId or redemptionId required" }, with no ok field. |