Skip to main content
POST
Ingest a POS sale
Submits a sale transaction, optionally linked to an existing booking via bookingId. The request is validated immediately; matching and storing the sale happens moments later, so a 202 confirms the payload was accepted, not that the sale is stored yet.
  • Either tables or bookingId must be provided.
  • closedAt is required, and must be after openedAt, when recordType is Final.
  • transactionType: Refund requires originalSaleId; Sale/Void must not set it.
  • Each item’s total must equal unitPrice × quantity − discount.
  • When payments is included, the amounts must sum to total (which excludes tip/surcharge — those are separate fields).
  • source.other is required when source.type is Other.
  • Final — the sale is closed/settled. Resubmitting the same partnerSaleId is a no-op.
  • Incremental — the record accumulates onto an existing partnerSaleId: amounts add up, and items/payments append.
  • Tally — a full running snapshot of a still-open sale, resent periodically as it changes. The first submission for a partnerSaleId inserts it; each subsequent resend is diffed against the stored record and only the changed fields are applied.
After a sale is stored, NowBookIt tries to match it to a booking — by bookingId if you sent one, otherwise by table name and close time. On a match the sale is enriched with the booking’s service, table sections, and diner details. See List Sales for how the outcome is reported.

Example

Response — 202 Accepted

Authorizations

Authorization
string
header
required

Your API key. See Authorization to create one.

Body

application/json

Canonical POS sale payload. Cross-field rules enforced server-side:

• source.other is required when source.type is Other. • originalSaleId is required (and must differ from partnerSaleId) when transactionType is Refund; forbidden for Sale/Void. • total must be non-negative. • closedAt must be strictly after openedAt, and is required when recordType is Final. • At least one of tables or bookingId is required. • Each item's total must reconcile. • When payments is present, the amounts must sum to total.

Explicit JSON null on any field (including nested) is treated as absent.

partnerSaleId
string
required

POS-generated sale ID. Idempotency key alongside partner/venue.

Minimum string length: 1
source
object
required

Origin of the sale. other is required when type is Other.

transactionType
enum<string>
required

Case-insensitive; normalised to canonical casing.

Available options:
Sale,
Refund,
Void
openedAt
string<date-time>
required

Any UTC offset accepted; normalised to UTC on write.

staffId
string
required

Required — every sale must be attributable to a staff member.

Minimum string length: 1
total
integer
required

Goods + tax only. Excludes tip and surcharges, which are separate additive fields. Must be >= 0.

currency
string
required

Three-letter currency code, e.g. AUD or USD.

Required string length: 3
Example:

"AUD"

originalSaleId
string

Required when transactionType is Refund; must not be set otherwise.

posLocationId
string

The venue or location ID as known to your POS system.

recordType
enum<string>
default:Final

Case-insensitive. Final is the closed/settled sale — resubmitting the same partnerSaleId is a no-op. Incremental accumulates onto an existing record — amounts add, items/payments append. Tally is a full running snapshot of a still-open sale; the first submission for a partnerSaleId inserts it, and each resend is diffed against the stored record so only the changed fields are applied (a delta update, not a full replace).

Available options:
Final,
Tally,
Incremental
posStatus
string

Raw POS status string, passed through as-is.

bookingId
string

NBI booking ID. Blank/whitespace normalises to absent. Either this or tables is required.

closedAt
string<date-time>

Required when recordType is Final. Must be strictly after openedAt.

tables
(string | object)[]

Either this or bookingId is required. Section info is never accepted here — only reconciliation populates per-table section.

A plain table name, or an object carrying its own seats count. Blank names are dropped.

Example:

"12"

covers
integer

Number of guests at the table.

Required range: x >= 1
register
object
staffName
string

Name of the staff member who processed the sale.

diner
object

id is the NBI-resolved diner id (maps to the stored dinerId, defaults to unknown when blank/absent). partnerDinerId is the partner's own external customer id, kept separate from id.

items
object[]

The food, drinks, and other items included in the sale.

payments
object[]

The payments made toward the sale.

subtotal
integer

Amount in whole cents (no decimals).

tax
integer

Amount in whole cents (no decimals).

discount
integer

Amount in whole cents (no decimals).

surcharges
integer

Amount in whole cents (no decimals).

tip
integer

Amount in whole cents (no decimals).

Response

Validated and enqueued.

Confirms the request was validated and enqueued — not that the sale was accepted as new. Actual ingestion (duplicate/accumulate/reject decision) happens asynchronously; the caller is never told the outcome synchronously.

accepted
boolean
required

Whether the sale was accepted for processing.

Example:

true

partnerSaleId
string
required

The sale ID you submitted, echoed back for reference.