openapi: 3.0.3
info:
  title: Palm Open API
  version: '1.0'
  description: |
    Public API for third-party integrations with Palm POS.
servers:
  - url: https://lite-dev.palmnet.co
    description: Production
security:
  - ApiKeyHeader: []
paths:
  /v1/orgs/{orgId}/orders:
    get:
      operationId: listOrgOrders
      summary: List orders
      description: |
        Retrieve orders across all stores in the organization,
        sorted by creation time (newest first). Supports filtering
        and cursor-based pagination.
      tags:
        - Orders
      security:
        - ApiKeyHeader: []
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Organization ID
        - name: store_id
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by a specific store
        - name: view
          in: query
          schema:
            type: string
            enum:
              - all
              - active
              - history
            default: all
          description: >
            `all` — no filter; `active` — excludes completed/cancelled;
            `history` — only completed/cancelled
        - name: payment_status
          in: query
          schema:
            type: string
          description: Comma-separated payment statuses (e.g. `paid,unpaid`)
        - name: fulfillment_status
          in: query
          schema:
            type: string
          description: Comma-separated fulfillment statuses (e.g. `completed,cancelled`)
        - name: source
          in: query
          schema:
            type: string
          description: Comma-separated order sources (e.g. `pos,kiosk`)
        - name: from
          in: query
          schema:
            type: string
          description: Start date/time (RFC 3339 or `YYYY-MM-DD`)
        - name: to
          in: query
          schema:
            type: string
          description: End date/time (RFC 3339 or `YYYY-MM-DD`; date-only = end of day)
        - name: amount_min
          in: query
          schema:
            type: integer
          description: Minimum totalCents (inclusive)
        - name: amount_max
          in: query
          schema:
            type: integer
          description: Maximum totalCents (inclusive)
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 200
          description: >
            Number of orders per page (1–200). Capped to 50 when `expand`
            includes `items`.
        - name: before
          in: query
          schema:
            type: string
            format: date-time
          description: >
            Pagination cursor — pass the `createdAt` of the last order from the
            previous page (RFC 3339 Nano)
        - name: expand
          in: query
          schema:
            type: string
          description: >
            Comma-separated list of sub-resources to inline in each order
            object. Allowed values: `items`, `payments`, `refunds`,
            `tax_breakdown`. When specified, the server batch-loads the
            requested sub-resources for all orders in the current page and
            includes them in the response.
          example: items,payments
      responses:
        '200':
          description: Orders retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - orders
                  - hasMore
                properties:
                  orders:
                    type: array
                    items:
                      $ref: '#/components/schemas/Order'
                  hasMore:
                    type: boolean
                    description: Whether more orders exist beyond this page
              example:
                orders:
                  - id: 550e8400-e29b-41d4-a716-446655440000
                    storeId: 660e8400-e29b-41d4-a716-446655440001
                    orderVersion: 3
                    serviceType: dine_in
                    paymentStatus: paid
                    fulfillmentStatus: completed
                    subtotalCents: 2500
                    discountCents: 0
                    manualDiscountCents: 0
                    taxCents: 250
                    totalCents: 2750
                    note: ''
                    source: pos
                    tzOffsetMinutes: 60
                    createdAt: '2024-06-15T14:30:00Z'
                hasMore: false
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient scope or org mismatch
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /v1/orgs/{orgId}/orders/{orderId}:
    get:
      operationId: getOrgOrder
      summary: Get order details
      description: |
        Retrieve full details of a single order including line items,
        refunds, and tax breakdown.
      tags:
        - Orders
      security:
        - ApiKeyHeader: []
      parameters:
        - name: orgId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Organization ID
        - name: orderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Order ID
      responses:
        '200':
          description: Order retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - order
                properties:
                  order:
                    $ref: '#/components/schemas/OrderDetail'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient scope or org mismatch
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Order not found or does not belong to the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-Api-Key
      description: API key passed in the `X-Api-Key` header
  schemas:
    Order:
      type: object
      required:
        - id
        - storeId
        - orderVersion
        - serviceType
        - paymentStatus
        - fulfillmentStatus
        - subtotalCents
        - discountCents
        - manualDiscountCents
        - taxCents
        - totalCents
        - note
        - source
        - tzOffsetMinutes
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        storeId:
          type: string
          format: uuid
        orderVersion:
          type: integer
        serviceType:
          type: string
          description: '`dine_in`, `takeaway`, `delivery`, etc.'
        paymentStatus:
          type: string
          enum:
            - unpaid
            - partial
            - paid
            - refunded
        fulfillmentStatus:
          type: string
          enum:
            - pending
            - pending_acceptance
            - accepted
            - preparing
            - ready
            - completed
            - cancelled
        subtotalCents:
          type: integer
          description: Subtotal before discounts and tax (cents)
        discountCents:
          type: integer
          description: Promotion/coupon discount (cents)
        manualDiscountCents:
          type: integer
          description: Manual discount by staff (cents)
        taxCents:
          type: integer
          description: Tax amount (cents)
        totalCents:
          type: integer
          description: Final total (cents)
        couponCode:
          type: string
          nullable: true
        sequenceNumber:
          type: integer
          nullable: true
          description: Daily sequence number
        ticketNumber:
          type: string
          nullable: true
        note:
          type: string
        source:
          type: string
          description: 'Order origin: `pos`, `kiosk`, `online`, etc.'
        tzOffsetMinutes:
          type: integer
          description: Store timezone offset from UTC in minutes
        paidAt:
          type: string
          format: date-time
          nullable: true
          description: Timestamp when payment was completed (null if unpaid)
        createdAt:
          type: string
          format: date-time
        tableId:
          type: string
          format: uuid
          nullable: true
        tableName:
          type: string
          nullable: true
        memberId:
          type: string
          format: uuid
          nullable: true
        memberName:
          type: string
          nullable: true
        memberPhone:
          type: string
          nullable: true
        shippingAddressId:
          type: string
          format: uuid
          nullable: true
          description: Shipping address ID (for delivery orders)
        parentOrderId:
          type: string
          format: uuid
          nullable: true
          description: Parent order ID (for split orders)
        pickupName:
          type: string
          nullable: true
        pickupPhone:
          type: string
          nullable: true
        pickupAt:
          type: string
          format: date-time
          nullable: true
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'
          description: >
            Line items. Present in detail endpoint; in list endpoint only when
            `expand=items` is requested.
        payments:
          type: array
          items:
            $ref: '#/components/schemas/OrderPayment'
          description: >
            Payment records. Present in detail endpoint; in list endpoint only
            when `expand=payments` is requested.
        refunds:
          type: array
          items:
            $ref: '#/components/schemas/OrderRefund'
          description: >
            Refund records. Present in detail endpoint; in list endpoint only
            when `expand=refunds` is requested.
        taxBreakdown:
          type: array
          items:
            $ref: '#/components/schemas/TaxBreakdownEntry'
          description: >
            Per-rate tax breakdown. Present in detail endpoint; in list endpoint
            only when `expand=tax_breakdown` is requested.
    OrderPayment:
      type: object
      required:
        - id
        - orderId
        - paymentMethodId
        - amountCents
        - note
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        orderId:
          type: string
          format: uuid
        paymentMethodId:
          type: string
          format: uuid
        paymentMethodName:
          type: object
          additionalProperties:
            type: string
          description: 'Localized name, e.g. `{"_base": "Cash", "en": "Cash"}`'
        paymentMethodCode:
          type: string
          description: Machine-readable code, e.g. `cash`, `card`
        amountCents:
          type: integer
          description: Amount paid (cents)
        tenderCents:
          type: integer
          nullable: true
          description: Amount tendered if different (cash overpayment)
        note:
          type: string
        createdAt:
          type: string
          format: date-time
    OrderDetail:
      description: >
        Full order object returned by the detail endpoint. Always includes
        items, payments, refunds, taxBreakdown, and appliedPromotions.
      allOf:
        - $ref: '#/components/schemas/Order'
        - type: object
          properties:
            appliedPromotions:
              type: array
              nullable: true
              items:
                $ref: '#/components/schemas/AppliedPromotion'
    OrderItem:
      type: object
      required:
        - id
        - orderId
        - productId
        - qty
        - unitPriceCents
        - amountCents
        - discountCents
        - manualDiscountCents
        - taxCents
        - refundedAmountCents
        - refundedQty
        - itemRole
      properties:
        id:
          type: string
          format: uuid
        orderId:
          type: string
          format: uuid
        productId:
          type: string
          format: uuid
        productName:
          type: object
          additionalProperties:
            type: string
          description: 'Localized product name, e.g. `{"_base": "Latte", "en": "Latte"}`'
        variantId:
          type: string
          format: uuid
          nullable: true
        variantName:
          type: object
          additionalProperties:
            type: string
          nullable: true
        qty:
          type: number
        unitPriceCents:
          type: integer
        amountCents:
          type: integer
          description: Line total before discount (cents)
        discountCents:
          type: integer
        manualDiscountCents:
          type: integer
        isPriceOverride:
          type: boolean
        modifiers:
          type: array
          items:
            $ref: '#/components/schemas/OrderItemModifier'
        taxRateId:
          type: string
          nullable: true
        taxCents:
          type: integer
        refundedAmountCents:
          type: integer
        refundedQty:
          type: number
        submitRound:
          type: integer
          description: Submit round number (increments on each kitchen send)
        deltaQty:
          type: number
          description: Quantity change in the current submit round
        itemRole:
          type: string
          enum:
            - normal
            - combo_parent
            - combo_child
        parentItemId:
          type: string
          format: uuid
          nullable: true
        comboGroupId:
          type: string
          description: Combo group ID within the combo product
        comboItemId:
          type: string
          description: Combo item ID within the group
        comboExtraCents:
          type: integer
          description: Extra charge for this combo selection (cents)
    AppliedPromotion:
      type: object
      required:
        - promotionId
        - triggerType
        - name
        - discountCents
      properties:
        promotionId:
          type: string
          format: uuid
        triggerType:
          type: string
          enum:
            - automatic
            - coupon
          description: How the promotion was triggered
        memberCouponId:
          type: string
          format: uuid
          nullable: true
          description: Member coupon instance ID (when triggered by coupon)
        name:
          type: object
          additionalProperties:
            type: string
          description: >-
            Localized promotion name, e.g. `{"_base": "10% Off", "en": "10%
            Off"}`
        discountCents:
          type: integer
          description: Total discount from this promotion (cents)
        lineAllocations:
          type: array
          items:
            $ref: '#/components/schemas/DiscountAllocation'
    DiscountAllocation:
      type: object
      required:
        - lineIndex
        - discountCents
        - promotionId
      properties:
        lineIndex:
          type: integer
          description: Index into the items array
        discountCents:
          type: integer
          description: Discount allocated to this item (cents)
        promotionId:
          type: string
          format: uuid
    OrderItemModifier:
      type: object
      required:
        - id
        - name
        - priceDeltaCents
      properties:
        id:
          type: string
          description: Modifier option ID
        name:
          type: object
          additionalProperties:
            type: string
          description: >-
            Localized modifier name, e.g. `{"_base": "Extra cheese", "en":
            "Extra cheese"}`
        priceDeltaCents:
          type: integer
          description: Price adjustment (cents, can be negative)
    OrderRefund:
      type: object
      required:
        - id
        - orderId
        - amountCents
        - reason
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        orderId:
          type: string
          format: uuid
        amountCents:
          type: integer
        reason:
          type: string
        createdAt:
          type: string
          format: date-time
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderRefundItem'
        payments:
          type: array
          items:
            $ref: '#/components/schemas/OrderRefundPayment'
    OrderRefundItem:
      type: object
      required:
        - id
        - refundId
        - orderItemId
        - qty
        - amountCents
        - taxCents
      properties:
        id:
          type: string
          format: uuid
        refundId:
          type: string
          format: uuid
        orderItemId:
          type: string
          format: uuid
        qty:
          type: number
        amountCents:
          type: integer
        taxCents:
          type: integer
    OrderRefundPayment:
      type: object
      required:
        - id
        - refundId
        - paymentMethodId
        - amountCents
      properties:
        id:
          type: string
          format: uuid
        refundId:
          type: string
          format: uuid
        paymentMethodId:
          type: string
          format: uuid
        paymentMethodName:
          type: object
          additionalProperties:
            type: string
        paymentMethodCode:
          type: string
        amountCents:
          type: integer
    TaxBreakdownEntry:
      type: object
      required:
        - taxRateId
        - taxRatePercent
        - taxCents
      properties:
        taxRateId:
          type: string
          description: Tax rate ID (empty string if unset)
        taxRatePercent:
          type: number
        taxCents:
          type: integer
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable error message
        request_id:
          type: string
          description: Unique request ID for debugging
