GraphQL — Узел магазина

296 определений

← Вся документация · Источник: контракты, graphql/store.graphql на коммите 8000474.

Справочники на английском — на языке контрактов, из которых они созданы.

scalar UUID

# GraphQL schema of the node in the store role (spec/API.md). Queries only in
# this version: changes go through REST. Every field checks the same
# permission as its REST counterpart in openapi/store-v1.yaml.
"A UUID in its canonical text form."
scalar UUID

scalar DateTime

"An RFC 3339 time in UTC."
scalar DateTime

scalar Date

"A business date, YYYY-MM-DD."
scalar Date

scalar Int64

"A signed 64-bit integer as a JSON number (amounts in minor units, quantities in thousandths)."
scalar Int64

scalar JSON

"Any JSON value (localized texts, profile attributes)."
scalar JSON

type Query

type Query {
  "The caller and its effective permissions."
  me: Me!
  "store.read: this store's update (spec/RELEASES.md, The store's update)"
  storeUpdate: StoreUpdate!
  "release.read: the releases this node holds, the latest version first: a hub's, or a store's with no hub (spec/RELEASES.md, A store with no hub); owned_by_hub at a store of a chain"
  releases(first: Int! = 50, after: String): ReleasePage!
  "release.read: one release with its signed manifest"
  release(version: String!): Release
  "store.read"
  store: Store!
  "store.read: the store's effective brand, the brand overlaid with its banner, or the default (spec/BRANDS.md, What the apps get)"
  brand: EffectiveBrand!
  "store.read"
  taxRates: [TaxRate!]!
  "store.read: spec/TAX.md, Container deposits"
  deposits: [Deposit!]!
  "store.read: spec/TAX.md, Rates from a provider; the key is only named"
  taxProvider: TaxProvider!
  "store.read"
  license: License!
  "item.read"
  items(q: String, groupId: UUID, active: Boolean, parentId: UUID, kindOf: ItemKindOf, first: Int! = 50, after: String): ItemPage!
  "item.read"
  item(id: UUID!): Item
  "item.read: code only as a variable (spec/API.md, Transport)"
  lookup(code: String!): Lookup
  "item.read"
  groups: [Group!]!
  "item.read: spec/SEARCH.md, Tags"
  tags: [Tag!]!
  "item.read"
  promotions: [Promotion!]!
  "stock.read"
  stock(itemId: UUID!): Stock
  "stock.read"
  holds(itemId: UUID): [Hold!]!
  "stock.read: the item serials matching a serial number, history newest first (spec/VARIANTS.md)"
  serials(serial: String!): [SerialRecord!]!
  "document.read"
  documents(kind: DocumentKind, status: DocumentStatus, order: ListOrder = ASC, first: Int! = 50, after: String): DocumentPage!
  "document.read"
  document(id: UUID!): Document
  "shift.read"
  shifts(status: ShiftStatus, workplaceId: UUID, first: Int! = 50, after: String): ShiftPage!
  "shift.read"
  shift(id: UUID!): Shift
  "shift.report.x"
  shiftReport(id: UUID!, cashierId: UUID): ShiftTotals
  "receipt.read"
  receipts(shiftId: UUID, first: Int! = 50, after: String): ReceiptPage!
  "receipt.read"
  receipt(id: UUID!): Receipt
  "employee.read"
  employees(first: Int! = 50, after: String): EmployeePage!
  "role.read"
  roles: [Role!]!
  "report.read: spec/REPORTS.md, Sales"
  salesReport(from: DateTime!, to: DateTime!, groupBy: SalesGroupBy!, rollUpVariants: Boolean! = false): SalesReport!
  "report.read: spec/REPORTS.md, Stock"
  stockReport(groupId: UUID, belowZero: Boolean, first: Int! = 50, after: String): StockReportPage!
  "report.read: spec/REPORTS.md, Movements"
  movements(from: DateTime!, to: DateTime!, itemId: UUID, first: Int! = 50, after: String): MovementPage!
  "price.edit: spec/PRICING.md, Markup (writes nothing)"
  priceProposals(input: PriceProposalInput!): [PriceProposal!]!
  "item.read: spec/SYNC.md, Change requests (empty in a store with no hub)"
  changeRequests(status: ChangeRequestStatus, first: Int! = 50, after: String): ChangeRequestPage!
  "Changes agents proposed that the caller asked for or may approve (spec/MCP.md)."
  proposals(status: ProposalStatus, first: Int! = 50, after: String): ProposalPage!
  "One proposal the caller asked for or may approve."
  proposal(id: UUID!): Proposal
  "assistant.use: the assistant's settings, never the key (spec/ASSISTANT.md)"
  assistantSettings: AssistantSettings!
  "item.read: search the catalog by meaning, exact matches first (spec/SEARCH.md, Searching); the license module search"
  searchItems(q: String!, groupId: UUID, tagId: UUID, storeId: UUID, includeInactive: Boolean = false, limit: Int! = 20): SearchResult!
  "item.read: suggested tags and groups, newest first (spec/SEARCH.md, Suggestions)"
  searchSuggestions(status: SuggestionStatus = OPEN, kind: SuggestionKind, itemId: UUID, first: Int! = 50, after: String): SuggestionPage!
  "item.read: the active index and the one building"
  searchIndex: SearchIndexStatus!
  "search.manage: the embedding provider and the suggestions' settings, never the key"
  searchSettings: SearchSettings!
  "network.read: spec/NETWORK.md, the private network as this store sees it"
  network: StoreNetworkState!
  "report.read: spec/REPORTS.md, Tax"
  taxReport(from: Date!, to: Date!): TaxReport!
  "kitchen.use: a station's tickets, oldest first (spec/FOOD_SERVICE.md, Kitchen display)"
  kitchenTickets(station: String!, state: KitchenTicketList = OPEN): [KitchenTicket!]!
  "pse.read: the pseudoephedrine logbook, newest first (spec/PHARMACY.md)"
  pseLog(from: Date!, to: Date!, first: Int! = 50, after: String): PseLogPage!
  "document.read: spec/STOCK.md, Transfers; both stores' documents"
  transfers(direction: TransferDirection, state: TransferState, first: Int! = 50, after: String): TransferPage!
  "document.read"
  transfer(id: UUID!): Transfer
  "stock.read: lines of transfers from or to this store that are in transit"
  inTransit: [InTransitLine!]!
  "store.read: the chain's stores, each by id, code and name only (spec/SYNC.md); empty with no hub"
  storeDirectory: [StoreDirectoryEntry!]!
  "document.read: spec/STOCK.md, Purchase orders"
  suppliers: [Supplier!]!
  "item.read: spec/STOCK.md, Recipes and production"
  recipes(productItemId: UUID): [Recipe!]!
  "item.read: spec/PRICING.md, Batch prices; holds nothing"
  priceSplit(itemId: UUID!, quantityMilli: Int64!, lot: String): PriceSplit!
  "item.read: spec/PRICING.md, Expiry rules"
  expiryRules: [ExpiryRule!]!
  "item.read: spec/LOYALTY.md, Programs"
  loyaltyPrograms: [LoyaltyProgram!]!
  "customer.read: masked (spec/LOYALTY.md, Customers); by a name's prefix or a phone's last four digits"
  customers(q: String, first: Int! = 50, after: String): CustomerPage!
  "customer.read: masked"
  customer(id: UUID!): Customer
  "customer.read: by a card number or an E.164 phone, exactly, each only as a variable (spec/API.md, Transport); masked; no presence token (REST POST /v1/customers/lookup gives one)"
  customerLookup(card: String, phone: String): Customer
  "customer.read: points and stamps at the balance owner"
  customerBalances(id: UUID!): CustomerBalances
  "loyalty.manage: newest first"
  customerLedger(id: UUID!, programId: UUID, first: Int! = 50, after: String): LedgerPage!
  "loyalty.manage: spec/LOYALTY.md, Balances and their owner"
  balanceHolds(status: BalanceHoldStatus, first: Int! = 50, after: String): BalanceHoldPage!
  "item.read: spec/LOYALTY.md, Vouchers and coupons"
  vouchers(active: Boolean, coupons: Boolean, first: Int! = 50, after: String): VoucherPage!
  "receipt.sell: a code's voucher and whether it can be used now; code only as a variable (spec/API.md, Transport)"
  voucherLookup(code: String!): VoucherCheck
  "gift_card.manage: masked (spec/LOYALTY.md, Gift cards); a balance by number is REST only, the number in its body"
  giftCards(status: GiftCardStatus, batchId: UUID, first: Int! = 50, after: String): GiftCardPage!
  "gift_card.manage"
  giftCard(id: UUID!): GiftCard
  "gift_card.manage: newest first"
  giftCardLedger(id: UUID!, first: Int! = 50, after: String): LedgerPage!
  "item.read: spec/LOYALTY.md, Partner codes"
  partners: [Partner!]!
  "bonus.read: spec/REPORTS.md, Employee bonuses"
  bonusRules: [BonusRule!]!
  "bonus.read: spec/REPORTS.md, Employee bonuses"
  bonusReport(from: DateTime!, to: DateTime!): BonusReport!
  "report.read: spec/REPORTS.md, Partners"
  partnerReport(from: DateTime!, to: DateTime!): PartnerReport!
  "check.use or floor.manage: spec/RESTAURANT.md, The floor; tables' QR tokens only with floor.manage"
  floor: Floor!
  "check.use: spec/RESTAURANT.md, Checks; open and billed by default, oldest first"
  checks(state: CheckListState = ACTIVE, tableId: UUID, waiterId: UUID, first: Int! = 50, after: String): CheckPage!
  "check.use"
  check(id: UUID!): Check
  "check.any: offline receipts of checks that did not apply (spec/RESTAURANT.md, Offline)"
  checkConflicts(resolved: Boolean = false, first: Int! = 50, after: String): CheckConflictPage!
  "check.use: guests' orders, pending first (spec/RESTAURANT.md, The guest listener)"
  guestOrders(state: GuestOrderState, tableId: UUID, first: Int! = 50, after: String): GuestOrderPage!
  "item.read: spec/RESTAURANT.md, The guest menu"
  menus: [Menu!]!
  "item.read"
  menu(id: UUID!): Menu
  "item.read: the guest menu's theme"
  menuTheme: MenuTheme!
  "item.read"
  menuPhotos: [MenuPhoto!]!
  "item.read: items temporarily not served"
  stopList: [StopListEntry!]!
  "store.read: whether the guest listener is on and its origin"
  guestListener: GuestListener!
  "report.read: spec/REPORTS.md, Restaurant"
  restaurantReport(from: DateTime!, to: DateTime!, groupBy: RestaurantGroupBy!): RestaurantReport!
  "report.read: spec/REPORTS.md, Service charges; the payroll basis apart from tips"
  serviceChargeReport(from: DateTime!, to: DateTime!, groupBy: ServiceChargeGroupBy!): ServiceChargeReport!
}

