Skip to main content
Transaction data is what a till captures at the moment of sale: when it happened, in which store, what was bought at what price, how it was paid for, and which discounts applied. It is the input that unlocks:
  • Digital receipts
  • Real-time bonus (earn)
  • Challenges (formerly Stamp Cards)

The Purchase API is what records the sale

The Purchase API (PurchaseLoad) is the record of sale. It stores the transaction and triggers receipts, bonus and challenges. It is not the Discount API, which only calculates discounts during checkout and stores nothing.Even if you use the Discount API at the till, you must still send the finalized transaction here. See POS Integrations for where this sits in the lifecycle.

How it works

The API is built for fast ingestion, so the till is not left waiting. It validates the payload, stores it, and asynchronously publishes a PurchaseLoadedEvent to message broker topics that other services subscribe to — bonus calculation, digital receipts and the rest. Purchases with and without a customer are published to separate topics. If you send a loyaltyCardId without a memberId, the service resolves the customer for you from the Loyalty Card service before publishing.

Integration options

Persist before you send. Write the transaction to local storage first, then transmit. A network blip or a service outage then costs you a retry rather than a lost sale.

Purchases with and without a customer

The presence of memberId in the payload decides how a purchase is treated.
Send everything, not just identified sales. Filtering anonymous purchases out is technically possible but costs you:
  • Lobyco sees only revenue from identified customers, so the business context is incomplete
  • Analytics degrade — some products, such as Self Checkout fraud detection, rely on whole-basket analysis
  • Power BI reports comparing identified and anonymous behaviour become impossible

Validations

The payload is validated before anything is stored. These are the rules your integration has to satisfy.

Required fields

Checkout type

The optional checkoutType records which channel the sale came through.

Products

Each product requires id, sequenceNumber, name, categoryId, quantity, originalPrice and price.
  • price is the amount after product-level discounts; originalPrice is before them
  • Returned or cancelled products carry a negative price
  • price and originalPrice must share the same sign
  • categoryId must not be empty or whitespace

Payment methods

At least one payment method is required unless totalAmount is zero. Each needs amount and a paymentType of Card, Cash, LoyaltyApp or Other.
  • Card payments require cardPan, the masked card number
  • Foreign currency requires both currencyCode and conversionRate (≥ 0)
  • If the currency matches the purchase currency, conversionRate must be 1
  • rounding is only allowed on Cash

Discounts

Discounts are optional, and each entry needs a totalDiscountAmount. There is no totalDiscount field on the purchase itself — the discounts array holds individual entries. An entry is global when appliedProducts is missing, null or empty, and product-level when it lists product sequence numbers. The distinction changes how the amounts are validated. One product can carry several discounts: the same sequenceNumber may appear in more than one entry, each with its own name and totalDiscountAmount. The product’s price must reflect the total after all of them, while originalPrice stays untouched — so a product at 10.00 with discounts of 0.50 and 4.83 ends up with a price of 4.67. Keeping the discounts as separate entries is what lets each one be reported on individually.
A worked payload for this case is in the Purchases API reference.

Amount calculations

totalAmount has to reconcile three ways, and all three must agree. taxNotIncluded is totalTaxAmount when any product has taxIncludedInPrice = false, and 0 otherwise. The first two agreeing is what proves your discounts correctly bridge original prices and actual prices. A small deviation against the payment sum is tolerated.

Other rules

  • memberId, loyaltyCardId, externalDiscountCardId, posId and phone must not be empty or whitespace when provided
  • Tax percentage is a fraction between 0 and 1 — 0.25 for 25%
  • Quantity value must not be 0
  • Shipping totalAmount must be greater than 0
  • DisposalItem unitPrice must be greater than 0

API reference

Endpoint documentation: Purchases.
Last modified on August 12, 2026