Skip to content

Recording Rules

How to represent common POS situations in POST /trade-records.

Amount identities

Case Identity
Tax added on top (prices_include_tax = false) total = subtotal − discount + tax + service_charge + tip + rounding
Tax included (prices_include_tax = true) total = subtotal − discount + service_charge + tip + rounding

Records that do not satisfy the identity are still accepted and reported in the response warnings.

Combos and modifiers

Situation How to record it
Combo One line for the combo itself (product_type = COMBO, combo price). One child line for every item in combo_components (including the main item), with parent_line_no pointing to the combo line and unit_price = "0.00".
Modifier with extra charge Put the charge in the line's modifiers[].price_delta and include it in unit_price. Example: Fries 4.49 + Cheese Powder 0.75 → unit_price = "5.24".
Combo drink upgrade The combo line's unit_price includes the upgrade, and its modifiers record the choice. The drink child line carries the drink actually served, with unit_price = "0.00".

Item quantities

Combo child lines count toward item quantities but not toward item revenue — the revenue sits on the combo line.

Example: combo with drink upgrade

Master Jimbo Combo (14.49), upgraded to Taiwan Boba Tea (+3.50) → combo line unit_price = "17.99".

[
  { "line_no": 1, "parent_line_no": null, "product_id": "COMBO-02", "product_name": "Master Jimbo Combo", "product_type": "COMBO",
    "quantity": "1", "unit_price": "17.99", "discount_amount": "0.00", "line_amount": "17.99", "tax_category": "STANDARD",
    "modifiers": [
      { "group_id": "FLAVOR_JIMBO", "option_id": "SALT_PEPPER", "option_name": "Taiwanese Salt & Pepper", "quantity": "1", "price_delta": "0.00" },
      { "group_id": "COMBO_DRINK",  "option_id": "DRK-BOBA",    "option_name": "Taiwan Boba Tea",          "quantity": "1", "price_delta": "3.50" }
    ], "is_voided": false },
  { "line_no": 2, "parent_line_no": 1, "product_id": "SGL-JIMBO",  "product_name": "Master Jimbo",    "product_type": "SINGLE",
    "quantity": "1", "unit_price": "0.00", "discount_amount": "0.00", "line_amount": "0.00", "tax_category": "STANDARD", "modifiers": [], "is_voided": false },
  { "line_no": 3, "parent_line_no": 1, "product_id": "SIDE-FRIES", "product_name": "Fries",           "product_type": "SIDE",
    "quantity": "1", "unit_price": "0.00", "discount_amount": "0.00", "line_amount": "0.00", "tax_category": "STANDARD", "modifiers": [], "is_voided": false },
  { "line_no": 4, "parent_line_no": 1, "product_id": "DRK-BOBA",   "product_name": "Taiwan Boba Tea", "product_type": "DRINK",
    "quantity": "1", "unit_price": "0.00", "discount_amount": "0.00", "line_amount": "0.00", "tax_category": "STANDARD", "modifiers": [], "is_voided": false }
]

Exchanges (EXCHANGE)

The customer brings back a purchased item and swaps it for another in a single transaction, paying or receiving only the difference. Because one transaction contains both returned (negative) and new (positive) lines, the transaction type's amount_sign is null.

  • Returned item: negative quantity and line_amount.
  • New item: positive quantity and line_amount.
  • original_trade_record_id points to the original transaction.
  • Difference: collect it with payment_type = SALE if the new item costs more; give it back with REFUND if it costs less; leave payments empty if equal.

Example: Fries (4.49) exchanged for Cheese Corn (5.49), collecting 1.00 before tax.

"items": [
  { "line_no": 1, "parent_line_no": null, "product_id": "SIDE-FRIES",       "product_name": "Fries",       "product_type": "SIDE",
    "quantity": "-1", "unit_price": "4.49", "discount_amount": "0.00", "line_amount": "-4.49", "tax_category": "STANDARD", "modifiers": [], "is_voided": false },
  { "line_no": 2, "parent_line_no": null, "product_id": "SIDE-CHEESE-CORN", "product_name": "Cheese Corn", "product_type": "SIDE",
    "quantity": "1",  "unit_price": "5.49", "discount_amount": "0.00", "line_amount": "5.49",  "tax_category": "STANDARD", "modifiers": [], "is_voided": false }
]

Tip

If your POS has no exchange function, send one RETURN transaction and one SALE transaction instead. HQ reports come out the same.

Refunds and changes to closed days

  • Refunds keep the business_date of the original transaction. Record them with payments[].payment_type = REFUND and original_trade_record_id.
  • Signs — in RETURN and REFUND transactions, item quantity and line_amount and the transaction amounts (subtotal_amount, discount_amount, tax_amount, total_amount, …) are negative. In an EXCHANGE, only the returned lines are negative. Payment amount stays positive; payment_type = REFUND gives the direction. This way the daily totals are simple sums.
  • Changing a transaction — resend the whole transaction with version + 1. The highest version wins; with equal versions, the later last_modified_at wins.
  • Changing a business date that is already closed — resend the changed transaction (version + 1) and resend that day's daily close with updated control totals and a new batch_id.

order_type vs order_source

order_type describes how the order is fulfilled (dine-in, to go, pick-up, delivery). order_source describes where the order was placed. Record both:

Situation order_type order_source order_source_detail
Ordered at the counter, eaten in DINE_IN POS —
Ordered at a kiosk, taken away TO_GO KIOSK —
Uber Eats delivery DELIVERY DELIVERY_PLATFORM UBER_EATS
Ordered on the website, picked up in store PICK_UP WEB —

order_source is a suggested list: values outside it are accepted. Send null (or omit it) when unknown — it is not assumed to be POS.

payment_type vs payment_method

payment_type describes what the money is for (sale, refund, deposit…). payment_method describes what was used to pay (cash, card, points…).

Situation payment_type payment_method payment_method_detail
Pay by VISA SALE CREDIT_CARD VISA
Pay with loyalty points SALE LOYALTY_POINTS e.g. POINTS:500
Pay with a gift card SALE GIFT_CARD —
Cash deposit for a cake order DEPOSIT CASH —
Deposit applied at pick-up DEPOSIT_APPLIED CASH (same instrument as the deposit) —
Return refunded to credit card REFUND CREDIT_CARD VISA
  • Payment amount is always positive; the direction comes from payment_type (only ADJUSTMENT may be negative).
  • payment_type values outside the suggested list are accepted, but HQ cannot tell their direction automatically and will review them manually. Tell HQ the meaning and direction of any custom value in advance.

Members

  • member_id is optional. Leave it null or omit it for non-members.
  • It must be your loyalty system's internal ID — never a phone number, email address or national ID. If your system is keyed by phone number, send an internal sequence number or a salted hash.