> ## Documentation Index
> Fetch the complete documentation index at: https://developer.klikit.io/llms.txt
> Use this file to discover all available pages before exploring further.

# List orders

> Paginated, reverse-chronological order list scoped to your
credential's business. Supports filtering by date range, status,
provider and branch.




## OpenAPI

````yaml /partner-api/api-reference/openapi.yaml get /v1/partner/orders
openapi: 3.1.0
info:
  title: klikit Partner API
  version: 0.2.0
  description: >
    Public partner-facing API for POS / ERP integrations with klikit.


    Every response is wrapped in the canonical

    [Envelope](#tag/Concepts) which carries a `request_id` for support

    correlation and a machine-readable `error.code` on failure.


    All endpoints are authenticated via HTTP Basic Auth using credentials

    issued by your klikit operator contact. The plaintext `secret_key` is

    shown only once at issuance — store it securely; rotate to receive a

    new one if lost.


    Each credential is scoped to a single klikit business and grants a

    fixed set of permissions (e.g. `orders:read`, `stock:update`). The

    full scope vocabulary is documented under

    [Concepts](#tag/Concepts).


    ---


    ## Quickstart


    Pick the **integration mode** that matches what your system needs to

    do. Each mode lists the minimum endpoints to call, in order. Skip

    any mode you don't need — they're independent.


    ### 0. Bootstrap (everyone runs this once)


    1. Confirm auth + your business hierarchy:
       - `GET ` [`/v1/partner/brands`](#operation/listBrands) — returns
         every brand under your business plus the business metadata.
         Capture the `id` for each brand you plan to operate on.
       - `GET ` [`/v1/partner/branches/{id}`](#operation/getBranch) for
         each branch you'll target.
       - To self-serve provisioning, use
         [`createBrand`](#operation/createBrand) /
         [`updateBrand`](#operation/updateBrand) and
         [`createBranch`](#operation/createBranch) /
         [`updateBranch`](#operation/updateBranch). Scopes:
         `brands:create` / `brands:update` and
         `branches:create` / `branches:update`.
    2. Resolve provider ids (klikit, GrabFood, foodpanda, …) that you'll
       reference inside `VisibilitiesMap` and `PriceMap` payloads:
       - `GET ` [`/v1/partner/providers`](#operation/listProviders).
         The catalog is also pinned in
         [`ProviderId`](#/components/schemas/ProviderId) — ids are stable
         across environments, so partners typically cache them and don't
         re-fetch per request.
    3. Pull the static dictionaries you'll need to decode payloads:
       [`OrderStatus`](#/components/schemas/OrderStatus),
       [`PaymentStatus`](#/components/schemas/PaymentStatus),
       [`PaymentMethod`](#/components/schemas/PaymentMethod),
       [`PaymentChannel`](#/components/schemas/PaymentChannel).

    Required scopes for bootstrap: `brands:read`, `branches:read`.


    ---


    ### Mode A — Order operations (POS integration)


    For partners whose POS / OMS accepts orders coming **out** of klikit

    (klikit places, you fulfil + ack the lifecycle).


    | Step | Endpoint | Purpose |

    |---|---|---|

    | A1 | `GET ` [`/v1/partner/orders/statuses`](#operation/listOrderStatuses)
    | One-time reference fetch — pair with
    [`OrderStatus`](#/components/schemas/OrderStatus). |

    | A2 | `GET ` [`/v1/partner/orders`](#operation/listOrders) | Poll for new
    orders. Use `from` / `to` to bound by `created_at`. |

    | A3 | `GET ` [`/v1/partner/orders/{id}`](#operation/getOrder) | Fetch full
    order detail if your list call is summary-only. |

    | A4 | `PATCH ` [`/v1/partner/orders/status`](#operation/updateOrderStatus)
    | Move an order through `ACCEPTED → READY → DELIVERED`, or to `CANCELLED`
    with a reason. Body: `{ id, status, platform: "enterprise" }`. |

    | A5 | `PATCH `
    [`/v1/partner/orders/{id}/comment`](#operation/setOrderComment) | Attach an
    internal note to an order (visible inside the klikit operator console). |


    Scopes: `orders:read`, `orders:update`.


    ---


    ### Mode B — Read the live store menu


    For partners that want to render klikit's menu inside their own

    storefront / POS — no menu authoring on the partner side.


    1. `GET `
    [`/v1/partner/menus?brand_id={brandID}&branch_id={branchID}`](#operation/getMenu)
       — returns the published, store-specific tree with sections →
       categories → items → modifier groups → modifiers nested.

    That's it. The full payload shape lives under

    [`MenuTree`](#/components/schemas/MenuTree); item-level

    `prices`, `visibilities`, `enabled`, `isAvailable` and stock signals

    are all in-tree.


    Scopes: `menus:read`.


    ---


    ### Mode C — Push a menu (partner-authored)


    For partners that own the source-of-truth menu and want to seed /

    update klikit. Walk this on each brand:


    | Step | Endpoint | Notes |

    |---|---|---|

    | C1 | `POST ` [`/v1/partner/menus/sections`](#operation/createSection) |
    Top of the tree. Capture `data.id`. |

    | C2 | `POST ` [`/v1/partner/menus/categories`](#operation/createCategory) |
    Under sections. Capture `data.id`. |

    | C3 | `POST `
    [`/v1/partner/menus/categories/link`](#operation/linkCategoryToSection) |
    Attach the category id(s) to the section id from C1. |

    | C4 | `POST ` [`/v1/partner/menus/items`](#operation/createItem) | One per
    sellable item. `onlyModifier:false`. |

    | C5 | `POST `
    [`/v1/partner/menus/items/categories/link`](#operation/linkCategoriesToItem)
    | Attach the item to its category. |

    | C6 | `POST `
    [`/v1/partner/menus/modifier-groups`](#operation/createModifierGroup) | One
    per "Size", "Toppings", … choice header. |

    | C7 | `POST ` [`/v1/partner/menus/items`](#operation/createItem) | One per
    modifier option, this time with `onlyModifier:true`. |

    | C8 | `POST `
    [`/v1/partner/menus/modifier-groups/modifiers`](#operation/linkModifiersToGroup)
    | Attach modifier-option ids to the modifier-group id. |

    | C9 | `POST `
    [`/v1/partner/menus/modifier-groups/link`](#operation/linkModifierGroupToItem)
    | Attach the modifier group to the item(s). Min/max + modifier ids must be
    re-stated here. |

    | C10 | `POST ` [`/v1/partner/menus/sync`](#operation/syncMenuToStores) |
    Push the business-level definition into each store. |

    | C11 | `POST ` [`/v1/partner/menus/publish`](#operation/publishMenu) |
    Rebuild the cached store snapshot served by `getMenu`, and ship to provider
    marketplaces. |


    Updating an existing menu uses the same chain, with two gotchas

    spelled out under the [`menu-publish`](#tag/menu-publish) tag:

    the sync `fieldIncludeOnSync` escape hatch (so updated schedules /

    visibility actually overwrite store overrides) and the mandatory

    `publishMenu` cache refresh.


    **Stock & OOS** — same scope set; reach for these as needed:


    - [`snoozeItem`](#operation/snoozeItem) — take an item OOS for a
      window (the temporary "we just ran out" UX).
    - [`enableItems`](#operation/enableItems) — bulk enable / disable
      items indefinitely (the permanent OOS toggle). The same shape
      exists for sections
      ([`enableSections`](#operation/enableSections)) and categories
      ([`enableCategories`](#operation/enableCategories)).
    - [`updateItemStocks`](#operation/updateItemStocks) — per-branch
      stock counts in bulk.
    - [`listOOSActivitySummaries`](#operation/listOOSActivitySummaries)
      plus the per-entity audit reads
      ([sections](#operation/listOOSActivitySections),
      [categories](#operation/listOOSActivityCategories),
      [items](#operation/listOOSActivityItems)) — the OOS history feed.

    Scopes: `menus:read`, `menus:create`, `menus:update`,

    optional `menus:delete` if you also clean up.


    ---


    ### Cross-cutting essentials


    - **Authentication** — `Authorization: Basic
    base64(partner_key:secret_key)`.
      Treat the secret like any other production credential. Rotation
      gives you a new secret with no overlap window.
    - **Errors** — switch on `error.code` (see
      [`ErrorBody`](#/components/schemas/ErrorBody)), never the message
      text. `request_id` is the support-correlation key.
    - **Idempotency** — write endpoints accept an `Idempotency-Key`
      header. Same key + same body within 24 h replays the cached
      response; same key + different body returns
      `409 state_idempotency_conflict`.
    - **Scoping** — every credential is pinned to one business. The
      gateway rewrites `business_id` (and the `filterByBusiness` filter
      on order reads) to your credential's value, so you cannot peek at
      another tenant's data even if you supply a different id.
    - **Rate limits** — the default is 600 req/min per credential. Hit
      the cap and the response is `429 rate_limit_exceeded`. Spread
      bulk writes accordingly.
  contact:
    name: klikit Partner API
    url: https://github.com/klikit/klikit-partner
servers:
  - url: https://api.dev.shadowchef.co
    description: Development gateway
security:
  - partnerBasicAuth: []
tags:
  - name: brands
    description: Read access to brands belonging to the partner's business.
  - name: branches
    description: Read access to branches.
  - name: providers
    description: |
      Catalog of order/menu providers (klikit, klikber, grab, foodpanda, …).
      Returned IDs are used as keys inside `VisibilitiesMap` and `PriceMap`
      when creating menu entities.
  - name: price-groups
    description: |
      Pricing groups (price tiers — "Mall", "Airport", …) let the same
      item sell at different prices per branch tier. Group ids are used
      as `price_group_id` on branch create/update and as the outer keys
      of `groupPrices` on item price writes. See the three-step recipe
      under [`listPriceGroups`](#operation/listPriceGroups).
  - name: orders
    description: Order lifecycle endpoints called by the partner.
  - name: menus
    description: Menu read endpoints — full tree fetch and structural reads.
  - name: menu-sections
    description: Sections live at the top of the menu tree.
  - name: menu-categories
    description: Categories sit under sections and contain items.
  - name: menu-items
    description: >-
      Items + per-item price / stock / availability / visibility / blackout /
      linking.
  - name: menu-modifier-groups
    description: >-
      Modifier groups, modifier-group ↔ item links, presets, and modifier-group
      ↔ modifier links.
  - name: menu-publish
    description: >
      Publish, populate, sync, and reset operations between the business

      menu and store overrides.


      ### How a business-level change reaches the partner-facing

      ### `getMenu` read


      The menu has three layers — business definition, per-store

      override, cached store snapshot — and partners interact with all

      three. After editing the business layer you need an explicit push

      to refresh each downstream layer:


      | Step                                       |
      Purpose                                                              |

      |--------------------------------------------|----------------------------------------------------------------------|

      | `PATCH /menus/sections/{id}` (or similar)  | Edit the business-level
      row.                                         |

      | `POST /menus/sync`                         | Replay business state into
      the per-store overrides. By default sync **preserves** the store's
      existing `availableTimes`, `isEnabled`, `visibilities`, `blackoutDates` —
      list whichever you want to force-overwrite in `fieldIncludeOnSync`. |

      | `POST /menus/publish`                      | Rebuild the cached
      `store_full_menus` blob that `getMenu` reads, and ship to
      providers.                                       |


      Skipping the publish step is the most common reason

      [`getMenu`](#operation/getMenu) keeps returning the old payload

      after a sync — see [`publishMenu`](#operation/publishMenu).
  - name: menu-oos
    description: OOS (out-of-stock) activity track reads.
  - name: webhooks
    description: >
      ## What this is


      Klikit pushes events to a URL **you** host. Whenever something

      happens on klikit that you care about — a new order arrives, an

      order's status changes, a customer modifies their cart — klikit

      makes an HTTP POST to your endpoint with the details. You don't

      need to poll.


      ## The three events you can subscribe to


      | Event | When it fires |

      |---|---|

      | `klikit.order.created.v2`     | A new order has just landed for one of
      your branches. This is the trigger for "ring the bell at the kitchen". |

      | `klikit.order.status.updated` | An order's status moved forward (e.g.
      *accepted → preparing → ready → delivered*) or was cancelled. |

      | `klikit.order.cart.updated`   | A customer changed items / quantities
      **before** finalising. Use this if you preview baskets in real time. |


      All three events POST the same envelope:


      ```json

      {
        "brand_id":  123,
        "branch_id": 456,
        "orders":    [ /* one or more orders, same shape as GET /v1/partner/orders */ ]
      }

      ```


      ## Getting set up


      1. **Tell your klikit operator the URL.** Webhook URLs are
         registered out-of-band during onboarding — give your klikit
         contact one URL per `(brand, branch, event)` tuple. There is
         no self-service registration endpoint today.
      2. **Get your `webhook_secret_key`** from the same operator. Treat
         it like a password: never log it, never put it in git.
      3. **Stand up a receiver.** See the worked examples in
         [examples/webhook-receiver](https://github.com/klikit/partner-api/tree/develop/examples/webhook-receiver)
         (Node + Python, ~80 lines each) — copy whichever fits your
         stack.

      ## What klikit sends with every delivery


      | Header | Meaning |

      |---|---|

      | `x-klikit-signature`  | `hex(HMAC-SHA256(your webhook_secret_key, raw
      request body))` |

      | `x-klikit-event-id`   | Unique per delivery attempt. Use this for
      idempotency — see below. |

      | `x-klikit-event-type` | One of the three event names above. |

      | `Content-Type`        | Always `application/json`. |


      ## How to verify the signature


      The header `x-klikit-signature` is an **HMAC-SHA256** computed

      over the **raw bytes** of the request body, using your

      `webhook_secret_key` as the key, and encoded as **lowercase

      hex**. Reject any delivery whose signature you cannot

      reproduce — it means the request was tampered with, or you have

      the wrong secret.


      ```js

      // Node

      const crypto = require("crypto");

      const expected = crypto
        .createHmac("sha256", WEBHOOK_SECRET)
        .update(rawBody)         // the raw bytes — NOT the parsed JSON
        .digest("hex");
      const ok = crypto.timingSafeEqual(
        Buffer.from(req.headers["x-klikit-signature"]),
        Buffer.from(expected),
      );

      ```


      ```python

      # Python

      import hmac, hashlib

      expected = hmac.new(
          WEBHOOK_SECRET.encode(),
          raw_body,                # the raw bytes — NOT the parsed JSON
          hashlib.sha256,
      ).hexdigest()

      ok = hmac.compare_digest(request.headers["x-klikit-signature"], expected)

      ```


      ```bash

      # Quick check from the shell

      printf '%s' "$RAW_BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET"

      ```


      > **Critical gotcha.** Compute the HMAC over the **raw bytes**

      > klikit sent. If you parse the JSON and re-serialise before

      > verifying, key reordering or whitespace differences will break

      > the signature.


      ## What klikit expects back


      - **HTTP 2xx within 10 seconds.** Body is ignored. JSON or
        empty are both fine.
      - **Anything else (3xx, 4xx, 5xx, timeout, connection error)** is
        treated as a failed delivery. Klikit will retry with
        exponential backoff. Failed deliveries are persisted in
        `hookit.webhook_logs` and your operator can ask support to
        replay them if you needed to recover from downtime.

      ## Two things you must build in


      **1. Be idempotent.** Because klikit retries on non-2xx, the same

      `x-klikit-event-id` *will* arrive twice in some failure modes.

      Store the IDs you've already processed (Redis `SETNX`, Postgres

      unique index, …) and silently return 2xx for repeats.


      **2. Acknowledge fast, do work async.** Reply 200 first, then

      push to your POS / kitchen display / database on a background

      worker. If your endpoint takes 11 seconds to push to a slow

      downstream and then returns 200, klikit has already counted it

      as a timeout and queued a retry — you will process every order

      twice.


      ## Troubleshooting


      | Symptom | Most likely cause |

      |---|---|

      | `401 invalid signature` on every request | Reading the parsed body
      instead of the raw body, or wrong `webhook_secret_key` |

      | Duplicate orders showing up downstream | Non-idempotent handler — same
      `x-klikit-event-id` processed twice after a retry |

      | Klikit reports deliveries failing | Endpoint returning non-2xx, taking
      >10s, or unreachable from klikit's egress |

      | `x-klikit-*` headers missing on your side | Reverse proxy stripping
      `x-*` headers — most common with nginx; allow explicitly |
  - name: Concepts
    description: |
      Shared shapes that aren't endpoints — request envelope, error codes,
      scope vocabulary.
  - name: Reference dictionaries
    description: |
      Static numeric enums that show up inside order / payment / branch
      payloads. Use these to decode order + payment fields without an
      extra endpoint, and to look up the `country_id` / `city_id` /
      `currency_id` triple your [`createBranch`](#operation/createBranch)
      call needs.

      Order & payment:
      [`OrderStatus`](#/components/schemas/OrderStatus),
      [`PaymentStatus`](#/components/schemas/PaymentStatus),
      [`PaymentMethod`](#/components/schemas/PaymentMethod),
      [`PaymentChannel`](#/components/schemas/PaymentChannel),
      [`ProviderId`](#/components/schemas/ProviderId).

      Geo & currency:
      [`CountryId`](#/components/schemas/CountryId) — 24 active country
      ids with ISO codes,
      [`CityId`](#/components/schemas/CityId) — 252 cities grouped by
      country (the partner-side source-of-truth for the
      `(country_id, city_id)` pair core's branch create validates
      against),
      [`CurrencyId`](#/components/schemas/CurrencyId) — currency ids
      paired with the ISO 4217 codes you already use inside
      [`PriceMap`](#/components/schemas/PriceMap).
paths:
  /v1/partner/orders:
    get:
      tags:
        - orders
      summary: List orders
      description: |
        Paginated, reverse-chronological order list scoped to your
        credential's business. Supports filtering by date range, status,
        provider and branch.
      operationId: listOrders
      parameters:
        - $ref: '#/components/parameters/From'
        - $ref: '#/components/parameters/To'
        - $ref: '#/components/parameters/Status'
        - $ref: '#/components/parameters/ProviderId'
        - $ref: '#/components/parameters/BranchId'
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Size'
      responses:
        '200':
          description: Order list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderListEnvelope'
              examples:
                two_orders:
                  $ref: '#/components/examples/OrderListExample'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    From:
      in: query
      name: from
      schema:
        type: string
        format: date-time
      description: Lower bound of `created_at` (inclusive).
    To:
      in: query
      name: to
      schema:
        type: string
        format: date-time
      description: Upper bound of `created_at` (exclusive). Max window is 90 days.
    Status:
      in: query
      name: status
      schema:
        type: string
    ProviderId:
      in: query
      name: provider_id
      schema:
        type: integer
    BranchId:
      in: query
      name: branch_id
      schema:
        type: integer
    Page:
      in: query
      name: page
      schema:
        type: integer
        minimum: 1
        default: 1
      description: 1-indexed page number. Echoed back in the response body.
    Size:
      in: query
      name: size
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
      description: Items per page. Echoed back in the response body.
  schemas:
    OrderListEnvelope:
      allOf:
        - $ref: '#/components/schemas/Envelope'
        - type: object
          properties:
            data:
              $ref: '#/components/schemas/OrderListData'
    Envelope:
      type: object
      description: |
        Canonical response wrapper. Every response — success or error —
        carries the `request_id` so you can quote one id to klikit
        support to correlate a request end-to-end.
      properties:
        request_id:
          type: string
          example: req_4d1b7e3f-...
        data:
          description: Endpoint-specific payload on success.
        error:
          $ref: '#/components/schemas/ErrorBody'
      required:
        - request_id
    OrderListData:
      type: object
      description: |
        Paginated order list payload. `page`, `size`, and `total` echo
        the query parameters back so clients can stop iterating once
        `page * size >= total`.
      properties:
        orders:
          type: array
          items:
            $ref: '#/components/schemas/Order'
        page:
          type: integer
          example: 1
        size:
          type: integer
          example: 20
        total:
          type: integer
          example: 50
    ErrorBody:
      type: object
      description: >
        Machine-readable error code + human message. The code is stable

        across releases — switch on `code` in your client code rather

        than parsing the message text.


        Common codes you will encounter as a partner:


        | Code                            | HTTP |
        Meaning                                           |

        |---------------------------------|------|---------------------------------------------------|

        | `auth_missing`                  | 401  | Authorization header absent /
        malformed           |

        | `auth_invalid_credential`       | 401  | partner_key or secret_key did
        not verify          |

        | `auth_revoked`                  | 403  | Credential is
        revoked                             |

        | `auth_forbidden`                | 403  | Credential not authorized for
        the requested scope |

        | `request_invalid`               | 400  | Body / query parameters
        failed validation         |

        | `request_missing_idempotency_key` | 400 | Write endpoint called
        without `Idempotency-Key`   |

        | `request_invalid_range`         | 400  | Date range > 90
        days                              |

        | `resource_not_found`            | 404  | Order / store / mapping does
        not exist            |

        | `resource_unmapped`             | 404  | Stock / availability call
        referenced an unknown SKU |

        | `state_invalid_transition`      | 409  | Order PATCH not allowed by
        current state          |

        | `state_idempotency_conflict`    | 409  | Same Idempotency-Key reused
        with a different body |

        | `rate_limit_exceeded`           | 429  | Per-credential rate cap
        hit                       |

        | `downstream_unavailable`        | 502  | An internal klikit dependency
        is unreachable      |
      properties:
        code:
          type: string
          example: auth_invalid_credential
        message:
          type: string
        details:
          description: Optional structured detail (validation errors, current state).
      required:
        - code
        - message
    Order:
      type: object
      description: |
        A single order record. The same shape is returned by
        [`listOrders`](#operation/listOrders) (inside `data.orders[]`),
        [`getOrder`](#operation/getOrder) (as `data`), and on the webhook
        payloads (inside `orders[]`).

        Only the most commonly consumed fields are documented below; the
        wire response carries ~100+ fields per order including finance
        breakdowns (`provider_*`, `merchant_*`, `*_display`), BIR
        (Philippines) fields, fulfillment timeline, and operator
        metadata. Partners should treat unknown fields as ignorable.
      properties:
        id:
          type: integer
          description: klikit-side order id. Stable for the life of the order.
          example: 71280866
        identity:
          type: string
          description: |
            Public order UUID (or short code on manual orders). Use this
            for customer-facing references; `id` is the integer primary
            key used everywhere on the API.
        short_id:
          type: string
          description: Human-readable counter shown on receipts (e.g. `1004`).
        external_id:
          type: string
          description: Aggregator / source-system order id.
        business_id:
          type: integer
        branch_id:
          type: integer
        brands:
          type: array
          description: Brands referenced in the cart (typically one).
          items:
            type: object
            properties:
              id:
                type: integer
              title:
                type: string
              logo:
                type: string
        status:
          type: integer
          description: |
            Numeric status id. Resolve via
            [`listOrderStatuses`](#operation/listOrderStatuses).
        fulfillment_status_id:
          type: integer
          nullable: true
          description: |
            Numeric fulfillment (delivery) status id. Resolve via
            [`listFulfillmentStatuses`](#operation/listFulfillmentStatuses).
        type:
          type: integer
          description: |
            Numeric order-type id (1 = pickup, 2 = delivery, 3 = dine-in).
            Resolve via [`listOrderTypes`](#operation/listOrderTypes).
        source:
          type: integer
          description: Order source (provider channel).
        provider_id:
          type: integer
          description: |
            Aggregator provider id (klikit = 1, GrabFood, foodpanda, …).
            Look up via [`listProviders`](#operation/listProviders).
        currency:
          type: string
          example: IDR
        currency_symbol:
          type: string
          example: Rp
        payment_status:
          type: integer
        payment_method:
          type: integer
        payment_channel_id:
          type: integer
        is_offline_payment:
          type: boolean
        is_manual_order:
          type: boolean
        is_vat_included:
          type: boolean
        item_count:
          type: integer
        unique_item_count:
          type: integer
        item_price:
          type: number
          description: Cart subtotal in minor units of `currency`.
        delivery_fee:
          type: number
        vat:
          type: number
        discount:
          type: number
        additional_fee:{ type: number }
        final_price:
          type: number
          description: Grand total in minor units of `currency`.
        item_price_display:
          type: string
          example: '13.000'
        delivery_fee_display:
          type: string
          example: '7.300'
        final_price_display:
          type: string
          example: '13.000'
        cart:
          type: array
          description: |
            Legacy v1 cart line items. Each entry carries klikit_*
            menu joins, modifier groups, applied promo, and price snapshot.
            Prefer `cart_v2` when present.
          items:
            type: object
            additionalProperties: true
        cart_v2:
          type: array
          description: Current cart line items (v2 schema).
          items:
            type: object
            additionalProperties: true
        delivery_info:
          type: object
          nullable: true
          description: Customer delivery details (address, contact, coordinates).
          additionalProperties: true
        fulfillment_receiver:
          type: object
          nullable: true
          description: |
            Resolved fulfillment receiver. Often the same shape as
            `delivery_info`.
          additionalProperties: true
        fulfillment_rider:
          type: object
          nullable: true
          description: Rider details once dispatched.
          additionalProperties: true
        fulfillments:
          type: array
          description: Per-fulfillment dispatch records (one per leg).
          items:
            type: object
            additionalProperties: true
        order_status_histories:
          type: array
          description: Append-only status transition log.
          items:
            type: object
            properties:
              id:
                type: integer
              order_id:
                type: integer
              status:
                type: integer
              processed_at:
                type: string
                format: date-time
              updated_by:
                type: integer
                nullable: true
        cancellation_reason:
          type: string
          nullable: true
        cancellation_reason_id:
          type: integer
          nullable: true
        cancelled_by:
          type: integer
          nullable: true
        preparation_time:
          type: integer
          description: Operator-set prep time in minutes.
        max_preparation_time:
          type: integer
        order_preparation_time_left:
          type: integer
        scheduled_time:
          type: string
          format: date-time
          nullable: true
        scheduled_status:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        can_accept:
          type: boolean
        can_ready:
          type: boolean
        can_deliver:
          type: boolean
        can_cancel:
          type: boolean
        can_void:
          type: boolean
        can_update:
          type: boolean
        can_update_prep_time:
          type: boolean
        user_full_name:
          type: string
        user_email:
          type: string
        user_phone:
          type: string
        order_comment:
          type: string
        qr_code_url:
          type: string
      additionalProperties: true
  examples:
    OrderListExample:
      summary: Two-order page from prod biz 398
      value:
        request_id: d4cc8bee50798956843cc19e0de6ba72
        data:
          orders:
            - additional_fee: 0
              additional_fee_display: '0.000'
              additional_info:
                vehicle_info: {}
              auto_accept: false
              auto_ready: true
              branch_id: 1293
              brands:
                - id: 5297
                  logo: /images/1a0cd999ecf8d62acb4f558ff8fb747ae5fe6b61.jpeg
                  title: (DEMO) Cimol Bojot AA
              business_id: 398
              campaigns: []
              can_accept: false
              can_cancel: true
              can_deliver: false
              can_ready: false
              can_void: true
              cart:
                - brand:
                    id: 5297
                    logo: /images/1a0cd999ecf8d62acb4f558ff8fb747ae5fe6b61.jpeg
                    title: (DEMO) Cimol Bojot AA
                  comment: ''
                  description: Cimol Beef Porsi Besar Isi 14 Pcs
                  groups: []
                  image: ''
                  item_final_price: 0
                  item_id: 289379
                  klikit_category_id: 39118
                  klikit_category_name: Cimol Beef
                  klikit_id: 289379
                  klikit_name: Cimol Beef Porsi Besar
                  klikit_price: 0
                  klikit_section_id: 6825
                  klikit_section_name: Cimol Bojot AA Master Menu
                  klikit_sku_id: CBA001
                  price: 0
                  promo_discount: 0
                  quantity: 1
                  title: Cimol Beef Porsi Besar
                  title_v2:
                    en: Cimol Beef Porsi Besar
                  unit_price: 0
                  vat: 0
              cart_v2:
                - brand:
                    id: 5297
                    logo: /images/1a0cd999ecf8d62acb4f558ff8fb747ae5fe6b61.jpeg
                    title: (DEMO) Cimol Bojot AA
                  description: Cimol Beef Porsi Besar Isi 14 Pcs
                  id: '289379'
                  klikit_category_id: 39118
                  klikit_category_name: Cimol Beef
                  klikit_id: 289379
                  klikit_name: Cimol Beef Porsi Besar
                  klikit_name_translation:
                    en: Cimol Beef Porsi Besar
                  klikit_section_id: 6825
                  klikit_section_name: Cimol Bojot AA Master Menu
                  klikit_sku_id: CBA001
                  klikit_unit_price: '0.00'
                  menu_version: 2
                  modifier_group_price: '0.00'
                  modifier_groups: []
                  name: Cimol Beef Porsi Besar
                  price: '0.00'
                  price_display: '0.000'
                  quantity: 1
                  unit_price: '0.00'
                  unit_price_display: '0.000'
              created_at: '2026-06-24T12:03:27Z'
              created_by: 11745
              created_by_user: Tiwi Support Testing Account
              currency: IDR
              currency_symbol: Rp
              delivery_address: ''
              delivery_fee: 0
              delivery_info: null
              discount: 0
              external_id: '1782302607204811041'
              final_price: 0
              final_price_display: '0.000'
              fulfillment_status_id: null
              fulfillments: []
              id: 71280866
              identity: e8481d02-550f-44b1-b489-e7b941b8c2f5
              is_manual_order: true
              is_offline_payment: true
              is_vat_included: true
              item_count: 1
              item_price: 0
              klikit_store_id: KSID-5297-1293
              order_comment: ''
              order_status_histories:
                - id: 253820147
                  order_id: 71280866
                  status: 1
                  processed_at: '2026-06-24T12:03:27Z'
                  updated_by: null
                - id: 253820195
                  order_id: 71280866
                  status: 2
                  processed_at: '2026-06-24T12:03:35Z'
                  updated_by: null
                - id: 253820300
                  order_id: 71280866
                  status: 4
                  processed_at: '2026-06-24T12:03:49Z'
                  updated_by: null
                - id: 253820409
                  order_id: 71280866
                  status: 5
                  processed_at: '2026-06-24T12:04:02Z'
                  updated_by: null
              payment_channel_id: 39
              payment_method: 1
              payment_status: 1
              preparation_time: 10
              provider_id: 1
              qr_code_url: >-
                https://me.klikit.io/order/AAAAAAQ_qOIAAAAAajz3yaRvUcUfMoHEBxwoEEfLqsU
              short_id: '1004'
              source: 9
              status: 5
              type: 3
              unique_item_count: 1
              updated_at: '2026-06-24T12:04:02Z'
              user_full_name: Tiwi Support Testing Account
              vat: 0
            - branch_id: 5913
              brands:
                - id: 1100
                  logo: /images/977d6c49b04755746ebb77056f6aee1849a9d8b1.png
                  title: TEST - Klikit Healthy Kitchen
              business_id: 398
              cancellation_reason: Maxim cancelled
              cancelled_by: 3
              can_accept: false
              can_cancel: true
              can_deliver: true
              can_ready: true
              can_void: false
              cart_v2:
                - brand:
                    id: 1100
                    logo: /images/977d6c49b04755746ebb77056f6aee1849a9d8b1.png
                    title: TEST - Klikit Healthy Kitchen
                  id: '43014'
                  klikit_id: 43014
                  klikit_category_name: Menu Satuan
                  klikit_section_name: Klikit Dimsum
                  name: Dimsum Cumi
                  quantity: 2
                  price: '13000.00'
                  unit_price: '6500.00'
                  unit_price_display: '6.500'
              created_at: '2026-06-24T10:10:14Z'
              currency: IDR
              delivery_fee: 730000
              delivery_fee_display: '7.300'
              delivery_info:
                address: >-
                  Fakultas Bahasa dan Seni Universitas Pendidikan Ganesha, Jl.
                  A. Yani, Kaliuntu, Buleleng, Buleleng, Bali, Indonesia, 81116
                coordinates:
                  latitude: -8.1121781
                  longitude: 115.085482
                email: rezalbachdar@gmail.com
                first_name: Rezal
                last_name: Rezal
                phone: '+6282155213518'
              external_id: bfb76006-835c-4d61-a789-b3a0afd6d5fc
              final_price: 1300000
              final_price_display: '13.000'
              fulfillment_status_id: 9
              fulfillments:
                - delivery_fee: 730000
                  fulfillment_provider_id: 5
                  order_id: 71269954
                  sequence: 1
                  status_id: 9
                  receiver:
                    address: >-
                      Fakultas Bahasa dan Seni Universitas Pendidikan Ganesha,
                      Jl. A. Yani, Kaliuntu, Buleleng, Buleleng, Bali,
                      Indonesia, 81116
                    coordinates:
                      latitude: -8.1121781
                      longitude: 115.085482
                    first_name: Rezal
                    phone: '+6282155213518'
                  sender:
                    address: >-
                      Jl. Pahlawan, Paket Agung, Kec. Buleleng, Kabupaten
                      Buleleng, Bali 81117
                    coordinates:
                      latitude: -8.124293388269452
                      longitude: 115.0929023057558
                    country_code: ID
                    first_name: 'TEST - Klikit Healthy Kitchen, Branch: (DEMO) Buleleng'
                    phone: '+6285172191644'
              id: 71269954
              identity: '268053'
              is_manual_order: false
              is_offline_payment: false
              item_count: 2
              item_price: 1300000
              max_preparation_time: 60
              order_status_histories:
                - id: 253780790
                  order_id: 71269954
                  status: 2
                  processed_at: '2026-06-24T10:10:49Z'
                - id: 253785511
                  order_id: 71269954
                  status: 3
                  processed_at: '2026-06-24T10:25:50Z'
              payment_channel_id: 49
              payment_method: 18
              payment_status: 1
              preparation_time: 20
              provider_grand_total: 2030000
              provider_grand_total_display: '20.300'
              provider_id: 17
              provider_sub_total: 1300000
              provider_sub_total_display: '13.000'
              short_id: '1002'
              source: 0
              status: 3
              type: 2
              unique_item_count: 1
              updated_at: '2026-06-24T10:25:50Z'
              user_email: rezalbachdar@gmail.com
              user_first_name: Rezal
              user_id: 160888
              user_last_name: Rezal
              user_phone: '+6282155213518'
          page: 1
          size: 2
          total: 50
  responses:
    InvalidRequest:
      description: Request input is malformed or invalid.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Envelope'
              - type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: request_invalid
                      message:
                        type: string
    Unauthorized:
      description: Missing or invalid credentials.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Envelope'
              - type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: auth_invalid_credential
                        enum:
                          - auth_missing
                          - auth_invalid_credential
    Forbidden:
      description: Authenticated but not allowed (revoked or out-of-scope).
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Envelope'
              - type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - auth_revoked
                          - auth_forbidden
    RateLimited:
      description: Per-credential request rate cap hit. Retry after a backoff.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Envelope'
              - type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: rate_limit_exceeded
  securitySchemes:
    partnerBasicAuth:
      type: http
      scheme: basic
      description: |
        `Authorization: Basic base64(partner_key:secret_key)`.

        Credentials are issued by a klikit operator. The plaintext
        `secret_key` is shown once at issuance and cannot be retrieved
        later — store it securely. If lost, ask your operator to rotate
        the secret to receive a new one. The old secret stops working
        immediately on rotation; there is no overlap window.

````