enum ProposalStatus

enum ProposalStatus {
  PENDING
  APPROVED
  REFUSED
  FAILED
}

enum ProposalOrigin

enum ProposalOrigin {
  ASSISTANT
  MCP
}

type ProposalActor

"Who asked for a proposal."
type ProposalActor {
  kind: PrincipalKind!
  id: UUID!
  name: String!
}

type Proposal

"A change an agent proposed, waiting for a person (spec/MCP.md)."
type Proposal {
  id: UUID!
  tool: String!
  arguments: JSON!
  summary: String!
  permissions: [String!]!
  origin: ProposalOrigin!
  conversationId: UUID
  createdBy: ProposalActor!
  status: ProposalStatus!
  result: JSON
  error: JSON
  reason: String
  createdAt: DateTime!
  resolvedBy: UUID
  resolvedAt: DateTime
  version: Int64!
}

type ProposalPage

type ProposalPage {
  items: [Proposal!]!
  next: String
}

enum AssistantProviderKind

enum AssistantProviderKind {
  OPENAI
  ANTHROPIC
  GEMINI
  OPENAI_COMPATIBLE
}

type AssistantProvider

type AssistantProvider {
  kind: AssistantProviderKind!
  model: String!
  "From the environment beside the key (spec/ASSISTANT.md, Settings)"
  baseUrl: String
  "The key's name, never the key."
  key: String!
}

type AssistantSettings

type AssistantSettings {
  enabled: Boolean!
  provider: AssistantProvider
  shareRows: Boolean!
  contextLimit: Int!
  compactAt: Int!
  resultBudget: Int!
  "Cents per million tokens: input, cached_input, cache_write, output."
  prices: JSON
  keyPresent: Boolean!
  version: Int64!
}

enum ChangeRequestStatus

enum ChangeRequestStatus {
  PENDING
  APPROVED
  REFUSED
}

enum ChangeRequestKind

enum ChangeRequestKind {
  ITEM
  PRICE
}

type ChangeRequest

"A store's request to its hub for a hub-owned change (spec/SYNC.md)."
type ChangeRequest {
  id: UUID!
  storeId: UUID!
  kind: ChangeRequestKind!
  recordId: UUID!
  proposed: JSON!
  reason: String!
  status: ChangeRequestStatus!
  resolutionReason: String
  createdBy: UUID!
  createdAt: DateTime!
  resolvedBy: UUID
  resolvedAt: DateTime
  version: Int64!
}

type ChangeRequestPage

type ChangeRequestPage {
  items: [ChangeRequest!]!
  next: String
}

enum PrincipalKind

enum PrincipalKind {
  EMPLOYEE
  DEVICE
  INTEGRATION
}

type Principal

type Principal {
  kind: PrincipalKind!
  id: UUID!
  displayName: String!
}

type EffectivePermission

type EffectivePermission {
  code: String!
  limit: Int64
}

type Me

type Me {
  principal: Principal!
  storeId: UUID!
  workplaceId: UUID
  permissions: [EffectivePermission!]!
}

enum UnitSystem

enum UnitSystem {
  METRIC
  US_CUSTOMARY
}

enum TaxMode

enum TaxMode {
  EXCLUSIVE
  INCLUSIVE
}

enum BaseUnit

enum BaseUnit {
  EACH
  KG
  LB
  L
  US_GAL
  M
  FT
}

type BarcodeTemplate

type BarcodeTemplate {
  pattern: String!
  weightUnit: BaseUnit
  weightDecimals: Int
}

type Store

type Store {
  id: UUID!
  code: String!
  name: JSON!
  country: String!
  "`none` or `us`: the country pack whose rules the store follows (spec/COUNTRY_PACKS.md)."
  countryPack: String!
  currency: String!
  currencyExponent: Int!
  unitSystem: UnitSystem!
  taxMode: TaxMode!
  timezone: String!
  receiptLanguage: String!
  holdTtlSeconds: Int!
  cashRoundingMinor: Int64
  barcodeTemplates: [BarcodeTemplate!]!
  registerShare: Int
  "The store's switch for the private network (spec/NETWORK.md); null follows the organization."
  networkEnabled: Boolean
  "The store's /24 block in the organization's subnet; null outside a chain."
  networkBlock: Int
  "Lines carry the codes of their item's tax category (spec/TAX.md)."
  taxByCategory: Boolean!
  profile: StoreProfile!
  pseLimits: PseLimits!
  "A unit a GS1 code shows expired: refused, or sold with a senior's approval (spec/BARCODES.md)."
  expiredSale: ExpiredSale!
  "Whether receipts of the open shift may be voided (spec/FISCAL.md)."
  voidsAllowed: Boolean!
  "The local time after which the day's expiry rules run (spec/PRICING.md)."
  expiryRunAt: String!
  "How long a restaurant check's stock holds live after its last change or renewal (spec/RESTAURANT.md)."
  checkHoldMinutes: Int!
  "When a table counts as seated long, in whole minutes from its oldest open or billed check's opening (spec/RESTAURANT.md, The floor)."
  seatedLongMinutes: Int!
  "The store's service charges, at most three, in the order they apply (spec/API.md, Service charges)."
  serviceCharges: [ServiceCharge!]!
  "Whether the store's back offices install a ready update by themselves (spec/RELEASES.md, Installing)."
  backOfficeUpdates: BackOfficeUpdates!
  "The oldest version of each app the store's node serves, set by a person; none named by default (spec/RELEASES.md, The store's minimum)."
  minAppVersions: MinAppVersions!
  version: Int64!
}

enum BackOfficeUpdates

enum BackOfficeUpdates {
  AUTOMATIC
  ASK
}

type MinAppVersions

"Each app's minimum version; null where the store names none. An app below its minimum is refused 426 app_update_required (spec/RELEASES.md, Refused per request)."
type MinAppVersions {
  "The register's, the handheld's and the kitchen display's."
  cashier: String
  "The back office's and the console's."
  manager: String
  "The device gateway's."
  gateway: String
}

enum ServiceChargeBase

enum ServiceChargeBase {
  AFTER_DISCOUNTS
  BEFORE_DISCOUNTS
}

enum ServiceChargeTaxKind

enum ServiceChargeTaxKind {
  "Taxed by the codes it names; none for an untaxed charge."
  CODES
  "Each line's share is taxed as the line is."
  FOLLOW_LINES
}

type ServiceChargeTax

type ServiceChargeTax {
  kind: ServiceChargeTaxKind!
  "The store's tax codes; empty for an untaxed charge or one that follows the lines."
  codes: [String!]!
}

type ServiceCharge

"One of a store's service charges; never a tip (spec/API.md, Service charges)."
type ServiceCharge {
  id: String!
  "Localized, 1 to 40 characters each."
  label: JSON!
  ratePpm: Int!
  "The party size from which it applies to a check; null for every check and sale."
  minCovers: Int
  orderTypes: [OrderType!]!
  base: ServiceChargeBase!
  tax: ServiceChargeTax!
  "The part the business pays its staff, printed and reported for payroll."
  toStaffPpm: Int!
  "The store's words, localized, on the pre-bill, the receipt and the guest menu."
  notice: JSON!
}

enum ServiceChargeHow

enum ServiceChargeHow {
  "Its conditions hold."
  AUTOMATIC
  ADDED
  "Removed by hand with a reason."
  REMOVED
}

type ServiceChargeShare

"A line's share of one service charge and the tax on that share."
type ServiceChargeShare {
  id: String!
  amountMinor: Int64!
  taxMinor: Int64!
}

type ServiceChargePart

"A charge as one item line a fiscal back end gets (spec/FISCAL.md)."
type ServiceChargePart {
  taxCodes: [String!]!
  amountMinor: Int64!
}

type ReceiptServiceCharge

"A service charge as the receipt keeps it, as it was at the sale."
type ReceiptServiceCharge {
  id: String!
  "In the receipt language."
  label: String!
  notice: String!
  ratePpm: Int!
  how: ServiceChargeHow!
  reason: String
  base: ServiceChargeBase!
  "What the rate was applied to; null on a refund."
  baseMinor: Int64
  amountMinor: Int64!
  taxMinor: Int64!
  parts: [ServiceChargePart!]!
  toStaffPpm: Int!
  "Fixed at the sale (spec/REPORTS.md, Service charges)."
  toStaffMinor: Int64!
}

type CheckServiceCharge

"A service charge a check carries (spec/RESTAURANT.md, Service charges)."
type CheckServiceCharge {
  id: String!
  how: ServiceChargeHow!
  reason: String
  by: UUID
  approvedBy: UUID
  at: DateTime
}

type CheckTotalsServiceCharge

"A charge that applies to a check, figured on its lines."
type CheckTotalsServiceCharge {
  id: String!
  how: ServiceChargeHow!
  baseMinor: Int64!
  amountMinor: Int64!
  taxMinor: Int64!
}

enum ExpiredSale

