Customer Account: Balance & Loyalty Points¶
Every customer (api/v1/customers) maintains two separate, permanent accounts:
- Prepaid balance (
accountBalance) — a real-money balance that the customer pays in advance and then uses as a means of payment for purchases. Can be paid out. - Loyalty points / bonus (
loyaltyPoints/bonusBalance) — points/bonus that are credited and redeemed with purchases. Cannot be paid out (the bonus is forfeited when the balance is paid out).
This page corrects an older inaccuracy in the REST API overview: there is no
POST /customers/{id}/credit and no GET /customers/{id}/transactions — these routes do not
exist in the code. The actual routes are documented below.
Endpoints¶
CustomersController — base api/v1/customers:
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/{id}/account-transactions |
Balance/account movements (prepaid ledger) |
GET |
/{id}/point-transactions |
Loyalty point movements |
POST |
/{id}/payout |
Pay out remaining balance (only accountBalance, not bonus) |
POST |
/{id}/settle |
Settle an open balance (e.g. a Disco IOU) via a payment |
GET |
/card/{cardId} |
Resolve a customer by customer card |
GET |
/{id}/invoice-summary |
The customer's invoice/subscription overview |
Both transaction endpoints optionally accept dateFrom/dateTo as query parameters to narrow
the period.
Account Movements (account-transactions)¶
GET /api/v1/customers/cus_a1b2c3/account-transactions?dateFrom=2026-07-01&dateTo=2026-07-31
Authorization: Bearer eyJ...
{
"success": true,
"data": [
{
"id": "cat_001",
"customerId": "cus_a1b2c3",
"customerName": "Max Mustermann",
"receiptId": "rec_9f8e7d",
"type": 0,
"amount": 50.00,
"balanceAfter": 50.00,
"bonusBalanceAfter": 2.50,
"description": "Balance top-up: Balance 50€ x 1",
"personalId": "usr_kasse1",
"transactionDate": "2026-07-05T18:22:00Z"
}
]
}
type is an int without its own public enum schema; values observed in practice: 0 top-up/
deposit, 1 use (payment with balance), 2 correction (manual void adjustment), 3 bonus
credit, 4 bonus use/forfeiture, 5 payout.
Point Movements (point-transactions)¶
{
"success": true,
"data": [
{
"id": "cpt_001",
"customerId": "cus_a1b2c3",
"customerName": "Max Mustermann",
"receiptId": "rec_9f8e7d",
"articleId": "art_cola",
"articleName": "Cola 0,3l",
"type": 0,
"points": 3.5,
"pointsAfter": 42.5,
"description": "Points for sale",
"transactionDate": "2026-07-05T18:22:00Z"
}
]
}
type: 0 = credited (Earned), 1 = redeemed (Redeemed), 2 = correction (Correction).
Paying Out (payout)¶
Pays out balance from accountBalance (not from the bonus — which is forfeited in the
process).
POST /api/v1/customers/cus_a1b2c3/payout
Authorization: Bearer eyJ...
Content-Type: application/json
{ "amount": 20.00 }
Error case (insufficient balance) — 400 with errorCode: "INSUFFICIENT_BALANCE".
Settling a Balance (settle)¶
For an open (negative) balance — e.g. a Disco IOU (an uncovered remaining amount on leaving, see Disco API) — the customer pays in, and the balance moves toward zero:
POST /api/v1/customers/cus_a1b2c3/settle
Authorization: Bearer eyJ...
Content-Type: application/json
{ "amount": 15.00, "method": "EC" }
method is free-form (default "Bar") and only ends up in the description of the posting —
there is no link to a payment terminal.
Topping Up Balance — the Real Way¶
There is no dedicated /credit endpoint. Instead, a prepaid balance is topped up exactly
like any other sale: via the normal payment endpoints (POST /api/v1/payments/direct or
POST /api/v1/payments/table), with two conditions:
- The article sold has
extraOption: 1(KundenAufladung) — a "top-up article" (e.g. "Balance 50 €"), created via the normal article API (POST /api/v1/articles). - The payment is linked to the customer via
customerId.
The article price (× quantity) is then automatically credited to the customer's accountBalance
— in addition to the actual payment processing (the guest pays the top-up amount completely
normally via payments[], e.g. cash or card). If a top-up bonus is configured on the article
(cardUploadPercent/cardUploadAmount), a bonus is automatically credited on top.
POST /api/v1/payments/direct
Authorization: Bearer eyJ...
Idempotency-Key: 6f1c2a9e-3b7d-4e21-9b0a-1f2e3d4c5b6a
Content-Type: application/json
{
"items": [ { "articleId": "art_aufladung50", "quantity": 1 } ],
"payments": [ { "method": "Cash", "amount": 50.00 } ],
"customerId": "cus_a1b2c3"
}
The top-up then appears as a type: 0 entry in account-transactions (see above), linked via
receiptId to the resulting POS receipt.
Flow¶
- Create a customer —
POST /api/v1/customers(minimum:name/customerNumber). - Top up balance — sell a
KundenAufladungarticle withcustomerId(POST /api/v1/payments/direct, see above). - Book to the account — later purchases by the customer with
payments[].method = "CustomerAccount"deduct fromaccountBalance(charged, not topped up). - Check —
GET /{id}/account-transactionsreviews the history. - Points — are credited/redeemed in the normal sales process, viewable via
GET /{id}/point-transactions. - Pay out/settle —
POST /{id}/payout(return remaining balance) orPOST /{id}/settle(settle an open balance/IOU).
Distinction: Customer Account Balance ↔ Disco Card Balance¶
Two different concepts that are easily confused:
| Customer Account Balance | Disco Card Balance | |
|---|---|---|
| Endpoint | payments/* + customers/* |
POST /api/v1/disco/guest/{cardId}/topup |
| Binding | to the customer record, permanent | to the physical card, for one visit |
| Payout | POST /{id}/payout |
remaining balance at checkout (disco/guest/{cardId}/checkout) |
→ Details on the card variant: Disco API (Cashless Club).