curl --request POST \
--url https://platform.uat.nowbookit.com/api/sales/transactions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"partnerSaleId": "pos-sale-9981",
"source": {
"type": "Pos"
},
"transactionType": "Sale",
"recordType": "Final",
"openedAt": "2026-07-23T19:05:00Z",
"closedAt": "2026-07-23T20:45:00Z",
"bookingId": "abc-123",
"tables": [
{
"name": "12",
"seats": 4
}
],
"staffId": "staff-7",
"items": [
{
"id": "item-1",
"name": "Wagyu Steak",
"category": "food",
"quantity": 2,
"unitPrice": 6500,
"unitTax": 591,
"total": 13000
}
],
"payments": [
{
"id": "pay-1",
"type": "EFTPOS",
"amount": 18500
}
],
"total": 18500,
"currency": "AUD"
}
'import requests
url = "https://platform.uat.nowbookit.com/api/sales/transactions"
payload = {
"partnerSaleId": "pos-sale-9981",
"source": { "type": "Pos" },
"transactionType": "Sale",
"recordType": "Final",
"openedAt": "2026-07-23T19:05:00Z",
"closedAt": "2026-07-23T20:45:00Z",
"bookingId": "abc-123",
"tables": [
{
"name": "12",
"seats": 4
}
],
"staffId": "staff-7",
"items": [
{
"id": "item-1",
"name": "Wagyu Steak",
"category": "food",
"quantity": 2,
"unitPrice": 6500,
"unitTax": 591,
"total": 13000
}
],
"payments": [
{
"id": "pay-1",
"type": "EFTPOS",
"amount": 18500
}
],
"total": 18500,
"currency": "AUD"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
partnerSaleId: 'pos-sale-9981',
source: {type: 'Pos'},
transactionType: 'Sale',
recordType: 'Final',
openedAt: '2026-07-23T19:05:00Z',
closedAt: '2026-07-23T20:45:00Z',
bookingId: 'abc-123',
tables: [{name: '12', seats: 4}],
staffId: 'staff-7',
items: [
{
id: 'item-1',
name: 'Wagyu Steak',
category: 'food',
quantity: 2,
unitPrice: 6500,
unitTax: 591,
total: 13000
}
],
payments: [{id: 'pay-1', type: 'EFTPOS', amount: 18500}],
total: 18500,
currency: 'AUD'
})
};
fetch('https://platform.uat.nowbookit.com/api/sales/transactions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://platform.uat.nowbookit.com/api/sales/transactions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'partnerSaleId' => 'pos-sale-9981',
'source' => [
'type' => 'Pos'
],
'transactionType' => 'Sale',
'recordType' => 'Final',
'openedAt' => '2026-07-23T19:05:00Z',
'closedAt' => '2026-07-23T20:45:00Z',
'bookingId' => 'abc-123',
'tables' => [
[
'name' => '12',
'seats' => 4
]
],
'staffId' => 'staff-7',
'items' => [
[
'id' => 'item-1',
'name' => 'Wagyu Steak',
'category' => 'food',
'quantity' => 2,
'unitPrice' => 6500,
'unitTax' => 591,
'total' => 13000
]
],
'payments' => [
[
'id' => 'pay-1',
'type' => 'EFTPOS',
'amount' => 18500
]
],
'total' => 18500,
'currency' => 'AUD'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://platform.uat.nowbookit.com/api/sales/transactions"
payload := strings.NewReader("{\n \"partnerSaleId\": \"pos-sale-9981\",\n \"source\": {\n \"type\": \"Pos\"\n },\n \"transactionType\": \"Sale\",\n \"recordType\": \"Final\",\n \"openedAt\": \"2026-07-23T19:05:00Z\",\n \"closedAt\": \"2026-07-23T20:45:00Z\",\n \"bookingId\": \"abc-123\",\n \"tables\": [\n {\n \"name\": \"12\",\n \"seats\": 4\n }\n ],\n \"staffId\": \"staff-7\",\n \"items\": [\n {\n \"id\": \"item-1\",\n \"name\": \"Wagyu Steak\",\n \"category\": \"food\",\n \"quantity\": 2,\n \"unitPrice\": 6500,\n \"unitTax\": 591,\n \"total\": 13000\n }\n ],\n \"payments\": [\n {\n \"id\": \"pay-1\",\n \"type\": \"EFTPOS\",\n \"amount\": 18500\n }\n ],\n \"total\": 18500,\n \"currency\": \"AUD\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://platform.uat.nowbookit.com/api/sales/transactions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"partnerSaleId\": \"pos-sale-9981\",\n \"source\": {\n \"type\": \"Pos\"\n },\n \"transactionType\": \"Sale\",\n \"recordType\": \"Final\",\n \"openedAt\": \"2026-07-23T19:05:00Z\",\n \"closedAt\": \"2026-07-23T20:45:00Z\",\n \"bookingId\": \"abc-123\",\n \"tables\": [\n {\n \"name\": \"12\",\n \"seats\": 4\n }\n ],\n \"staffId\": \"staff-7\",\n \"items\": [\n {\n \"id\": \"item-1\",\n \"name\": \"Wagyu Steak\",\n \"category\": \"food\",\n \"quantity\": 2,\n \"unitPrice\": 6500,\n \"unitTax\": 591,\n \"total\": 13000\n }\n ],\n \"payments\": [\n {\n \"id\": \"pay-1\",\n \"type\": \"EFTPOS\",\n \"amount\": 18500\n }\n ],\n \"total\": 18500,\n \"currency\": \"AUD\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://platform.uat.nowbookit.com/api/sales/transactions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"partnerSaleId\": \"pos-sale-9981\",\n \"source\": {\n \"type\": \"Pos\"\n },\n \"transactionType\": \"Sale\",\n \"recordType\": \"Final\",\n \"openedAt\": \"2026-07-23T19:05:00Z\",\n \"closedAt\": \"2026-07-23T20:45:00Z\",\n \"bookingId\": \"abc-123\",\n \"tables\": [\n {\n \"name\": \"12\",\n \"seats\": 4\n }\n ],\n \"staffId\": \"staff-7\",\n \"items\": [\n {\n \"id\": \"item-1\",\n \"name\": \"Wagyu Steak\",\n \"category\": \"food\",\n \"quantity\": 2,\n \"unitPrice\": 6500,\n \"unitTax\": 591,\n \"total\": 13000\n }\n ],\n \"payments\": [\n {\n \"id\": \"pay-1\",\n \"type\": \"EFTPOS\",\n \"amount\": 18500\n }\n ],\n \"total\": 18500,\n \"currency\": \"AUD\"\n}"
response = http.request(request)
puts response.read_body{
"accepted": true,
"partnerSaleId": "<string>"
}Ingest Sale
Submit a POS sale transaction in NowBookIt’s canonical shape.
curl --request POST \
--url https://platform.uat.nowbookit.com/api/sales/transactions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"partnerSaleId": "pos-sale-9981",
"source": {
"type": "Pos"
},
"transactionType": "Sale",
"recordType": "Final",
"openedAt": "2026-07-23T19:05:00Z",
"closedAt": "2026-07-23T20:45:00Z",
"bookingId": "abc-123",
"tables": [
{
"name": "12",
"seats": 4
}
],
"staffId": "staff-7",
"items": [
{
"id": "item-1",
"name": "Wagyu Steak",
"category": "food",
"quantity": 2,
"unitPrice": 6500,
"unitTax": 591,
"total": 13000
}
],
"payments": [
{
"id": "pay-1",
"type": "EFTPOS",
"amount": 18500
}
],
"total": 18500,
"currency": "AUD"
}
'import requests
url = "https://platform.uat.nowbookit.com/api/sales/transactions"
payload = {
"partnerSaleId": "pos-sale-9981",
"source": { "type": "Pos" },
"transactionType": "Sale",
"recordType": "Final",
"openedAt": "2026-07-23T19:05:00Z",
"closedAt": "2026-07-23T20:45:00Z",
"bookingId": "abc-123",
"tables": [
{
"name": "12",
"seats": 4
}
],
"staffId": "staff-7",
"items": [
{
"id": "item-1",
"name": "Wagyu Steak",
"category": "food",
"quantity": 2,
"unitPrice": 6500,
"unitTax": 591,
"total": 13000
}
],
"payments": [
{
"id": "pay-1",
"type": "EFTPOS",
"amount": 18500
}
],
"total": 18500,
"currency": "AUD"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
partnerSaleId: 'pos-sale-9981',
source: {type: 'Pos'},
transactionType: 'Sale',
recordType: 'Final',
openedAt: '2026-07-23T19:05:00Z',
closedAt: '2026-07-23T20:45:00Z',
bookingId: 'abc-123',
tables: [{name: '12', seats: 4}],
staffId: 'staff-7',
items: [
{
id: 'item-1',
name: 'Wagyu Steak',
category: 'food',
quantity: 2,
unitPrice: 6500,
unitTax: 591,
total: 13000
}
],
payments: [{id: 'pay-1', type: 'EFTPOS', amount: 18500}],
total: 18500,
currency: 'AUD'
})
};
fetch('https://platform.uat.nowbookit.com/api/sales/transactions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://platform.uat.nowbookit.com/api/sales/transactions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'partnerSaleId' => 'pos-sale-9981',
'source' => [
'type' => 'Pos'
],
'transactionType' => 'Sale',
'recordType' => 'Final',
'openedAt' => '2026-07-23T19:05:00Z',
'closedAt' => '2026-07-23T20:45:00Z',
'bookingId' => 'abc-123',
'tables' => [
[
'name' => '12',
'seats' => 4
]
],
'staffId' => 'staff-7',
'items' => [
[
'id' => 'item-1',
'name' => 'Wagyu Steak',
'category' => 'food',
'quantity' => 2,
'unitPrice' => 6500,
'unitTax' => 591,
'total' => 13000
]
],
'payments' => [
[
'id' => 'pay-1',
'type' => 'EFTPOS',
'amount' => 18500
]
],
'total' => 18500,
'currency' => 'AUD'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://platform.uat.nowbookit.com/api/sales/transactions"
payload := strings.NewReader("{\n \"partnerSaleId\": \"pos-sale-9981\",\n \"source\": {\n \"type\": \"Pos\"\n },\n \"transactionType\": \"Sale\",\n \"recordType\": \"Final\",\n \"openedAt\": \"2026-07-23T19:05:00Z\",\n \"closedAt\": \"2026-07-23T20:45:00Z\",\n \"bookingId\": \"abc-123\",\n \"tables\": [\n {\n \"name\": \"12\",\n \"seats\": 4\n }\n ],\n \"staffId\": \"staff-7\",\n \"items\": [\n {\n \"id\": \"item-1\",\n \"name\": \"Wagyu Steak\",\n \"category\": \"food\",\n \"quantity\": 2,\n \"unitPrice\": 6500,\n \"unitTax\": 591,\n \"total\": 13000\n }\n ],\n \"payments\": [\n {\n \"id\": \"pay-1\",\n \"type\": \"EFTPOS\",\n \"amount\": 18500\n }\n ],\n \"total\": 18500,\n \"currency\": \"AUD\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://platform.uat.nowbookit.com/api/sales/transactions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"partnerSaleId\": \"pos-sale-9981\",\n \"source\": {\n \"type\": \"Pos\"\n },\n \"transactionType\": \"Sale\",\n \"recordType\": \"Final\",\n \"openedAt\": \"2026-07-23T19:05:00Z\",\n \"closedAt\": \"2026-07-23T20:45:00Z\",\n \"bookingId\": \"abc-123\",\n \"tables\": [\n {\n \"name\": \"12\",\n \"seats\": 4\n }\n ],\n \"staffId\": \"staff-7\",\n \"items\": [\n {\n \"id\": \"item-1\",\n \"name\": \"Wagyu Steak\",\n \"category\": \"food\",\n \"quantity\": 2,\n \"unitPrice\": 6500,\n \"unitTax\": 591,\n \"total\": 13000\n }\n ],\n \"payments\": [\n {\n \"id\": \"pay-1\",\n \"type\": \"EFTPOS\",\n \"amount\": 18500\n }\n ],\n \"total\": 18500,\n \"currency\": \"AUD\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://platform.uat.nowbookit.com/api/sales/transactions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"partnerSaleId\": \"pos-sale-9981\",\n \"source\": {\n \"type\": \"Pos\"\n },\n \"transactionType\": \"Sale\",\n \"recordType\": \"Final\",\n \"openedAt\": \"2026-07-23T19:05:00Z\",\n \"closedAt\": \"2026-07-23T20:45:00Z\",\n \"bookingId\": \"abc-123\",\n \"tables\": [\n {\n \"name\": \"12\",\n \"seats\": 4\n }\n ],\n \"staffId\": \"staff-7\",\n \"items\": [\n {\n \"id\": \"item-1\",\n \"name\": \"Wagyu Steak\",\n \"category\": \"food\",\n \"quantity\": 2,\n \"unitPrice\": 6500,\n \"unitTax\": 591,\n \"total\": 13000\n }\n ],\n \"payments\": [\n {\n \"id\": \"pay-1\",\n \"type\": \"EFTPOS\",\n \"amount\": 18500\n }\n ],\n \"total\": 18500,\n \"currency\": \"AUD\"\n}"
response = http.request(request)
puts response.read_body{
"accepted": true,
"partnerSaleId": "<string>"
}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.
Validation rules
Validation rules
- Either
tablesorbookingIdmust be provided. closedAtis required, and must be afteropenedAt, whenrecordTypeisFinal.transactionType: RefundrequiresoriginalSaleId;Sale/Voidmust not set it.- Each item’s
totalmust equalunitPrice × quantity − discount. - When
paymentsis included, the amounts must sum tototal(which excludestip/surcharge— those are separate fields). source.otheris required whensource.typeisOther.
recordType: Final, Tally, or Incremental
recordType: Final, Tally, or Incremental
Final— the sale is closed/settled. Resubmitting the samepartnerSaleIdis a no-op.Incremental— the record accumulates onto an existingpartnerSaleId: 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 apartnerSaleIdinserts it; each subsequent resend is diffed against the stored record and only the changed fields are applied.
Booking reconciliation
Booking reconciliation
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
curl --location 'https://platform.uat.nowbookit.com/api/sales/transactions' \
--header 'Authorization: Bearer your_api_key_here' \
--header 'Content-Type: application/json' \
--data '{
"partnerSaleId": "pos-sale-9981",
"source": { "type": "Pos" },
"transactionType": "Sale",
"openedAt": "2026-07-23T19:05:00Z",
"closedAt": "2026-07-23T20:45:00Z",
"bookingId": "abc-123",
"tables": [{ "name": "12", "seats": 4 }],
"staffId": "staff-7",
"items": [
{
"id": "item-1",
"name": "Wagyu Steak",
"quantity": 2,
"unitPrice": 6500,
"unitTax": 591,
"total": 13000
}
],
"payments": [{ "id": "pay-1", "type": "EFTPOS", "amount": 18500 }],
"total": 18500,
"currency": "AUD"
}'
{
"accepted": true,
"partnerSaleId": "pos-sale-9981"
}
Authorizations
Your API key. See Authorization to create one.
Body
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.
POS-generated sale ID. Idempotency key alongside partner/venue.
1Origin of the sale. other is required when type is Other.
Show child attributes
Show child attributes
Case-insensitive; normalised to canonical casing.
Sale, Refund, Void Any UTC offset accepted; normalised to UTC on write.
Required — every sale must be attributable to a staff member.
1Goods + tax only. Excludes tip and surcharges, which are separate additive fields. Must be >= 0.
Three-letter currency code, e.g. AUD or USD.
3"AUD"
Required when transactionType is Refund; must not be set otherwise.
The venue or location ID as known to your POS system.
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).
Final, Tally, Incremental Raw POS status string, passed through as-is.
NBI booking ID. Blank/whitespace normalises to absent. Either this or tables is required.
Required when recordType is Final. Must be strictly after openedAt.
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.
"12"
Number of guests at the table.
x >= 1Show child attributes
Show child attributes
Name of the staff member who processed the sale.
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.
Show child attributes
Show child attributes
The food, drinks, and other items included in the sale.
Show child attributes
Show child attributes
The payments made toward the sale.
Show child attributes
Show child attributes
Amount in whole cents (no decimals).
Amount in whole cents (no decimals).
Amount in whole cents (no decimals).
Amount in whole cents (no decimals).
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.