enum ExpiredSale {
  REFUSE
  APPROVE
}

enum StoreProfile

enum StoreProfile {
  GENERAL
  SUPERMARKET
  GROCERY
  PHARMACY
  COFFEE_SHOP
  PIZZERIA
  RESTAURANT
}

type PseLimits

type PseLimits {
  dailyMg: Int!
  windowMg: Int!
  windowDays: Int!
}

enum Jurisdiction

enum Jurisdiction {
  STATE
  COUNTY
  CITY
  DISTRICT
}

enum TaxCategory

enum TaxCategory {
  GENERAL
  GROCERY
  PREPARED_FOOD
  CANDY
  SOFT_DRINK
  DIETARY_SUPPLEMENT
  CLOTHING
  OTC_DRUG
  RX_DRUG
  MEDICAL_EQUIPMENT
  ALCOHOL
  TOBACCO
  NONTAXABLE
}

type TaxHoliday

type TaxHoliday {
  from: Date!
  to: Date!
  categories: [TaxCategory!]!
  maxUnitPriceMinor: Int64
}

type TaxRate

type TaxRate {
  code: String!
  ratePpm: Int!
  name: JSON!
  jurisdiction: Jurisdiction
  "Null for every category."
  categories: [TaxCategory!]
  exemptCategories: [TaxCategory!]!
  holidays: [TaxHoliday!]!
}

type Deposit

type Deposit {
  code: String!
  name: JSON!
  amountMinor: Int64!
  taxable: Boolean!
}

type TaxReportCode

type TaxReportCode {
  code: String!
  name: JSON!
  jurisdiction: Jurisdiction
  baseMinor: Int64!
  taxMinor: Int64!
}

type TaxReport

type TaxReport {
  from: Date!
  to: Date!
  codes: [TaxReportCode!]!
  netSalesMinor: Int64!
  nontaxableSalesMinor: Int64!
  snapMinor: Int64!
  depositMinor: Int64!
  taxMinor: Int64!
}

enum KitchenTicketList

enum KitchenTicketList {
  OPEN
  READY
}

enum KitchenTicketState

enum KitchenTicketState {
  NEW
  STARTED
  READY
  CANCELED
}

type KitchenTicketLine

type KitchenTicketLine {
  quantityMilli: Int64!
  name: String!
  options: [String!]!
  "The check's line; null for a receipt's ticket."
  lineNo: Int
  "The check line's seat, 0 shared; null for a receipt's ticket."
  seat: Int
  "Voided after it was sent: shown struck through."
  voided: Boolean!
}

enum OrderLabelKind

enum OrderLabelKind {
  TABLE
  TAB
}

enum KitchenTicketKind

enum KitchenTicketKind {
  ORDER
  "The voided lines of a check: acknowledged with a bump, never started."
  CANCEL
}

type KitchenTicket

type KitchenTicket {
  id: UUID!
  kind: KitchenTicketKind!
  "The receipt; null for a check's ticket."
  receiptId: UUID
  receiptNumber: Int64
  "The restaurant check (spec/RESTAURANT.md); null for a receipt's ticket."
  checkId: UUID
  checkNumber: Int64
  course: Int
  waiterId: UUID
  waiterName: String
  "The ticket a cancel ticket cancels lines of."
  cancelsTicketId: UUID
  station: String!
  state: KitchenTicketState!
  orderType: OrderType
  orderLabel: String
  "Whether a check's orderLabel names a table or a tab; null for a receipt's ticket."
  orderLabelKind: OrderLabelKind
  lines: [KitchenTicketLine!]!
  createdAt: DateTime!
  startedAt: DateTime
  readyAt: DateTime
  version: Int64!
}

enum PseIdType

enum PseIdType {
  DRIVER_LICENSE
  STATE_ID
  PASSPORT
  MILITARY_ID
  OTHER
}

type PsePurchaser

type PsePurchaser {
  name: String!
  address: String!
  dateOfBirth: Date!
  idType: PseIdType!
  idState: String!
  idNumber: String!
  signed: Boolean!
}

type PseLogLine

type PseLogLine {
  itemId: UUID!
  name: String!
  quantityMilli: Int64!
  baseMg: Int!
}

type PseLogEntry

type PseLogEntry {
  id: UUID!
  receiptId: UUID!
  receiptNumber: Int64!
  businessDate: Date!
  createdAt: DateTime!
  employeeId: UUID!
  lines: [PseLogLine!]!
  baseMg: Int!
  purchaser: PsePurchaser!
}

type PseLogPage

type PseLogPage {
  items: [PseLogEntry!]!
  next: String
}

enum DeliveryChannel

enum DeliveryChannel {
  EMAIL
  SMS
}

enum DeliveryStatus

enum DeliveryStatus {
  QUEUED
  SENT
  FAILED
}

type ReceiptDelivery

type ReceiptDelivery {
  id: UUID!
  channel: DeliveryChannel!
  "Masked: the address itself is never stored."
  toMasked: String!
  status: DeliveryStatus!
  createdAt: DateTime!
  sentAt: DateTime
  error: String
}

enum LicenseStatus

enum LicenseStatus {
  NONE
  VALID
  GRACE
  EXPIRED
  INVALID
}

type License

type License {
  status: LicenseStatus!
  tier: String!
  org: String
  orgName: String
  modules: [String!]!
  stores: Int!
  registers: Int!
  expires: Date
  usage: LicenseUsage!
  registerShare: Int
  "The node's own organization id, the one a key must name (spec/LICENSE.md, Getting a key)"
  organizationId: UUID
  "The clock is more than 24 hours behind the latest time the node has seen (spec/LICENSE.md, Verification)"
  clockBehind: Boolean
}

type LicenseUsage

type LicenseUsage {
  stores: Int!
  registers: Int!
  "At a store, its tables; null at the hub (spec/LICENSE.md, Usage)."
  tables: Int
  "At a store, its open and billed tabs; null at the hub."
  openTabs: Int
  "3, the places each store has, when the key lacks the restaurant module; null (no limit) when it has it."
  tableLimit: Int
}

enum ItemKind

enum ItemKind {
  PIECE
  WEIGHED
  VARIABLE_WEIGHT
  SERVICE
  "Takes its components' stock at the sale (spec/STOCK.md, Kits)."
  KIT
  "Sells stored value (spec/LOYALTY.md, Gift cards)."
  GIFT_CARD
}

enum StockPolicy

enum StockPolicy {
  FIFO
  FEFO
}

type Barcode

type Barcode {
  barcode: String!
  packQuantityMilli: Int64!
}

type StorePrice

type StorePrice {
  priceMinor: Int64!
  currency: String!
}

type Item

type Item {
  id: UUID!
  sku: String!
  plu: String
  name: JSON!
  kind: ItemKind!
  baseUnit: BaseUnit!
  taxCodes: [String!]!
  groupId: UUID
  profile: String
  attributes: JSON!
  stockPolicy: StockPolicy!
  discountable: Boolean!
  active: Boolean!
  barcodes: [Barcode!]!
  modifierGroups: [ModifierGroup!]!
  "The item's tags in the order of their ids."
  tags: [ItemTag!]!
  price: StorePrice
  "On a parent, its options in order (spec/VARIANTS.md); empty otherwise."
  variantOptions: [VariantOption!]!
  "On a variant, its parent."
  parentId: UUID
  "On a variant, {option code: value code}."
  variantValues: JSON
  serialTracked: Boolean!
  "A kit's components (spec/STOCK.md, Kits), each taxed as the kit is; empty otherwise."
  kitComponents: [Component!]!
  version: Int64!
}

type Component

"A kit's or a recipe's component."
type Component {
  itemId: UUID!
  quantityMilli: Int64!
}

type VariantValue

type VariantValue {
  code: String!
  name: JSON!
}

type VariantOption

type VariantOption {
  code: String!
  name: JSON!
  values: [VariantValue!]!
}

enum ItemKindOf

enum ItemKindOf {
  PARENT
  VARIANT
  PLAIN
}

enum SerialState

enum SerialState {
  IN_STOCK
  SOLD
  WRITTEN_OFF
}

enum SerialEventKind

enum SerialEventKind {
  RECEIVED
  SOLD
  RETURNED
  VOIDED
  WRITTEN_OFF
}

type SerialEvent

type SerialEvent {
  event: SerialEventKind!
  at: DateTime!
  documentId: UUID
  receiptId: UUID
  overridden: Boolean!
  conflict: Boolean!
}

type SerialRecord

type SerialRecord {
  itemId: UUID!
  serial: String!
  state: SerialState!
  history: [SerialEvent!]!
}

enum TaxProviderKind

enum TaxProviderKind {
  AVALARA
  TAXJAR
}

enum TaxSyncState

enum TaxSyncState {
  NEVER
  OK
  FAILED
}

enum TaxSyncError

enum TaxSyncError {
  KEY_MISSING
  PROVIDER_UNREACHABLE
  PROVIDER_REFUSED
  BAD_ANSWER
}

type TaxProviderAddress

type TaxProviderAddress {
  line1: String!
  city: String!
  region: String!
  postalCode: String!
}

type TaxRateChange

type TaxRateChange {
  code: String!
  fromPpm: Int!
  toPpm: Int!
}

type TaxProviderStatus

type TaxProviderStatus {
  state: TaxSyncState!
  lastAttemptAt: DateTime
  lastSuccessAt: DateTime
  error: TaxSyncError
  changed: [TaxRateChange!]!
}

type TaxProvider

"spec/TAX.md, Rates from a provider: null provider is off."
type TaxProvider {
  provider: TaxProviderKind
  accountId: String
  keyName: String
  keyPresent: Boolean!
  sandbox: Boolean!
  address: TaxProviderAddress
  "{jurisdiction: tax code}"
  mapping: JSON!
  status: TaxProviderStatus!
}

type ModifierOption

type ModifierOption {
  id: String!
  name: JSON!
  priceDeltaMinor: Int64!
}

