Pay-at-Table
Pay-at-Table is not required for a baseline POS integration with Mozrest. POS systems that implement the two endpoints on this page automatically unlock Mozrest's Pay-at-Table partner products (e.g. Blitz) for every venue connected through them — partners settle bills directly from the guest's seat. Implementations that do not expose these endpoints still ship a fully working order-flow integration; their venues simply stay out of the Pay-at-Table partner scope.
How it fits
Mozrest sits between Pay-at-Table partners and your POS:
Pay-at-Table partner ──> Mozrest ──> Your POS
(e.g. Blitz) Mozrest endpoints on this page
Pay-at-Table API
The partner discovers their venues and the live bill through Mozrest's Pay-at-Table partner API; Mozrest calls the endpoints below to fetch the bill from your POS and apply the payment back to it. The partner never talks to your POS directly.
Two endpoints make up the contract:
- Get Bill — return the current bill for the open order at a table.
- Apply Payment — record a partial or full payment against an open order, including tip. A
fullpayment that brings the balance to zero closes the order automatically (no separateclosecall).
Get Bill
Return the current bill for the open order at a table. Mozrest calls this whenever a partner asks "what does this table owe right now?".
GET /venue/{venueId}/tables/{tableId}/bill
Authorization: Bearer {api_key}
Response
{
"orderId": "98765",
"status": "open",
"tables": ["T12"],
"partySize": 2,
"totals": {
"subtotal": 130.00,
"tax": 12.00,
"tip": 0.00,
"discount": 0.00,
"total": 142.00,
"paid": 50.00,
"balanceDue": 92.00
},
"checks": [
{
"checkNumber": "232323233",
"status": "open",
"totalAmount": 130.00,
"tax": 12.00,
"tip": 0.00,
"discount": 0.00,
"paymentMethod": null,
"items": [
{ "id": 1, "name": "Steak", "price": 40.00, "quantity": 2 },
{ "id": 2, "name": "Wine", "price": 50.00, "quantity": 1 }
]
}
]
}
| Field | Type | Description |
|---|---|---|
orderId | String | POS-side order id of the open order on this table. |
status | String | Order status — open while a bill exists. |
tables | Array | POS table ids the order is seated on. |
partySize | Integer | Number of guests. |
totals.subtotal | Number | Sum of item amounts across checks, before tax / tip / discount. |
totals.tax | Number | Total tax on the bill. |
totals.tip | Number | Tip rung onto the bill at the POS (gratuity already on the check, not collected via Pay-at-Table). |
totals.discount | Number | Total discount applied. |
totals.total | Number | subtotal + tax + tip − discount. |
totals.paid | Number | Aggregate amount of all Pay-at-Table payments already applied to this order. Tip collected via Pay-at-Table is recorded on the payment but is not counted in paid. |
totals.balanceDue | Number | total − paid — what the partner is allowed to charge next. |
checks | Array | Per-check breakdown. Same shape as List Orders. |
No open bill
404 Not Found
{ "error": { "code": 404, "message": "No open order on table T12" } }
Return 404 when there is no open order on the table. Mozrest surfaces this to the partner as "nothing to pay".
Apply Payment
Record a partial or full Pay-at-Table payment against an open order. The amount, tip and currency reflect what the partner just collected from the guest; the POS applies it as a tender on the order.
POST /venue/{venueId}/orders/{orderId}/payments
Authorization: Bearer {api_key}
Content-Type: application/json
Payload
| Field | Type | Required | Description |
|---|---|---|---|
externalReference | String | Yes | Idempotency key chosen by the partner, unique per order. See Idempotency. |
type | String | Yes | full or partial. A full payment that brings balanceDue to zero closes the order — see Auto-close on full payment. |
amount | Number | Yes | Amount paid in currency, excluding tip. Must be > 0 and <= balanceDue. |
tip | Number | No | Gratuity collected on top of amount. Defaults to 0. Stored against the payment; does not reduce balanceDue. |
currency | String | Yes | ISO 4217 code (e.g. EUR, GBP, USD). |
paymentMethod | String | No | Free-text label describing how the guest paid. Recommended vocabulary: card, cash, voucher, gift_card, other. POSes that map onto a fixed tender list should choose the closest match. |
reference | String | No | Human-readable label for the POS UI / receipt (e.g. Pay-at-Table — Blitz). Optional display hint. |
Example
{
"externalReference": "blitz-tx-a7c93e",
"type": "full",
"amount": 92.00,
"tip": 10.00,
"currency": "EUR",
"paymentMethod": "card",
"reference": "Pay-at-Table — Blitz"
}
Response
{
"paymentId": "POS-PAY-7782",
"status": "accepted",
"order": {
"orderId": "98765",
"status": "closed",
"totals": {
"paid": 142.00,
"balanceDue": 0.00
}
}
}
| Field | Type | Description |
|---|---|---|
paymentId | String | POS-side identifier for the recorded tender. Stored by Mozrest for reconciliation. |
status | String | Always accepted on 2xx. |
order.orderId | String | Echoed for trace correlation. |
order.status | String | open if balance remains, closed if this payment cleared the order. |
order.totals.paid | Number | Cumulative amount paid against the order, including this call. |
order.totals.balanceDue | Number | Remaining balance after this payment. |
Auto-close on full payment
When a payment with type: "full" brings balanceDue to zero, the POS must close the order in the same transaction:
- Set the order status to
closed. - Set every open check on the order to
closed. - Record the tender so the POS UI reflects the order as settled.
Mozrest does not send a separate close call for Pay-at-Table — accepting the payment is the close signal. This matches how Oracle Simphony, Sapaad and other tender-driven POSes already behave.
A payment with type: "partial" keeps the order open regardless of the resulting balance. Sending partial with an amount that would zero the balance is a business-rule error (422).
Idempotency
Apply Payment is the only write call on this page. Replay protection is keyed on externalReference per order:
- Same
externalReferenceand identical payload → return200 OKwith the original response body and headerX-Idempotent-Replay: true. Do not record a second tender. - Same
externalReferencebut a different payload → return409 Conflict.
This lets Mozrest retry safely on network failure: replay the exact same body, get the original outcome.
Errors
| Code | When | Notes |
|---|---|---|
200 | Payment accepted (including idempotent replay) | Returns the post-payment state of the order. |
400 | Malformed payload | Missing required field, wrong type. |
401 | Bad / missing token | Standard auth error. |
404 | Unknown order, or order already closed | Same response whether the order never existed or has been closed — Mozrest surfaces both as "nothing to pay". |
409 | Idempotency conflict | Same externalReference, different body. |
422 | Business-rule violation | amount > balanceDue, amount <= 0, unknown / mismatched currency, or type: "partial" that would zero the balance. Include error.details describing the offending field. |
Error response format
Use the same shape as the rest of the POS API (API Basics):
{
"error": {
"code": 422,
"message": "Amount exceeds balance due",
"details": {
"amount": "Must be <= 42.50"
}
}
}
Discovery
POSes do not advertise Pay-at-Table support themselves. Mozrest registers the capability per-POS during onboarding (after the endpoints on this page are accredited end-to-end) and surfaces it to partners per venue through the Pay-at-Table partner API — partners check that response before exposing the flow to the guest.