---
updatedAt: 2026-07-08T09:43:12.000Z
---

Fetch the complete documentation index at: https://developers.flipdish.com/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# List sales

⚠️ **Beta Notice:** This endpoint is currently in beta and may change without notice. Returns paginated full sale payloads for the org, sorted by creation time descending. Supports pagination via `pageSize` and `cursor`, and optional `after` / `before` UTC datetime filters on `createdAt`.

Returns paginated full sale payloads including the nested `sale` object. Supports pagination via `pageSize` and `cursor`, and optional `after` / `before` UTC datetime filters on `createdAt`.

# OpenAPI definition

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "Sales Management — Org-wide Queries",
    "version": "1.0.0",
    "description": "These are **read-only GET endpoints** for querying sales at **org level** (`orgId` only — no `salesChannelId` required in the path).\n\nThis page is separate from the main [Sales Management API](https://developers.flipdish.com/v3.0/reference/sales-management-api) reference. Create, cancel, update, and sales-channel-scoped read endpoints remain documented there.\n\n**What is on this page:**\n- **List sales** — paginated complete sale objects for sync or export\n- **List sales for a property** — paginated complete sale objects scoped to a property\n- **Get a sale** — single sale by `orgId` and `saleId`\n\n**Routing note:** Requests are served via `https://api.flipdish.co/sales/...`.",
    "contact": {
      "name": "Flipdish API Support",
      "url": "https://help.flipdish.com",
      "email": "help@flipdish.com"
    }
  },
  "servers": [
    {
      "url": "https://api.flipdish.co",
      "description": "Flipdish base API"
    }
  ],
  "tags": [
    {
      "name": "Sales",
      "description": "Org-scoped read endpoints with full sale payloads. Does not replace the main Sales Management API reference."
    }
  ],
  "paths": {
    "/sales/orgs/{orgId}/sales": {
      "get": {
        "summary": "List sales",
        "description": "⚠️ **Beta Notice:** This endpoint is currently in beta and may change without notice. Returns paginated full sale payloads for the org, sorted by creation time descending. Supports pagination via `pageSize` and `cursor`, and optional `after` / `before` UTC datetime filters on `createdAt`.\n\nReturns paginated full sale payloads including the nested `sale` object. Supports pagination via `pageSize` and `cursor`, and optional `after` / `before` UTC datetime filters on `createdAt`.",
        "operationId": "ListOrgSales",
        "tags": [
          "Sales"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^org\\d+$",
              "description": "Org ID in standard format",
              "example": "org123"
            },
            "required": true,
            "name": "orgId",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 50,
              "maximum": 200
            },
            "required": false,
            "name": "pageSize",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque pagination token from a previous response nextCursor. Pass unchanged together with the same filter query parameters."
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "List of full sales",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListSalesFullResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    }
  },
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "schemas": {
      "PublicSaleResponse": {
        "type": "object",
        "properties": {
          "saleId": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "type": "string",
                "pattern": "^[0-9A-HJKMNP-TV-Z]+$"
              }
            ],
            "example": "3N356"
          },
          "orgId": {
            "type": "string",
            "pattern": "^org\\d+$",
            "description": "Org ID in standard format",
            "example": "org123"
          },
          "brandId": {
            "type": "string",
            "minLength": 1,
            "description": "Brand ID string. Most brands use the format br<number> (e.g. br123). Legacy brands may use the fd<number> format (e.g. fd123) or a plain alphanumeric slug (e.g. mexicancafe). No format is enforced — any non-empty string is accepted.",
            "example": "br123"
          },
          "propertyId": {
            "type": "string",
            "pattern": "^p\\d+$",
            "description": "Property ID in standard format",
            "example": "p123"
          },
          "salesChannelId": {
            "type": "string",
            "minLength": 1,
            "description": "Sales channel ID string. Most sales channels use the format sc<number> (e.g. sc123). Legacy sales channels may be a plain integer without the sc prefix (e.g. 123). No format is enforced — any non-empty string is accepted.",
            "example": "sc123"
          },
          "salesChannelType": {
            "type": "string",
            "example": "UberEats"
          },
          "externalId": {
            "type": "string",
            "example": "sale-external-123"
          },
          "source": {
            "type": "string",
            "example": "Web"
          },
          "dispatchType": {
            "type": "string",
            "example": "delivery"
          },
          "pendingAction": {
            "type": "string",
            "enum": [
              "accept",
              "refund",
              "cancel"
            ],
            "description": "Present while an accept/refund/cancel request is in flight awaiting RMS. Public `status` stays the previous lifecycle value; Portal shows a loader on this action only.",
            "example": "refund"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "cancelRequestedAt": {
            "type": "string",
            "format": "date-time"
          },
          "cancelledAt": {
            "type": "string",
            "format": "date-time"
          },
          "rejectionReason": {
            "type": "string",
            "minLength": 1,
            "description": "RMS SaleRejectionReason when the sale was rejected (e.g. RejectedAutomatically). Present for rejected sales when known; used by Portal to distinguish auto-reject vs restaurant reject.",
            "example": "RejectedAutomatically"
          },
          "sale": {
            "$ref": "#/components/schemas/PublicSale"
          }
        },
        "required": [
          "saleId",
          "orgId",
          "brandId",
          "propertyId",
          "salesChannelId",
          "salesChannelType",
          "createdAt",
          "sale"
        ]
      },
      "ListSalesFullResponse": {
        "type": "object",
        "properties": {
          "pageSize": {
            "type": "integer",
            "example": 50
          },
          "hasMoreRecords": {
            "type": "boolean",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "description": "Present when hasMoreRecords is true. Pass as the cursor query parameter on the next request with the same filters. Not portable to GET /sales or to different propertyId filters."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicSaleResponse"
            }
          }
        },
        "required": [
          "pageSize",
          "hasMoreRecords",
          "data"
        ]
      },
      "PublicSale": {
        "type": "object",
        "properties": {
          "salesChannelId": {
            "type": "string",
            "minLength": 1,
            "description": "Flipdish-generated ID of the sales channel (the platform-specific selling surface — e.g. a Deliveroo storefront, an UberEats listing, a Flipdish kiosk) under which the sale was placed. Format: `sc` followed by digits, e.g. `sc123`.",
            "example": "sc123"
          },
          "source": {
            "type": "string",
            "description": "Free-text identifier of the producer that submitted the sale (e.g. \"App\", \"ChatGPT\", \"PartnerX\"). Echoed back on `sale.created.v1` so consumers can attribute the sale to its originating surface.",
            "example": "App"
          },
          "creationTime": {
            "type": "string",
            "format": "date-time",
            "description": "Date (ISO 8601 UTC) at which the sale was created.",
            "example": "2025-10-28T14:00:00Z"
          },
          "requestedFulfilmentTime": {
            "type": "string",
            "format": "date-time",
            "description": "Date (ISO 8601 UTC) at which the restaurant is expected to hand over responsibility for the food (e.g. handed to courier or to dine-in customer).",
            "example": "2025-10-28T14:30:00Z"
          },
          "acceptedForTime": {
            "type": "string",
            "format": "date-time",
            "description": "Date (ISO 8601 UTC) at which the restaurant expects the sale to be ready after acceptance (handed to customer or courier). Set when the sale is accepted; absent on created/pending sales. Aligns with legacy Portal `AcceptedFor` and the `acceptedForTime` field on `sale.accept.requested.v1`.",
            "example": "2025-10-28T14:40:00Z"
          },
          "desiredAsap": {
            "type": "boolean",
            "description": "Whether the customer wants the sale fulfilled as soon as possible. When `true`, the server reconciles this against `requestedFulfilmentTime`: if `requestedFulfilmentTime` is more than ~1 hour away the requested time is honoured and the sale is treated as scheduled (asap is overridden); if it is within ~1 hour, the sale is fulfilled as soon as possible and `requestedFulfilmentTime` is effectively ignored.",
            "example": true
          },
          "dispatchType": {
            "type": "string",
            "enum": [
              "DineIn",
              "TakeAway",
              "Collection",
              "Delivery"
            ],
            "description": "Fulfilment mode. One of: DineIn, TakeAway, Collection, Delivery.",
            "example": "Delivery"
          },
          "externalId": {
            "type": "string",
            "description": "The partner/platform's own reference identifier for this sale (e.g. the marketplace's order number or the POS system's ticket ID). This is a machine-readable correlation key: it lets the partner look up the sale in Flipdish using their own system's ID. It is **not** shown to restaurant staff by default. If `displayId` is omitted, `externalId` is used as the fallback back-of-house display label — so if `externalId` is a long UUID or opaque string, supplying a short `displayId` is strongly recommended.",
            "example": "bd1ed236-ee79-11ed-a05b-0242ac12A003"
          },
          "displayId": {
            "type": "string",
            "description": "A short, human-readable label shown to restaurant staff in back-of-house systems (KDS, POS, receipt printouts). Unlike `externalId` — which is a machine-readable correlation key for partner integrations — `displayId` is the label staff use to identify a sale in their day-to-day workflow (e.g. `#2A003`). Keep it brief so it remains scannable at a glance. If omitted, `externalId` is used as the fallback display label.",
            "example": "2A003"
          },
          "menuId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the menu that the sale was placed against.",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "menuRevisionId": {
            "type": "string",
            "description": "Identifier of the specific menu revision (snapshot) that was active when the sale was placed. Required so receipts and audits can reproduce exactly what was on the menu at the time.",
            "example": "123"
          },
          "customer": {
            "$ref": "#/components/schemas/PublicSaleCustomer"
          },
          "delivery": {
            "$ref": "#/components/schemas/PublicSaleDelivery"
          },
          "dineIn": {
            "$ref": "#/components/schemas/PublicSaleDineIn"
          },
          "notes": {
            "type": "string",
            "description": "Notes intended for the restaurant staff / chef",
            "example": "please keep the spices minimum"
          },
          "charges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicSaleCharge"
            },
            "description": "Additional monetary lines added to the sale (delivery fees, service charges, tips, etc.)."
          },
          "discounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicSaleDiscount"
            },
            "description": "Discounts applied to the sale (vouchers, loyalty rewards, spot/manual discounts, other)."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicSaleItem"
            },
            "minItems": 1,
            "description": "Items included in the sale. At least one item is required."
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicSalePayment"
            },
            "description": "Payment and refund lines for the sale. Used to generate receipt lines downstream. May be empty when no payment has been recorded yet, in which case the sale is unpaid."
          },
          "currency": {
            "type": "string",
            "enum": [
              "AED",
              "AFN",
              "ALL",
              "AMD",
              "ANG",
              "AOA",
              "ARS",
              "AUD",
              "AWG",
              "AZN",
              "BAM",
              "BBD",
              "BDT",
              "BGN",
              "BHD",
              "BIF",
              "BMD",
              "BND",
              "BOB",
              "BOV",
              "BRL",
              "BSD",
              "BTN",
              "BWP",
              "BYN",
              "BZD",
              "CAD",
              "CDF",
              "CHE",
              "CHF",
              "CHW",
              "CLF",
              "CLP",
              "CNY",
              "COP",
              "COU",
              "CRC",
              "CUC",
              "CUP",
              "CVE",
              "CZK",
              "DJF",
              "DKK",
              "DOP",
              "DZD",
              "EGP",
              "ERN",
              "ETB",
              "EUR",
              "FJD",
              "FKP",
              "GBP",
              "GEL",
              "GHS",
              "GIP",
              "GMD",
              "GNF",
              "GTQ",
              "GYD",
              "HKD",
              "HNL",
              "HTG",
              "HUF",
              "IDR",
              "ILS",
              "INR",
              "IQD",
              "IRR",
              "ISK",
              "JMD",
              "JOD",
              "JPY",
              "KES",
              "KGS",
              "KHR",
              "KMF",
              "KPW",
              "KRW",
              "KWD",
              "KYD",
              "KZT",
              "LAK",
              "LBP",
              "LKR",
              "LRD",
              "LSL",
              "LYD",
              "MAD",
              "MDL",
              "MGA",
              "MKD",
              "MMK",
              "MNT",
              "MOP",
              "MRU",
              "MUR",
              "MVR",
              "MWK",
              "MXN",
              "MXV",
              "MYR",
              "MZN",
              "NAD",
              "NGN",
              "NIO",
              "NOK",
              "NPR",
              "NZD",
              "OMR",
              "PAB",
              "PEN",
              "PGK",
              "PHP",
              "PKR",
              "PLN",
              "PYG",
              "QAR",
              "RON",
              "RSD",
              "RUB",
              "RWF",
              "SAR",
              "SBD",
              "SCR",
              "SDG",
              "SEK",
              "SGD",
              "SHP",
              "SLE",
              "SOS",
              "SRD",
              "SSP",
              "STN",
              "SVC",
              "SYP",
              "SZL",
              "THB",
              "TJS",
              "TMT",
              "TND",
              "TOP",
              "TRY",
              "TTD",
              "TWD",
              "TZS",
              "UAH",
              "UGX",
              "USD",
              "USN",
              "UYI",
              "UYU",
              "UZS",
              "VED",
              "VEF",
              "VND",
              "VUV",
              "WST",
              "XAF",
              "XCD",
              "XDR",
              "XOF",
              "XPF",
              "XSU",
              "XUA",
              "YER",
              "ZAR",
              "ZMW",
              "ZWL"
            ],
            "description": "ISO 4217 currency code for all monetary amounts on this sale.",
            "example": "EUR"
          },
          "totalCharges": {
            "type": "number",
            "minimum": 0,
            "description": "Sum of charges on the sale.",
            "example": 5
          },
          "totalDiscounts": {
            "type": "number",
            "minimum": 0,
            "description": "Sum of discounts on the sale.",
            "example": 6
          },
          "totalRefunds": {
            "type": "number",
            "minimum": 0,
            "description": "Sum of refunds on the sale.",
            "example": 0
          },
          "totalTax": {
            "type": "number",
            "minimum": 0,
            "description": "Total tax itemised on the sale. May be zero when tax is included in line prices rather than broken out separately, or when items are zero-rated. totalSaleAmount always reflects the full amount payable including tax.",
            "example": 0
          },
          "totalSaleAmount": {
            "type": "number",
            "minimum": 0,
            "description": "Total sale amount including tax.",
            "example": 30
          },
          "totalPaidAmount": {
            "type": "number",
            "minimum": 0,
            "description": "Total amount paid.",
            "example": 30
          },
          "metadata": {
            "type": "string",
            "description": "Free-text field for partner-supplied additional information. Conventionally a JSON-encoded string, but stored and returned as a string (no schema is enforced on its contents).",
            "example": "{\"key\": \"value\"}"
          },
          "status": {
            "type": "string",
            "enum": [
              "SALE_CREATED",
              "SALE_ACCEPTED",
              "SALE_REJECTED",
              "SALE_CANCELLED",
              "SALE_CANCEL_REQUESTED",
              "SALE_PREPARED_BY_KITCHEN",
              "SALE_DISPATCHED",
              "SALE_ON_THE_WAY",
              "SALE_DELIVERED",
              "SALE_REFUNDED",
              "SALE_FULLY_REFUNDED",
              "SALE_PARTIALLY_REFUNDED"
            ],
            "description": "Current lifecycle status of the sale.",
            "example": "SALE_ACCEPTED"
          },
          "refundable": {
            "type": "boolean",
            "default": true,
            "description": "Whether this sale is eligible for refunds. Some sale types (e.g. Marketplace sales) or payment methods may set this to `false`, preventing refund requests even if other conditions (accepted, not cancelled, not fully refunded) are met. Defaults to `true` for backward compatibility.",
            "example": true
          }
        },
        "required": [
          "salesChannelId",
          "requestedFulfilmentTime",
          "dispatchType",
          "menuId",
          "menuRevisionId",
          "charges",
          "discounts",
          "items",
          "payments",
          "currency",
          "totalCharges",
          "totalDiscounts",
          "totalRefunds",
          "totalTax",
          "totalSaleAmount",
          "totalPaidAmount"
        ],
        "description": "Full public sale payload as projected from the source-of-truth events."
      },
      "PublicSaleCustomer": {
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string",
            "description": "The partner/platform's own identifier for this customer (e.g. the marketplace's customer account ID). Distinct from the sale-level `externalId` (which identifies the sale) and from the Flipdish customer UUID in `id`. Use this to correlate the customer record back to the originating platform's system.",
            "example": "customer_123"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Flipdish customer UUID. If provided, must reference an existing customer; omit or leave empty if unknown.",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "name": {
            "type": "string",
            "description": "Customer full name",
            "example": "John Smith"
          },
          "contactMaskingCode": {
            "type": "string",
            "description": "Numeric code provided by the marketplace alongside a relay phone number. When the restaurant calls the relay number, entering this code routes the call to the customer's actual phone number. Used by platforms (e.g. Deliveroo) that obfuscate the customer's real phone number.",
            "example": "024755255"
          },
          "contactPhoneNumber": {
            "type": "string",
            "pattern": "^\\+[1-9]\\d{1,14}$",
            "description": "Customer phone number in E.164 format. Leading + is required.",
            "example": "+1234567890"
          },
          "emailAddress": {
            "type": "string",
            "format": "email",
            "description": "Customer email address",
            "example": "john.smith@flipdish.com"
          }
        },
        "description": "The end customer for the sale (name, contact details, identifiers)."
      },
      "PublicSaleDelivery": {
        "type": "object",
        "properties": {
          "notes": {
            "type": "string",
            "description": "Free-text instructions from the customer for preparation or delivery",
            "example": "Leave at door, don't ring the bell"
          },
          "deliveredBy": {
            "type": "string",
            "enum": [
              "Client",
              "External"
            ],
            "description": "Who is fulfilling the delivery. `Client` = the restaurant delivers; `External` = a marketplace or third-party courier delivers.",
            "example": "Client"
          },
          "location": {
            "$ref": "#/components/schemas/PublicSaleLocation"
          }
        },
        "description": "Delivery details (drop-off address, who delivers, special instructions). Required when `dispatchType` is `Delivery`."
      },
      "PublicSaleDineIn": {
        "type": "object",
        "properties": {
          "tableId": {
            "type": "string",
            "description": "Table identifier (free text field)",
            "example": "3"
          },
          "guests": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of guests",
            "example": 10
          }
        },
        "required": [
          "tableId",
          "guests"
        ],
        "description": "Dine-in details (table, guest count). Expected when `dispatchType` is `DineIn`."
      },
      "PublicSaleCharge": {
        "type": "object",
        "properties": {
          "itemId": {
            "type": "string",
            "format": "uuid",
            "description": "Deprecated. Flipdish-internal sale item line UUID. Use menuItemId instead — integrators correlate charges to menu catalog items, not internal line IDs. Only present when the charge is scoped to a specific sale item line.",
            "example": "123e4567-e89b-12d3-a456-426614174000",
            "deprecated": true
          },
          "menuItemId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the menu item this charge relates to, when the charge is scoped to a specific item (e.g. an item-level bag fee). Omitted for sale-level charges such as delivery or service fees.",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "type": {
            "type": "string",
            "enum": [
              "Delivery",
              "Service",
              "Tip",
              "Other"
            ],
            "description": "Charge category such as Delivery.Charge or Bag.Charge",
            "example": "Delivery"
          },
          "amount": {
            "type": "number",
            "minimum": 0,
            "description": "Monetary value for the charge",
            "example": 3.5
          }
        },
        "required": [
          "type",
          "amount"
        ]
      },
      "PublicSaleDiscount": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "amount": {
                "type": "number",
                "minimum": 0,
                "description": "Monetary value for the discount",
                "example": 10
              },
              "type": {
                "type": "string",
                "enum": [
                  "Voucher"
                ]
              },
              "code": {
                "type": "string",
                "description": "Voucher code (required for Voucher discounts)",
                "example": "FIRST10"
              }
            },
            "required": [
              "amount",
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "amount": {
                "type": "number",
                "minimum": 0,
                "description": "Monetary value for the discount",
                "example": 10
              },
              "type": {
                "type": "string",
                "enum": [
                  "Loyalty"
                ]
              },
              "code": {
                "type": "string",
                "description": "Loyalty program code (required for Loyalty discounts)",
                "example": "LOYALTY10"
              }
            },
            "required": [
              "amount",
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "amount": {
                "type": "number",
                "minimum": 0,
                "description": "Monetary value for the discount",
                "example": 10
              },
              "type": {
                "type": "string",
                "enum": [
                  "Spot"
                ]
              },
              "code": {
                "type": "string",
                "description": "Optional code identifying the spot discount",
                "example": "SPOT5"
              }
            },
            "required": [
              "amount",
              "type"
            ]
          },
          {
            "type": "object",
            "properties": {
              "amount": {
                "type": "number",
                "minimum": 0,
                "description": "Monetary value for the discount",
                "example": 10
              },
              "type": {
                "type": "string",
                "enum": [
                  "Other"
                ]
              },
              "code": {
                "type": "string",
                "description": "Optional code describing the discount"
              }
            },
            "required": [
              "amount",
              "type"
            ]
          }
        ]
      },
      "PublicSaleItem": {
        "type": "object",
        "properties": {
          "menuItemId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the menu item being purchased. Must match an item in the current menu (or its revision).",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "menuItemName": {
            "type": "string",
            "description": "Menu item display name, as captured at the time the sale was placed",
            "example": "Margherita Pizza"
          },
          "quantity": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true,
            "description": "Quantity of the item",
            "example": 2
          },
          "unitPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Price of the item",
            "example": 15
          },
          "notes": {
            "type": "string",
            "description": "Any instructions related to an item",
            "example": "No onions please"
          },
          "modifierItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicSaleModifierItem"
            },
            "description": "Modifiers applied to this item, allowing any depth of nesting (e.g. a pizza item → size modifier → topping modifier → extra-cheese modifier). Modifier entries require both the legacy menuItemId and the new modifierItemId during migration; menuItemId will be deprecated.",
            "example": [
              {
                "menuItemId": "123e4567-e89b-12d3-a456-426614174000",
                "modifierItemId": "123e4567-e89b-12d3-a456-426614174000",
                "modifierItemName": "Extra Cheese",
                "quantity": 1,
                "unitPrice": 2.5,
                "notes": "Extra cheese",
                "modifierItems": []
              }
            ]
          }
        },
        "required": [
          "menuItemId",
          "quantity",
          "unitPrice"
        ]
      },
      "PublicSalePayment": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "Sale",
              "Refund"
            ],
            "description": "Whether this line is money collected (Sale) or returned to the customer (Refund). Allowed values: Sale, Refund.",
            "example": "Sale"
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "Cash",
              "Credit",
              "Online",
              "PhonePayment",
              "ExternalPayment"
            ],
            "description": "How payment was made. Allowed: Cash, Credit, Online, PhonePayment, ExternalPayment",
            "example": "Cash"
          },
          "amount": {
            "type": "number",
            "minimum": 0,
            "description": "Amount of the payment type that was made against the sale",
            "example": 30
          },
          "description1": {
            "type": "string",
            "description": "Description about the payment, e.g. 'Visa ****4921' or 'Paypal c***y@gm**.com'",
            "example": "Visa ****4921"
          },
          "description2": {
            "type": "string",
            "description": "Description about the payment, e.g. Authorization code or reference number",
            "example": "VM278412312"
          },
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "description": "Date (ISO 8601 UTC) at which the payment was made.",
            "example": "2025-10-28T14:30:00Z"
          }
        },
        "required": [
          "type",
          "paymentMethod",
          "amount",
          "paidAt"
        ]
      },
      "PublicSaleLocation": {
        "type": "object",
        "properties": {
          "addressFields": {
            "$ref": "#/components/schemas/PublicSaleAddressFields"
          },
          "countryCode": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code",
            "example": "IE"
          },
          "coordinates": {
            "$ref": "#/components/schemas/PublicSaleCoordinates"
          }
        },
        "required": [
          "addressFields",
          "countryCode"
        ],
        "description": "Address fields for drop-off location. Required when deliveredBy is 'Client'"
      },
      "PublicSaleModifierItem": {
        "type": "object",
        "properties": {
          "menuItemId": {
            "type": "string",
            "format": "uuid",
            "description": "Legacy UUID of the menu item this modifier represents. This field will be deprecated after consumers migrate to modifierItemId.",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "modifierItemId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the menu item this modifier represents. Must match an item in the current menu (or its revision).",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "modifierItemName": {
            "type": "string",
            "description": "Modifier menu item display name, as captured at the time the sale was placed",
            "example": "Extra Cheese"
          },
          "quantity": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true,
            "description": "Quantity of the modifier",
            "example": 2
          },
          "unitPrice": {
            "type": "number",
            "minimum": 0,
            "description": "Price of the modifier",
            "example": 15
          },
          "notes": {
            "type": "string",
            "description": "Any instructions related to the modifier",
            "example": "No onions please"
          },
          "modifierItems": {
            "type": "array",
            "description": "Nested modifiers (recursive - same structure at any depth).",
            "items": {
              "$ref": "#/components/schemas/PublicSaleModifierItem"
            },
            "example": [
              {
                "menuItemId": "123e4567-e89b-12d3-a456-426614174000",
                "modifierItemId": "123e4567-e89b-12d3-a456-426614174000",
                "modifierItemName": "Extra Cheese",
                "quantity": 1,
                "unitPrice": 2.5,
                "notes": "Extra cheese",
                "modifierItems": []
              }
            ]
          }
        },
        "required": [
          "menuItemId",
          "modifierItemId",
          "quantity",
          "unitPrice"
        ]
      },
      "PublicSaleAddressFields": {
        "type": "object",
        "properties": {
          "line1": {
            "type": "string",
            "minLength": 1,
            "description": "Address line 1",
            "example": "Apartment 5"
          },
          "line2": {
            "type": "string",
            "description": "Address line 2",
            "example": "123 Main Street"
          },
          "line3": {
            "type": "string",
            "description": "Address line 3",
            "example": "State"
          },
          "postCode": {
            "type": "string",
            "description": "Postal code",
            "example": "12345"
          }
        },
        "required": [
          "line1"
        ],
        "description": "Address fields"
      },
      "PublicSaleCoordinates": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number",
            "description": "Latitude",
            "example": 24.867762
          },
          "longitude": {
            "type": "number",
            "description": "Longitude",
            "example": 67.057387
          }
        },
        "required": [
          "latitude",
          "longitude"
        ],
        "description": "Geographic coordinates"
      }
    },
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Bearer token authentication"
      }
    }
  }
}
```