type ModifierGroup

type ModifierGroup {
  id: String!
  name: JSON!
  min: Int!
  max: Int!
  options: [ModifierOption!]!
}

type Promotion

type Promotion {
  id: UUID!
  name: JSON!
  active: Boolean!
  startsAt: DateTime
  endsAt: DateTime
  itemIds: [UUID!]!
  groupIds: [UUID!]!
  "spec/PROMOTIONS.md: {kind: percent, rate_ppm} or {kind: quantity_break, tiers}."
  rule: JSON!
  version: Int64!
}

type ItemPage

type ItemPage {
  items: [Item!]!
  next: String
}

enum LookupSource

enum LookupSource {
  BARCODE
  PACK_BARCODE
  VARIABLE_MEASURE
  PLU
  SKU
}

type Lookup

type Lookup {
  item: Item!
  source: LookupSource!
  quantityMilli: Int64
  priceMinor: Int64
}

type Group

type Group {
  id: UUID!
  parentId: UUID
  name: JSON!
}

type Tag

"A tag of the catalog (spec/SEARCH.md)."
type Tag {
  id: UUID!
  name: JSON!
  version: Int64!
}

enum TagSource

enum TagSource {
  MANUAL
  SUGGESTED
}

type ItemTag

type ItemTag {
  tagId: UUID!
  source: TagSource!
  "The accepted suggestion's confidence, 0 to 1000; null when manual."
  confidence: Int
}

enum SearchMatch

enum SearchMatch {
  EXACT
  SEMANTIC
}

type SearchHit

type SearchHit {
  item: Item!
  match: SearchMatch!
  "Available quantity on a store node; null at the hub."
  availableMilli: Int64
}

type SearchIndex

type SearchIndex {
  id: UUID!
  model: String!
  dimensions: Int!
  indexed: Int64!
  total: Int64!
  startedAt: DateTime!
  readyAt: DateTime
}

type SearchIndexStatus

type SearchIndexStatus {
  active: SearchIndex
  building: SearchIndex
}

type SearchResult

type SearchResult {
  items: [SearchHit!]!
  index: SearchIndex!
}

enum SearchProviderKind

enum SearchProviderKind {
  LOCAL
  OPENAI_COMPATIBLE
}

type SearchProvider

type SearchProvider {
  kind: SearchProviderKind!
  "From the environment beside the key (spec/SEARCH.md, Settings)"
  baseUrl: String
  model: String
  dimensions: Int
  maxInputTokens: Int
  "The key's name, or none; never the key."
  key: String
}

type SearchSuggestionSettings

type SearchSuggestionSettings {
  enabled: Boolean!
  neighbors: Int!
  minScore: Int!
  minConfidence: Int!
}

type SearchSettings

type SearchSettings {
  provider: SearchProvider!
  suggestions: SearchSuggestionSettings!
  keyPresent: Boolean!
  version: Int64!
}

enum SuggestionKind

enum SuggestionKind {
  TAG
  GROUP
}

enum SuggestionStatus

enum SuggestionStatus {
  OPEN
  ACCEPTED
  DISMISSED
}

type SuggestionResolver

type SuggestionResolver {
  id: UUID!
  name: String!
}

type Suggestion

"A tag or a group the node suggests for an item (spec/SEARCH.md, Suggestions)."
type Suggestion {
  id: UUID!
  itemId: UUID!
  itemName: JSON!
  kind: SuggestionKind!
  targetId: UUID!
  targetName: JSON!
  confidence: Int!
  status: SuggestionStatus!
  createdAt: DateTime!
  resolvedAt: DateTime
  resolvedBy: SuggestionResolver
}

type SuggestionPage

type SuggestionPage {
  items: [Suggestion!]!
  next: String
}

type Batch

type Batch {
  id: UUID!
  itemId: UUID!
  lot: String
  expiresOn: Date
  receivedAt: DateTime!
  unitCostMinor: Int64!
  costCurrency: String!
  balanceMilli: Int64!
  needsReview: Boolean!
  "The batch's own sale price; null sells at the store price (spec/PRICING.md)."
  priceMinor: Int64
}

type Stock

type Stock {
  itemId: UUID!
  baseUnit: BaseUnit!
  onHandMilli: Int64!
  heldMilli: Int64!
  availableMilli: Int64!
  batches: [Batch!]!
  "A kit's stock, from its components."
  kit: Boolean!
}

type Hold

type Hold {
  id: UUID!
  itemId: UUID!
  quantityMilli: Int64!
  workplaceId: UUID
  expiresAt: DateTime!
  "The price split of the quantity held, when batches carry prices."
  split: [PriceSplitLine!]!
}

enum DocumentKind

enum DocumentKind {
  RECEIVING
  WRITE_OFF
  INVENTORY
  PRICE_CHANGE
  TRANSFER_OUT
  TRANSFER_IN
  PURCHASE_ORDER
  SUPPLIER_RETURN
  REGRADE
  PRODUCTION
}

enum DocumentStatus

enum DocumentStatus {
  DRAFT
  POSTED
  CANCELED
}

enum ListOrder

"ASC oldest first, DESC newest first."
enum ListOrder {
  ASC
  DESC
}

type DocumentLine

type DocumentLine {
  lineNo: Int!
  itemId: UUID!
  quantityMilli: Int64
  countedMilli: Int64
  bookMilli: Int64
  unitCostMinor: Int64
  retailPriceMinor: Int64
  lot: String
  expiresOn: Date
  batchId: UUID
  reason: String
  "Price change: the new store price."
  priceMinor: Int64
  "Price change, after posting: the price it replaced."
  previousPriceMinor: Int64
  "A serial-tracked item's serials (spec/VARIANTS.md)."
  serials: [String!]!
  "Receiving: the sale price of the batch the line makes."
  batchPriceMinor: Int64
  "Re-grade: the item the quantity really is."
  toItemId: UUID
  "Transfer receiving: the transfer's line it receives."
  transferLineNo: Int
  "A posted transfer_out: the batches the line took."
  shipped: [ShippedBatch!]!
  "A posted transfer_in or re-grade: the batches the line made."
  made: [MovedBatch!]!
  shortageMilli: Int64
  surplusMilli: Int64
  shortageCostMinor: Int64
  surplusCostMinor: Int64
  "Purchase order: received so far and still due."
  receivedMilli: Int64
  dueMilli: Int64
  "Once posted: the line's cost."
  costMinor: Int64
}

type ShippedBatch

type ShippedBatch {
  batchId: UUID!
  quantityMilli: Int64!
  unitCostMinor: Int64!
  costCurrency: String!
  lot: String
  expiresOn: Date
}

type MovedBatch

type MovedBatch {
  batchId: UUID!
  fromBatchId: UUID
  quantityMilli: Int64!
  unitCostMinor: Int64!
}

type ProductionOutput

type ProductionOutput {
  itemId: UUID!
  quantityMilli: Int64!
  lot: String
  expiresOn: Date
  batchId: UUID
  unitCostMinor: Int64
}

enum DocumentOrigin

enum DocumentOrigin {
  EXPIRY_RULE
}

enum OrderState

enum OrderState {
  DRAFT
  SENT
  PARTLY_RECEIVED
  RECEIVED
  CLOSED
  CANCELED
}

enum TransferState

enum TransferState {
  DRAFT
  IN_TRANSIT
  RECEIVED
  CANCELED
}

enum TransferDirection

enum TransferDirection {
  OUTGOING
  INCOMING
}

type Document

type Document {
  id: UUID!
  kind: DocumentKind!
  status: DocumentStatus!
  number: Int64!
  comment: String
  createdAt: DateTime!
  createdBy: UUID!
  postedAt: DateTime
  postedBy: UUID
  lines: [DocumentLine!]!
  version: Int64!
  "Price change: why; null for other kinds."
  reason: PriceChangeReason
  "Made by the expiry run; null when a person made it."
  origin: DocumentOrigin
  supplierId: UUID
  expectedOn: Date
  orderState: OrderState
  purchaseOrderId: UUID
  receivingId: UUID
  fromStoreId: UUID
  toStoreId: UUID
  transferId: UUID
  transferState: TransferState
  recipeId: UUID
  plannedMilli: Int64
  output: ProductionOutput
  lossMilli: Int64
  yieldPpm: Int64
  costMinor: Int64
}

enum PriceChangeReason

enum PriceChangeReason {
  REPRICING
  MARKDOWN
}

type DocumentPage

type DocumentPage {
  items: [Document!]!
  next: String
}

enum ShiftStatus

enum ShiftStatus {
  OPEN
  CLOSED
}

type Shift

type Shift {
  id: UUID!
  workplaceId: UUID!
  cashierId: UUID!
  number: Int64!
  status: ShiftStatus!
  openedAt: DateTime!
  closedAt: DateTime
  openingCashMinor: Int64!
  currency: String!
  "The fiscal shift's Z report, once closed with one (spec/FISCAL.md)."
  fiscal: ShiftFiscal
}

type ShiftFiscal

type ShiftFiscal {
  shiftNumber: Int64!
  zNumber: Int64!
  zAt: DateTime!
  salesMinor: Int64!
  refundsMinor: Int64!
  taxes: [ReceiptTax!]!
  tenders: [FiscalTenderTotal!]!
}

type FiscalTenderTotal

type FiscalTenderTotal {
  tender: String!
  amountMinor: Int64!
}

enum FiscalBackendKind

enum FiscalBackendKind {
  DEVICE
  SIGNER
  SOFTWARE
  SERVICE
}

type ReceiptFiscal

"The fiscal document a back end gave a receipt (spec/FISCAL.md)."
type ReceiptFiscal {
  kind: FiscalBackendKind!
  driver: String!
  deviceSerial: String
  registrationNumber: String
  shiftNumber: Int64!
  documentNumber: Int64!
  sign: String!
  qr: String
  at: DateTime!
  extra: JSON!
}

type ShiftPage

type ShiftPage {
  items: [Shift!]!
  next: String
}

enum Tender

enum Tender {
  CASH
  CARD
  EBT_SNAP
  EBT_CASH
  GIFT_CARD
  "A preorder's online payment, captured at hand-over (spec/PAYMENTS_ONLINE.md)."
  ONLINE
  OTHER
}

type TenderTotal

type TenderTotal {
  tender: Tender!
  amountMinor: Int64!
}

type CashTotals

type CashTotals {
  openingMinor: Int64!
  depositsMinor: Int64!
  payoutsMinor: Int64!
  pickupsMinor: Int64!
  salesMinor: Int64!
  refundsMinor: Int64!
  expectedMinor: Int64!
}

type ShiftTotals

type ShiftTotals {
  saleCount: Int!
  refundCount: Int!
  voidCount: Int!
  salesMinor: Int64!
  refundsMinor: Int64!
  netSalesMinor: Int64!
  taxMinor: Int64!
  discountsMinor: Int64!
  tipsMinor: Int64!
  "The sales' service charges without their tax, less what refunds took back; never in tipsMinor."
  serviceChargesMinor: Int64!
  payments: [TenderTotal!]!
  cash: CashTotals!
  "The cashier the totals are for; null for the whole shift."
  cashierId: UUID
  "Per tax code, the base and the tax of sales minus refunds."
  taxes: [ReceiptTax!]!
  "The store's open and billed restaurant checks when the totals were read (spec/RESTAURANT.md)."
  openChecks: OpenChecks!
}

enum ReceiptKind

enum ReceiptKind {
  SALE
  REFUND
  VOID
}

type ReceiptLineBatch

type ReceiptLineBatch {
  batchId: UUID!
  quantityMilli: Int64!
  "A kit line's component."
  itemId: UUID
}

type ReceiptLine

type ReceiptLine {
  lineNo: Int!
  itemId: UUID!
  name: String!
  quantityMilli: Int64!
  baseUnit: BaseUnit!
  unitPriceMinor: Int64!
  gross: Int64!
  lineDiscount: Int64!
  receiptDiscount: Int64!
  net: Int64!
  tax: Int64!
  "The container deposit (spec/TAX.md); 0 without one."
  deposit: Int64!
  "What SNAP paid of the line; 0 without SNAP."
  snap: Int64!
  batches: [ReceiptLineBatch!]!
  promotionId: UUID
  modifiers: [ReceiptLineModifier!]!
  "A serial-tracked item's serials (spec/VARIANTS.md)."
  serials: [String!]!
  "The lot a GS1 code named (spec/BARCODES.md)."
  lot: String
  "The marking code a GS1 code carried, as scanned."
  markingCode: String
  "The employee who sold the line (spec/REPORTS.md)."
  sellerId: UUID
  "The loyalty steps' parts of the line's discounts (spec/LOYALTY.md)."
  stampDiscount: Int64!
  cardDiscount: Int64!
  voucherDiscount: Int64!
  pointsSpent: Int64!
  pointsEarned: Int64!
  "The employee bonus fixed at the sale."
  bonusMinor: Int64!
  bonusEmployeeId: UUID
  bonusRuleId: UUID
  "The line's share of the partner's commission."
  commissionMinor: Int64!
  giftCardNumberMasked: String
  "The restaurant check's line it paid; null without a check."
  checkLineNo: Int
  "The line's share of each service charge, with the tax on that share (not in tax)."
  serviceCharges: [ServiceChargeShare!]!
}

type ReceiptLineModifier

type ReceiptLineModifier {
  groupId: String!
  optionId: String!
  name: String!
  priceDeltaMinor: Int64!
}

enum OrderType

enum OrderType {
  DINE_IN
  TAKE_OUT
  DELIVERY
}

type ReceiptTax

type ReceiptTax {
  code: String!
  base: Int64!
  amount: Int64!
}

type Payment

type Payment {
  id: UUID!
  tender: Tender!
  amountMinor: Int64!
  provider: String
  providerRef: String
  cardBrand: String
  cardLast4: String
  authorizationCode: String
}

type Receipt

type Receipt {
  id: UUID!
  number: Int64!
  kind: ReceiptKind!
  originalReceiptId: UUID
  shiftId: UUID!
  workplaceId: UUID!
  cashierId: UUID!
  currency: String!
  createdAt: DateTime!
  lines: [ReceiptLine!]!
  taxes: [ReceiptTax!]!
  grossTotal: Int64!
  discountTotal: Int64!
  netTotal: Int64!
  taxTotal: Int64!
  total: Int64!
  cashDue: Int64
  payments: [Payment!]!
  changeMinor: Int64!
  needsReview: Boolean!
  orderType: OrderType
  tipMinor: Int64!
  depositTotal: Int64!
  snapTotal: Int64!
  "FSA- and HSA-eligible lines' net and tax (spec/PHARMACY.md)."
  healthcareTotal: Int64!
  rxTotal: Int64!
  orderLabel: String
  deliveries: [ReceiptDelivery!]!
  "Closed offline: the serials a sale online would have refused."
  serialConflicts: [String!]!
  "The fiscal document; null without a fiscal back end (spec/FISCAL.md)."
  fiscal: ReceiptFiscal
  customerId: UUID
  "What the loyalty steps did (spec/LOYALTY.md)."
  loyalty: ReceiptLoyalty
  "The partner and rate at the sale."
  partner: ReceiptPartner
  "The restaurant check it paid lines of (spec/RESTAURANT.md); null otherwise."
  check: ReceiptCheck
  "Every service charge it carries, removed ones included (spec/API.md, Service charges)."
  serviceCharges: [ReceiptServiceCharge!]!
  "The charges that apply, inside total."
  serviceChargeTotal: Int64!
}

type ReceiptLoyalty

type ReceiptLoyalty {
  cardNumberMasked: String
  unresolvedCard: Boolean!
  pointsSpent: Int64!
  pointsValueMinor: Int64!
  pointsEarned: Int64!
  stamps: [ReceiptStamps!]!
  voucherId: UUID
  voucherCode: String
}

type ReceiptStamps

type ReceiptStamps {
  programId: UUID!
  earned: Int64!
  rewards: Int64!
  redeemed: Int64!
}

type ReceiptPartner

type ReceiptPartner {
  partnerId: UUID!
  code: String!
  commissionPpm: Int!
  commissionMinor: Int64!
}

type ReceiptPage

type ReceiptPage {
  items: [Receipt!]!
  next: String
}

enum Scope

enum Scope {
  NETWORK
  STORE
  WORKPLACE
}

type Assignment

type Assignment {
  id: UUID!
  role: String!
  scope: Scope!
  storeId: UUID
  workplaceId: UUID
}

type Employee

type Employee {
  id: UUID!
  login: String!
  displayName: String!
  language: String!
  active: Boolean!
  assignments: [Assignment!]!
  version: Int64!
}

type EmployeePage

type EmployeePage {
  items: [Employee!]!
  next: String
}

type Role

type Role {
  code: String!
  name: JSON!
  scope: Scope!
  permissions: [String!]!
  limits: JSON!
  builtin: Boolean!
}

enum SalesGroupBy

enum SalesGroupBy {
  DAY
  EMPLOYEE
  ITEM
  GROUP
  TENDER
}

type SalesReportRow

"spec/REPORTS.md, Sales: rows by tender carry only amount."
type SalesReportRow {
  "The local date, employee id, item id, group id (null without a group) or tender."
  key: String
  name: String
  sales: Int
  refunds: Int
  quantityMilli: Int64
  gross: Int64
  discounts: Int64
  net: Int64
  tax: Int64
  revenue: Int64
  cost: Int64
  margin: Int64
  marginPpm: Int64
  tips: Int64
  "The receipts' service charges without their tax, by day or employee."
  serviceCharges: Int64
  amount: Int64
}

type SalesTotals

type SalesTotals {
  sales: Int
  refunds: Int
  quantityMilli: Int64
  gross: Int64
  discounts: Int64
  net: Int64
  tax: Int64
  revenue: Int64
  cost: Int64
  margin: Int64
  marginPpm: Int64
  tips: Int64
  "The receipts' service charges without their tax, by day or employee."
  serviceCharges: Int64
  amount: Int64
}

type SalesReport

type SalesReport {
  groupBy: SalesGroupBy!
  currency: String!
  rows: [SalesReportRow!]!
  totals: SalesTotals!
}

type StockReportRow

type StockReportRow {
  itemId: UUID!
  sku: String!
  name: JSON!
  baseUnit: BaseUnit!
  groupId: UUID
  onHandMilli: Int64!
  heldMilli: Int64!
  availableMilli: Int64!
  valueMinor: Int64!
  batches: Int!
  nearestExpiry: Date
}

type StockReportPage

type StockReportPage {
  items: [StockReportRow!]!
  next: String
  totalValueMinor: Int64!
}

enum MovementKind

enum MovementKind {
  RECEIVING
  SALE
  REFUND
  VOID
  WRITE_OFF
  INVENTORY_SURPLUS
  INVENTORY_SHORTAGE
  TRANSFER_OUT
  TRANSFER_IN
  SUPPLIER_RETURN
  REGRADE_OUT
  REGRADE_IN
  PRODUCTION_OUT
  PRODUCTION_IN
}

type Movement

type Movement {
  at: DateTime!
  itemId: UUID!
  batchId: UUID!
  quantityMilli: Int64!
  kind: MovementKind!
  sourceId: UUID!
  sourceNumber: Int64!
  needsReview: Boolean!
}

type MovementPage

type MovementPage {
  items: [Movement!]!
  next: String
}

input PriceProposalInput

input PriceProposalInput {
  itemIds: [UUID!]
  groupId: UUID
  markupPpm: Int!
  roundToMinor: Int! = 1
  endingMinor: Int
  endingBase: Int
}

type PriceProposal

type PriceProposal {
  itemId: UUID!
  costMinor: Int64
  priceMinor: Int64
  proposedMinor: Int64
}

enum TunnelState

enum TunnelState {
  OFF
  UP
  DOWN
}

type StoreNetworkState

type StoreNetworkState {
  enabled: Boolean!
  address: String
  hubAddress: String
  endpoint: String
  tunnelState: TunnelState!
  lastHandshakeAt: DateTime
  publicKey: String
  devices: [DevicePeer!]!
}

type DevicePeer

type DevicePeer {
  deviceId: UUID!
  address: String!
  publicKey: String!
  lastHandshakeAt: DateTime
}

type Transfer

"A transfer with both stores' documents."
type Transfer {
  id: UUID!
  fromStoreId: UUID!
  toStoreId: UUID!
  state: TransferState!
  transfer: Document!
  receiving: Document
}

type TransferPage

type TransferPage {
  items: [Transfer!]!
  next: String
}

type InTransitLine

type InTransitLine {
  transferId: UUID!
  number: Int64!
  fromStoreId: UUID!
  toStoreId: UUID!
  shippedAt: DateTime!
  lineNo: Int!
  itemId: UUID!
  quantityMilli: Int64!
  costMinor: Int64!
  costCurrency: String!
}

type Supplier

type Supplier {
  id: UUID!
  code: String!
  name: String!
  comment: String
  active: Boolean!
  version: Int64!
}

type Recipe

type Recipe {
  id: UUID!
  productItemId: UUID!
  yieldMilli: Int64!
  components: [Component!]!
  active: Boolean!
  version: Int64!
}

type PriceSplitLine

type PriceSplitLine {
  unitPriceMinor: Int64!
  quantityMilli: Int64!
  batches: [ReceiptLineBatch!]!
}

type PriceSplit

type PriceSplit {
  itemId: UUID!
  lines: [PriceSplitLine!]!
}

type ExpiryStep

type ExpiryStep {
  daysBefore: Int!
  ratePpm: Int!
}

type ExpiryRule

type ExpiryRule {
  id: UUID!
  itemIds: [UUID!]!
  groupIds: [UUID!]!
  markdowns: [ExpiryStep!]!
  autoPost: Boolean!
  writeOffExpired: Boolean!
  roundToMinor: Int64!
  endingMinor: Int64
  endingBase: Int64
  active: Boolean!
  version: Int64!
}

enum LoyaltyKind

enum LoyaltyKind {
  DISCOUNT
  BONUS
  STAMPS
}

type EarnRate

type EarnRate {
  "Null for every group not listed."
  groupId: UUID
  ratePpm: Int!
}

type PointValue

type PointValue {
  "Null for every other store."
  storeId: UUID
  valueMinor: Int64!
}

type LoyaltyProgram

type LoyaltyProgram {
  id: UUID!
  name: JSON!
  kind: LoyaltyKind!
  active: Boolean!
  ratePpm: Int
  earn: [EarnRate!]!
  pointValues: [PointValue!]!
  maxSpendPpm: Int
  stampsPerReward: Int
  itemIds: [UUID!]!
  groupIds: [UUID!]!
  excludedGroupIds: [UUID!]!
  version: Int64!
}

type CustomerCard

type CustomerCard {
  programId: UUID!
  numberMasked: String!
}

enum ConsentSource

enum ConsentSource {
  REGISTER
  BACK_OFFICE
  CUSTOMER_APP
  IMPORT
}

type CustomerConsent

type CustomerConsent {
  at: DateTime!
  source: ConsentSource!
  termsVersion: String!
}

type CustomerMarketing

type CustomerMarketing {
  allowed: Boolean!
  at: DateTime
}

type Customer

"A customer, masked: the phone and e-mail are never read back (spec/LOYALTY.md)."
type Customer {
  id: UUID!
  name: String
  phoneMasked: String
  emailMasked: String
  cards: [CustomerCard!]!
  consent: CustomerConsent!
  marketing: CustomerMarketing!
  deletedAt: DateTime
  createdAt: DateTime!
  version: Int64!
}

type CustomerPage

type CustomerPage {
  items: [Customer!]!
  next: String
}

type PointsBalance

type PointsBalance {
  programId: UUID!
  balance: Int64!
  available: Int64!
  pointValueMinor: Int64!
}

type StampsBalance

type StampsBalance {
  programId: UUID!
  balance: Int64!
  available: Int64!
  stampsPerReward: Int!
}

type CustomerBalances

type CustomerBalances {
  customerId: UUID!
  points: [PointsBalance!]!
  stamps: [StampsBalance!]!
}

enum AccountKind

enum AccountKind {
  POINTS
  STAMPS
  GIFT_CARD
  VOUCHER
}

enum LedgerEntryKind

enum LedgerEntryKind {
  POINTS_EARNED
  POINTS_SPENT
  POINTS_EARNED_REVERSED
  POINTS_SPENT_RETURNED
  STAMPS_EARNED
  STAMPS_REDEEMED
  STAMPS_REVERSED
  GIFT_CARD_LOADED
  GIFT_CARD_SPENT
  GIFT_CARD_REFUNDED
  GIFT_CARD_UNLOADED
  VOUCHER_USED
  VOUCHER_RETURNED
  ADJUSTMENT
}

type LedgerEntry

type LedgerEntry {
  id: UUID!
  accountKind: AccountKind!
  accountId: UUID!
  programId: UUID
  kind: LedgerEntryKind!
  amount: Int64!
  receiptId: UUID
  holdId: UUID
  storeId: UUID!
  reason: String
  at: DateTime!
}

type LedgerPage

type LedgerPage {
  items: [LedgerEntry!]!
  next: String
}

enum BalanceHoldStatus

enum BalanceHoldStatus {
  HELD
  COMMITTED
  SETTLED
  EXPIRED
  RELEASED
  ORPHANED
}

type BalanceHold

type BalanceHold {
  id: UUID!
  accountKind: AccountKind!
  accountId: UUID!
  programId: UUID
  amount: Int64!
  status: BalanceHoldStatus!
  expiresAt: DateTime
  storeId: UUID!
  receiptId: UUID
  createdAt: DateTime!
  committedAt: DateTime
}

type BalanceHoldPage

type BalanceHoldPage {
  items: [BalanceHold!]!
  next: String
}

type Voucher

"A voucher or coupon; rule is PROMOTIONS.md's shape or {kind: amount, amount_minor} (spec/LOYALTY.md)."
type Voucher {
  id: UUID!
  code: String!
  name: JSON
  rule: JSON!
  itemIds: [UUID!]!
  groupIds: [UUID!]!
  minSpendMinor: Int64!
  startsAt: DateTime
  endsAt: DateTime
  "Null: a coupon, used any number of times."
  maxUses: Int
  customerId: UUID
  uses: Int64!
  active: Boolean!
  version: Int64!
}

type VoucherPage

type VoucherPage {
  items: [Voucher!]!
  next: String
}

enum VoucherUnusable

enum VoucherUnusable {
  INACTIVE
  NOT_STARTED
  ENDED
  USED_UP
  OTHER_CUSTOMER
}

type VoucherCheck

type VoucherCheck {
  voucher: Voucher!
  remainingUses: Int64
  usable: Boolean!
  reason: VoucherUnusable
}

enum GiftCardStatus

enum GiftCardStatus {
  ACTIVE
  BLOCKED
}

type GiftCard

"A gift card: only the number's mask; the owner finds a card by the number's keyed hash, which no answer carries (spec/LOYALTY.md, Gift card numbers)."
type GiftCard {
  id: UUID!
  numberMasked: String!
  currency: String!
  status: GiftCardStatus!
  hasPin: Boolean!
  "The issue or import that made the card."
  batchId: UUID!
  "The owner's; null in a replicated copy."
  balanceMinor: Int64
  availableMinor: Int64
  createdAt: DateTime!
  version: Int64!
}

type GiftCardPage

type GiftCardPage {
  items: [GiftCard!]!
  next: String
}

type Partner

type Partner {
  id: UUID!
  name: String!
  codes: [String!]!
  commissionPpm: Int!
  itemIds: [UUID!]!
  groupIds: [UUID!]!
  startsAt: DateTime
  endsAt: DateTime
  active: Boolean!
  version: Int64!
}

type BonusRule

type BonusRule {
  id: UUID!
  name: String!
  ratePpm: Int!
  itemIds: [UUID!]!
  groupIds: [UUID!]!
  startsOn: Date
  endsOn: Date
  active: Boolean!
  version: Int64!
}

type BonusReportRow

type BonusReportRow {
  "At the hub."
  storeId: UUID
  employeeId: UUID!
  lines: Int64!
  revenueMinor: Int64!
  bonusMinor: Int64!
}

type BonusTotals

type BonusTotals {
  lines: Int64!
  revenueMinor: Int64!
  bonusMinor: Int64!
}

type BonusReport

type BonusReport {
  from: DateTime!
  to: DateTime!
  rows: [BonusReportRow!]!
  totals: BonusTotals!
}

type PartnerReportRow

type PartnerReportRow {
  "At the hub."
  storeId: UUID
  partnerId: UUID!
  sales: Int64!
  refunds: Int64!
  revenueMinor: Int64!
  costMinor: Int64!
  marginMinor: Int64!
  commissionMinor: Int64!
  marginAfterCommissionMinor: Int64!
}

type PartnerTotals

type PartnerTotals {
  sales: Int64!
  refunds: Int64!
  revenueMinor: Int64!
  costMinor: Int64!
  marginMinor: Int64!
  commissionMinor: Int64!
  marginAfterCommissionMinor: Int64!
}

type PartnerReport

type PartnerReport {
  from: DateTime!
  to: DateTime!
  rows: [PartnerReportRow!]!
  totals: PartnerTotals!
}

type StoreDirectoryEntry

"What a store of a chain knows of another store (spec/SYNC.md)."
type StoreDirectoryEntry {
  id: UUID!
  code: String!
  name: JSON!
}

enum TableShape

enum TableShape {
  RECT
  CIRCLE
}

type Area

"A room of the floor (spec/RESTAURANT.md, The floor)."
type Area {
  id: UUID!
  name: JSON!
  position: Int!
  version: Int64!
}

type Table

"A table on its area's grid of whole units."
type Table {
  id: UUID!
  areaId: UUID!
  name: String!
  shape: TableShape!
  x: Int!
  y: Int!
  w: Int!
  h: Int!
  "0, 90, 180 or 270 degrees, clockwise."
  rotation: Int!
  seats: Int!
  "The QR token; null unless the caller holds floor.manage."
  qrToken: String
  version: Int64!
}

type FloorTable

type FloorTable {
  table: Table!
  "The table's open and billed checks, oldest first."
  checks: [CheckSummary!]!
  guestOrdersPending: Int!
  "Past the store's first three tables by id without the restaurant module: no new check opens on it (spec/LICENSE.md, The restaurant's three places)."
  locked: Boolean!
}

type Floor

type Floor {
  areas: [Area!]!
  tables: [FloorTable!]!
  "Open and billed tabs, oldest first."
  tabs: [CheckSummary!]!
}

enum CheckKind

enum CheckKind {
  TABLE
  TAB
}

enum CheckState

enum CheckState {
  OPEN
  BILLED
  CLOSED
  CANCELED
}

enum CheckListState

enum CheckListState {
  "Open and billed."
  ACTIVE
  OPEN
  BILLED
  CLOSED
  CANCELED
}

enum CheckLineState

enum CheckLineState {
  UNSENT
  SENT
  VOIDED
}

enum HoldState

enum HoldState {
  HELD
  LAPSED
  NONE
}

enum DiscountKind

enum DiscountKind {
  PERCENT
  AMOUNT
}

type LineDiscount

type LineDiscount {
  kind: DiscountKind!
  ratePpm: Int
  amount: Int64
}

type CheckSummary

"A check without its lines."
type CheckSummary {
  id: UUID!
  number: Int64!
  kind: CheckKind!
  tableId: UUID
  tableName: String
  "A tab's name."
  name: String
  waiterId: UUID!
  "The waiter's name as the staff list has it, as a kitchen ticket prints it."
  waiterName: String!
  covers: Int!
  state: CheckState!
  totalMinor: Int64!
  openedAt: DateTime!
  version: Int64!
}

type CheckLineVoid

type CheckLineVoid {
  reason: String!
  by: UUID!
  approvedBy: UUID
  at: DateTime!
}

type CheckLine

"A sale line with its seat and course."
type CheckLine {
  lineNo: Int!
  itemId: UUID!
  name: String!
  quantityMilli: Int64!
  unitPriceMinor: Int64!
  modifiers: [ReceiptLineModifier!]!
  discounts: [LineDiscount!]!
  "0 for a shared line."
  seat: Int!
  course: Int!
  state: CheckLineState!
  sentAt: DateTime
  voided: CheckLineVoid
  receiptId: UUID
  holdState: HoldState!
  sellerId: UUID
  guestOrderId: UUID
  addedBy: UUID!
  addedAt: DateTime!
}

type CheckLineTotals

type CheckLineTotals {
  lineNo: Int!
  gross: Int64!
  lineDiscount: Int64!
  receiptDiscount: Int64!
  net: Int64!
  tax: Int64!
  serviceCharges: [ServiceChargeShare!]!
}

type CheckTotals

"What one sale receipt of the check's lines that are not voided would be."
type CheckTotals {
  lines: [CheckLineTotals!]!
  taxes: [ReceiptTax!]!
  grossTotal: Int64!
  discountTotal: Int64!
  netTotal: Int64!
  depositTotal: Int64
  taxTotal: Int64!
  total: Int64!
  pointsEarned: Int64
  "The service charges that apply, judged on the check's covers and order type."
  serviceCharges: [CheckTotalsServiceCharge!]!
  serviceChargeTotal: Int64!
}

type Check

"An open order on a table, or a tab (spec/RESTAURANT.md, Checks)."
type Check {
  id: UUID!
  number: Int64!
  kind: CheckKind!
  tableId: UUID
  tableName: String
  areaId: UUID
  name: String
  waiterId: UUID!
  covers: Int!
  state: CheckState!
  orderType: OrderType!
  heldCourses: [Int!]!
  customerId: UUID
  cardNumberMasked: String
  partnerCode: String
  "Percentages only."
  receiptDiscounts: [LineDiscount!]!
  "The store's service charges it carries, each with how it came to."
  serviceCharges: [CheckServiceCharge!]!
  lines: [CheckLine!]!
  totals: CheckTotals!
  paidMinor: Int64!
  receiptIds: [UUID!]!
  prebillCount: Int!
  openedAt: DateTime!
  billedAt: DateTime
  closedAt: DateTime
  mergedInto: UUID
  version: Int64!
}

type CheckPage

type CheckPage {
  items: [Check!]!
  next: String
}

enum CheckConflictReasonCode

enum CheckConflictReasonCode {
  CHECK_CLOSED
  CHECK_CANCELED
  LINE_MISSING
  LINE_VOIDED
  LINE_PAID
  LINE_CHANGED
  "The charges that apply on the check now are not those the receipt applied."
  SERVICE_CHARGES_CHANGED
}

type CheckConflictReason

type CheckConflictReason {
  lineNo: Int
  reason: CheckConflictReasonCode!
}

enum CheckConflictResolution

enum CheckConflictResolution {
  APPLY
  DISMISS
}

type CheckConflict

"An offline receipt of a check that did not apply (spec/RESTAURANT.md, Offline)."
type CheckConflict {
  id: UUID!
  checkId: UUID!
  receiptId: UUID!
  reasons: [CheckConflictReason!]!
  createdAt: DateTime!
  resolution: CheckConflictResolution
  resolvedBy: UUID
  resolvedAt: DateTime
  version: Int64!
}

type CheckConflictPage

type CheckConflictPage {
  items: [CheckConflict!]!
  next: String
}

enum GuestOrderState

enum GuestOrderState {
  PENDING
  CONFIRMED
  REFUSED
  EXPIRED
}

type GuestOrderLine

type GuestOrderLine {
  itemId: UUID!
  name: String!
  quantityMilli: Int64!
  modifiers: [String!]!
  unitPriceMinor: Int64!
  amountMinor: Int64!
}

type GuestOrder

"An order a guest sent from a table's menu, waiting for a person."
type GuestOrder {
  id: UUID!
  tableId: UUID!
  tableName: String!
  state: GuestOrderState!
  lines: [GuestOrderLine!]!
  estimateMinor: Int64!
  createdAt: DateTime!
  checkId: UUID
  "A refusal's reason, which the guest sees."
  reason: String
  resolvedBy: UUID
  resolvedAt: DateTime
  version: Int64!
}

type GuestOrderPage

type GuestOrderPage {
  items: [GuestOrder!]!
  next: String
}

enum DietTag

enum DietTag {
  VEGETARIAN
  VEGAN
  HALAL
  KOSHER
  GLUTEN_FREE
  LACTOSE_FREE
  SPICY
}

enum Allergen

enum Allergen {
  CELERY
  CRUSTACEANS
  EGGS
  FISH
  GLUTEN
  LUPIN
  MILK
  MOLLUSKS
  MUSTARD
  PEANUTS
  SESAME
  SOY
  SULFITES
  TREE_NUTS
  WHEAT
}

type MenuEntry

type MenuEntry {
  itemId: UUID!
  description: JSON!
  photoId: UUID
  diet: [DietTag!]!
  allergens: [Allergen!]!
}

type MenuSection

type MenuSection {
  id: String!
  name: JSON!
  entries: [MenuEntry!]!
}

type Menu

"A guest menu (spec/RESTAURANT.md, The guest menu)."
type Menu {
  id: UUID!
  name: JSON!
  position: Int!
  active: Boolean!
  "The areas it is shown in; empty for every area."
  areaIds: [UUID!]!
  sections: [MenuSection!]!
  version: Int64!
}

enum MenuFont

enum MenuFont {
  INTER
  NOTO_SERIF
  NOTO_SANS_ARMENIAN
}

enum MenuLayout

enum MenuLayout {
  LIST
  GRID
}

type MenuTheme

"The guest menu's look; its colors reach WCAG 2.2 contrast."
type MenuTheme {
  logoPhotoId: UUID
  background: String!
  text: String!
  secondaryText: String!
  accent: String!
  onAccent: String!
  font: MenuFont!
  layout: MenuLayout!
  "Codes of the product's languages; the first is the default."
  languages: [String!]!
  version: Int64!
}

type MenuPhoto

type MenuPhoto {
  id: UUID!
  contentType: String!
  bytes: Int!
  width: Int!
  height: Int!
  etag: String!
  createdAt: DateTime!
}

type StopListEntry

type StopListEntry {
  itemId: UUID!
  "When it lifts by itself; null until taken off."
  until: DateTime
  reason: String
  by: UUID!
  at: DateTime!
}

type GuestListener

type GuestListener {
  enabled: Boolean!
  "The origin printed in the tables' QR codes."
  origin: String
}

type OpenChecks

type OpenChecks {
  count: Int!
  totalMinor: Int64!
}

enum SplitKind

enum SplitKind {
  NONE
  ITEMS
  SEATS
  EQUAL
}

type ReceiptCheckSplit

type ReceiptCheckSplit {
  kind: SplitKind!
  seat: Int
  parts: Int
}

type ReceiptCheck

"The check a receipt paid lines of."
type ReceiptCheck {
  checkId: UUID!
  checkNumber: Int64!
  tableId: UUID
  tableName: String
  areaId: UUID
  waiterId: UUID!
  covers: Int!
  openedAt: DateTime!
  split: ReceiptCheckSplit!
  "This receipt paid the check's last line."
  closing: Boolean!
  "An offline receipt that did not apply to its check."
  conflict: Boolean!
  "The check's version after this receipt."
  version: Int64
}

enum RestaurantGroupBy

enum RestaurantGroupBy {
  DAY
  WAITER
  TABLE
  AREA
}

type RestaurantReportRow

type RestaurantReportRow {
  "The day, waiter, table or area; null for a tab by table or area."
  key: String
  checks: Int!
  covers: Int!
  revenue: Int64!
  tips: Int64!
  "Service charges without their tax, less refunds' parts."
  serviceCharges: Int64!
  tableMinutes: Int64!
  averageCheck: Int64
  averageCover: Int64
  averageMinutes: Int64
}

type RestaurantMetrics

type RestaurantMetrics {
  checks: Int!
  covers: Int!
  revenue: Int64!
  tips: Int64!
  "Service charges without their tax, less refunds' parts."
  serviceCharges: Int64!
  tableMinutes: Int64!
  averageCheck: Int64
  averageCover: Int64
  averageMinutes: Int64
}

type RestaurantReport

type RestaurantReport {
  groupBy: RestaurantGroupBy!
  currency: String!
  rows: [RestaurantReportRow!]!
  totals: RestaurantMetrics!
}

enum ServiceChargeGroupBy

enum ServiceChargeGroupBy {
  DAY
  EMPLOYEE
  CHARGE
}

type ServiceChargeMetrics

type ServiceChargeMetrics {
  sales: Int!
  refunds: Int!
  "Charges removed by hand on the sales."
  removed: Int!
  "Without their tax."
  amountMinor: Int64!
  taxMinor: Int64!
  toStaffMinor: Int64!
  houseMinor: Int64!
}

type ServiceChargeReportRow

type ServiceChargeReportRow {
  "The local date, the employee id or the charge's id."
  key: String!
  sales: Int!
  refunds: Int!
  removed: Int!
  amountMinor: Int64!
  taxMinor: Int64!
  toStaffMinor: Int64!
  houseMinor: Int64!
}

type ServiceChargeReport

"spec/REPORTS.md, Service charges."
type ServiceChargeReport {
  groupBy: ServiceChargeGroupBy!
  currency: String!
  rows: [ServiceChargeReportRow!]!
  totals: ServiceChargeMetrics!
}

enum UpdateState

"A store's update state (spec/RELEASES.md, The states)."
enum UpdateState {
  IDLE
  DOWNLOADING
  WAITING_WINDOW
  APPLYING
  VERIFYING
  DONE
  FAILED
  ROLLED_BACK
  MANUAL
}

enum UpdateStep

"The step a failed or rolled back update stopped at."
enum UpdateStep {
  DOWNLOAD
  VERIFY
  BACKUP
  REHEARSE
  LOAD
  MIGRATE
  START
  CHECK
}

enum UpdateAction

enum UpdateAction {
  UPDATE
  ROLLBACK
}

enum Healthz

"The updater's last probe of GET /healthz."
enum Healthz {
  OK
  FAILING
}

enum UpdaterKind

enum UpdaterKind {
  COMPOSE
  NONE
}

enum ReleasePlatform

enum ReleasePlatform {
  LINUX_X64
  LINUX_ARM64
  WINDOWS_X64
  MACOS_UNIVERSAL
}

type UpdateWindow

"Local wall-clock hours, from inclusive to exclusive (spec/RELEASES.md, The window)."
type UpdateWindow {
  from: String!
  to: String!
}

enum IncompatibleReason

enum IncompatibleReason {
  NODE_TOO_OLD
  NODE_TOO_NEW
}

type Compatibility

"Why the hub refuses this node (spec/RELEASES.md, Compatibility)."
type Compatibility {
  reason: IncompatibleReason!
  hubVersion: String!
  "The hub's effective minimum."
  minNodeVersion: String!
}

type UpdateTarget

"The store's update target as the hub sent it."
type UpdateTarget {
  release: String!
  action: UpdateAction!
  "In the store's time zone; null for at once."
  window: UpdateWindow
  "Null for a store's own target (spec/RELEASES.md, A store with no hub)."
  rolloutId: UUID
  orderedAt: DateTime!
  "When a person last cleared the store's held apps for this release (spec/RELEASES.md, One device first)."
  appsClearedAt: DateTime
  version: Int64!
  "The signed manifest document, as JSON."
  manifest: JSON!
}

type StoreUpdate

"This store's update, as GET /v1/update answers."
type StoreUpdate {
  runningVersion: String!
  previousVersion: String
  target: UpdateTarget
  state: UpdateState!
  stateSince: DateTime
  step: UpdateStep
  error: String
  updater: UpdaterKind!
  healthz: Healthz
  "The hub's refusal while the exchange is refused; null otherwise."
  compatibility: Compatibility
  "The pre-update archive of the target's attempt (spec/BACKUP.md, Before an update); null before it."
  backup: UpdateBackup
  "The rehearsal of the target's migrations (spec/RELEASES.md, Rehearsal); null before it."
  rehearsal: UpdateRehearsal
  backgroundTasks: [BackgroundTask!]!
  "The store's apps by version (spec/RELEASES.md, Reporting)."
  apps: [AppVersionCount!]!
  "The offers of the release the node runs (spec/RELEASES.md, One device first)."
  appOffers: [AppOffer!]!
}

enum UpdateBackup

"skipped comes only from an updater before 1.1."
enum UpdateBackup {
  TAKEN
  FAILED
  SKIPPED
}

enum RehearsalOutcome

enum RehearsalOutcome {
  PASSED
  FAILED
  SKIPPED
}

enum RehearsalSkipReason

enum RehearsalSkipReason {
  TOO_LARGE
  DISK
  NOT_PERMITTED
}

type UpdateRehearsal

type UpdateRehearsal {
  outcome: RehearsalOutcome!
  "Why it was skipped; null otherwise."
  reason: RehearsalSkipReason
}

enum BackgroundTaskState

enum BackgroundTaskState {
  PENDING
  RUNNING
  DONE
  FAILED
}

type BackgroundTask

"A long data change run after the node starts (spec/RELEASES.md, Migrations)."
type BackgroundTask {
  id: String!
  release: String!
  state: BackgroundTaskState!
  done: Int64!
  total: Int64
  startedAt: DateTime
  finishedAt: DateTime
  "A short code; null unless failed."
  error: String
}

enum AppId

"An app that updates itself from its node (spec/RELEASES.md, The apps)."
enum AppId {
  CASHIER
  MANAGER
  GATEWAY
}

type AppVersionCount

"Counts only, never a device (spec/RELEASES.md, Reporting)."
type AppVersionCount {
  app: AppId!
  platform: ReleasePlatform!
  version: String!
  devices: Int!
  failed: Int!
  silent: Int!
}

enum AppOfferState

enum AppOfferState {
  OFFERED
  INSTALLED
  PROVEN
  HELD
}

type AppOffer

"The offer of the release the node runs to one app and platform (spec/RELEASES.md, One device first)."
type AppOffer {
  app: AppId!
  platform: ReleasePlatform!
  release: String!
  state: AppOfferState!
  since: DateTime!
}

enum ReleaseStatus

enum ReleaseStatus {
  DOWNLOADING
  VERIFIED
  FAILED
}

enum ReleaseSource

enum ReleaseSource {
  DOWNLOAD
  BUNDLE
}

type ReleaseArtifactSummary

"An artifact of a release as the node lists it, its values as the signed manifest writes them: tokens a later release may add to (spec/RELEASES.md, Values a later release adds)."
type ReleaseArtifactSummary {
  name: String!
  "image, installer, binary or archive; a later release may add a value."
  kind: String!
  "node, console, updater, cashier, manager, gateway or deploy; a later release may add a value."
  component: String!
  "linux-x64, linux-arm64, windows-x64, macos-universal, or any for the deployment kit; a later release may add a value."
  platform: String!
  "hub or store; a later release may add a value."
  roles: [String!]!
  size: Int64!
  sha256: String!
}

type Release

"A release a hub, or a store with no hub, holds (spec/RELEASES.md, The hub's releases, A store with no hub)."
type Release {
  id: UUID!
  version: String!
  status: ReleaseStatus!
  source: ReleaseSource!
  publishedAt: DateTime!
  minNodeVersion: String!
  contractsCommit: String!
  keyId: String!
  artifacts: [ReleaseArtifactSummary!]!
  size: Int64!
  "A refusal code, source_unreachable or storage_failed; null unless failed."
  error: String
  importedAt: DateTime!
  importedBy: UUID!
  inUse: Boolean!
  "The signed manifest document, as JSON."
  manifest: JSON!
}

type ReleasePage

type ReleasePage {
  items: [Release!]!
  next: String
}

enum BrandSource

"Where an effective brand comes from (spec/BRANDS.md)."
enum BrandSource {
  DEFAULT
  BRAND
  BANNER
}

type BrandImageRef

"An image of the brand; its bytes from GET /v1/brand/images/{id}."
type BrandImageRef {
  "The lower-case hex SHA-256 of the PNG."
  id: String!
  width: Int!
  height: Int!
}

type BrandThemeColors

"One theme's accent tokens, as #RRGGBB."
type BrandThemeColors {
  accent: String!
  accentHover: String!
  accentActive: String!
  accentOn: String!
  accentText: String!
  accentSoft: String!
}

type BrandColors

type BrandColors {
  light: BrandThemeColors!
  dark: BrandThemeColors!
}

type EffectiveBrand

"What the apps show in a store: every field null and no lines when the source is DEFAULT."
type EffectiveBrand {
  source: BrandSource!
  "The brand's and the banner's versions, 0 for one absent."
  version: String!
  "The chain's name by language."
  name: JSON
  logo: BrandImageRef
  darkLogo: BrandImageRef
  receiptLogo: BrandImageRef
  colors: BrandColors
  "Lines printed under the logo, each text by language."
  receiptHeader: [JSON!]!
  "Lines printed at the foot."
  receiptFooter: [JSON!]!
  senderName: String
}