> ## 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.

# Update a branch

> Updates an existing branch. Re-send `brand_ids` whenever you
change brand assignment and set `brand_list_changed: true`.




## OpenAPI

````yaml /partner-api/api-reference/openapi.yaml patch /v1/partner/branches/{id}
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/branches/{id}:
    patch:
      tags:
        - branches
      summary: Update a branch
      description: |
        Updates an existing branch. Re-send `brand_ids` whenever you
        change brand assignment and set `brand_list_changed: true`.
      operationId: updateBranch
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BranchCreateRequest'
      responses:
        '200':
          description: Branch updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Envelope'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  schemas:
    BranchCreateRequest:
      type: object
      description: |
        Mirrors core's `BranchCreateUpdateReq`. `business_id` is
        overwritten by the forwarder. `phone` must be E.164
        (`+<country><digits>`). `available_times` follows the same
        weekday-keyed shape as section availability.
      required:
        - title
        - address
        - phone
        - lat
        - lon
        - city_id
        - country_id
        - currency_id
        - day_availability
        - subscription_type
        - available_times
        - custom_vat_config
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 255
        address:
          type: string
          minLength: 5
          maxLength: 255
        google_map_link:
          type: string
          nullable: true
        phone:
          type: string
          description: E.164
          e.g. "+6281234567890": null
        secondary_phone:
          type: string
          nullable: true
        lat:
          type: number
        lon:
          type: number
        country_id:
          $ref: '#/components/schemas/CountryId'
        city_id:
          allOf:
            - $ref: '#/components/schemas/CityId'
          description: |
            Must belong to the supplied `country_id` — core validates
            the pair against the cities table and the gateway returns
            `502 downstream_unavailable` on a join miss. See
            [`CityId`](#/components/schemas/CityId) for the per-country
            city list.
        currency_id:
          $ref: '#/components/schemas/CurrencyId'
        brand_ids:
          type: array
          items:
            type: integer
        day_availability:
          type: array
          minItems: 1
          maxItems: 7
          items:
            type: integer
            minimum: 0
            maximum: 6
        kitchen_equipment_ids:
          type: array
          items:
            type: integer
        brand_list_changed:
          type: boolean
          default: false
        kitchen_equipment_list_changed:
          type: boolean
          default: false
        populate_menu_for_new_brand:
          type: boolean
          default: false
        subscription_type:
          type: integer
        available_times:
          $ref: '#/components/schemas/AvailableTimes'
        custom_vat_config:
          type: boolean
        vat_enabled:
          type: boolean
          nullable: true
        vat_inclusive:
          type: boolean
          nullable: true
        vat_label:
          type: string
          nullable: true
        vat_calculation_pre_discount:
          type: boolean
          nullable: true
        vat_percentage:
          type: number
          nullable: true
        prioritize_item_level_vat:
          type: boolean
          nullable: true
        order_cleanup_time:
          type: integer
          description: Cleanup window in minutes.
        manual_order_auto_accept_enabled:
          type: boolean
          default: false
        is_shift_enabled:
          type: boolean
          default: false
        is_driver_progress_enabled:
          type: boolean
          default: false
        marketplace_enabled:
          type: boolean
          nullable: true
        webshop_otp_required:
          type: boolean
          nullable: true
        bir_config_enabled:
          type: boolean
          nullable: true
        price_group_id:
          type: integer
          nullable: true
          description: |
            Pricing group this branch sells at (see
            [`listPriceGroups`](#operation/listPriceGroups)). **Update
            semantics:** a `PATCH` body that omits this field CLEARS an
            existing assignment (branch reverts to base prices) — once a
            branch is on a group, include `price_group_id` in every
            branch update you send.
    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
    CountryId:
      type: integer
      description: |
        Numeric country id used inside `BranchCreateRequest.country_id`
        and a few other resolver calls. Only ids with `configured = 1`
        are listed — anything outside this list is rejected upstream.

        | id | iso | country |
        |----|-----|---------|
        | 1 | AUS | Australia |
        | 2 | PHL | Philippines |
        | 3 | SGP | Singapore |
        | 4 | IDN | Indonesia |
        | 5 | TWN | Taiwan |
        | 6 | MYS | Malaysia |
        | 7 | GBR | United Kingdom |
        | 8 | HKG | Hong Kong |
        | 9 | THA | Thailand |
        | 10 | JPN | Japan |
        | 11 | VNM | Vietnam |
        | 12 | KHM | Cambodia |
        | 23 | ARG | Argentina |
        | 30 | BGD | Bangladesh |
        | 38 | BOL | Bolivia |
        | 43 | BRA | Brazil |
        | 59 | COL | Colombia |
        | 64 | CRI | Costa Rica |
        | 75 | ECU | Ecuador |
        | 101 | GTM | Guatemala |
        | 151 | MEX | Mexico |
        | 167 | NZL | New Zealand |
        | 168 | NIC | Nicaragua |
        | 182 | PER | Peru |
      enum:
        - 1
        - 2
        - 3
        - 4
        - 5
        - 6
        - 7
        - 8
        - 9
        - 10
        - 11
        - 12
        - 23
        - 30
        - 38
        - 43
        - 59
        - 64
        - 75
        - 101
        - 151
        - 167
        - 168
        - 182
      x-enum-varnames:
        - AUS
        - PHL
        - SGP
        - IDN
        - TWN
        - MYS
        - GBR
        - HKG
        - THA
        - JPN
        - VNM
        - KHM
        - ARG
        - BGD
        - BOL
        - BRA
        - COL
        - CRI
        - ECU
        - GTM
        - MEX
        - NZL
        - NIC
        - PER
    CityId:
      type: integer
      description: >
        Numeric city id paired with
        [`CountryId`](#/components/schemas/CountryId)

        on every branch create — core validates the `(country_id,

        city_id)` pair against the cities table, so a city id from a

        different country returns 4xx.


        Listed per country to keep the table searchable. Names are the

        canonical English title; non-ASCII characters reflect the

        upstream database row verbatim.


        **Australia (country_id = 1)**


        | city_id | city |

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

        | 1 | Gold Coast |

        | 2 | Sydney |

        | 3 | Brisbane |

        | 4 | Perth |

        | 5 | Melbourne |

        | 6 | Hobart |


        **Philippines (country_id = 2)**


        | city_id | city |

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

        | 7 | Manila |

        | 8 | Cebu |

        | 9 | Binan |

        | 10 | Caloocan |

        | 11 | Las Pinas |

        | 12 | Makati |

        | 13 | Malabon |

        | 14 | Mandaluyong |

        | 15 | Marikina |

        | 16 | Muntinlupa |

        | 17 | Navotas |

        | 18 | Paranaque |

        | 19 | Pasay |

        | 20 | Pasig |

        | 21 | Quezon City |

        | 22 | San Juan |

        | 23 | Taguig |

        | 24 | Valenzuela |

        | 25 | Pateros |

        | 26 | Imus |

        | 27 | Bacoor |

        | 28 | Angeles City |

        | 29 | San Fernando |

        | 37 | Cainta |

        | 38 | Taytay |

        | 39 | Malolos |

        | 40 | Batangas |

        | 41 | Cavite |

        | 44 | Tarlac |

        | 90 | Calamba |

        | 91 | Subic |

        | 92 | Baguio |

        | 93 | Mexico |

        | 94 | Davao |

        | 95 | Cagayan de Oro |

        | 96 | Pangasinan |

        | 97 | Zamboanga |

        | 98 | Sta Rosa Laguna |

        | 264 | Albay |

        | 265 | Antipolo |

        | 266 | San Mateo |

        | 267 | Bulacan |

        | 268 | Boracay |

        | 269 | General Santos |


        **Singapore (country_id = 3)**


        | city_id | city |

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

        | 30 | Singapore |


        **Indonesia (country_id = 4)**


        | city_id | city |

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

        | 31 | Jakarta |

        | 51 | Tangerang |

        | 52 | Bekasi |

        | 53 | Depok |

        | 54 | Bogor |

        | 55 | Bandung |

        | 56 | Yogyakarta |

        | 57 | Semarang |

        | 58 | Surabaya |

        | 59 | Bali |

        | 60 | Medan |

        | 61 | Palembang |

        | 62 | Pekanbaru |

        | 63 | Batam |

        | 64 | Padang |

        | 65 | Bandar Lampung |

        | 66 | Makassar |

        | 67 | Manado |

        | 68 | Pontianak |

        | 69 | Balikpapan |

        | 70 | Banjarmasin |

        | 71 | Balikapapan |

        | 72 | Malang |

        | 73 | Lombok |

        | 74 | Magelang |

        | 75 | Solo |

        | 76 | Batu |

        | 77 | Cirebon |

        | 78 | Purwakarta |

        | 79 | Purwekorto |

        | 80 | Madura |

        | 81 | Tanjung Pinang Riau |

        | 82 | Pangkal Pinang |

        | 83 | Ambon |

        | 84 | Kupang |

        | 85 | Jayapura |

        | 86 | Sorong |

        | 87 | Banda Aceh |

        | 88 | Jambi |

        | 89 | Bengkulu |

        | 106 | Denpasar |

        | 107 | Ubud |

        | 108 | Kuta |

        | 109 | Cangu |

        | 110 | Umalas |

        | 111 | Nusa Dua |

        | 112 | Jimbaran |

        | 113 | Badung |

        | 114 | Buleleng |

        | 207 | Langsa |

        | 208 | Lhokseumawe |

        | 209 | Sabang |

        | 210 | Subulussalam |

        | 212 | Binjai |

        | 213 | Padangsidimpuan |

        | 214 | Pematang Siantar |

        | 215 | Tebing Tinggi |

        | 217 | Pariaman |

        | 218 | Payakumbuh |

        | 219 | Sawahlunto |

        | 220 | Solok |

        | 222 | Dumai |

        | 225 | Lubuklinggau |

        | 226 | Pagar Alam |

        | 227 | Serang |

        | 228 | Cilegon |

        | 232 | Banjar |

        | 235 | Cimahi |

        | 238 | Sukabumi |

        | 239 | Tasikmalaya |

        | 241 | Pekalongan |

        | 242 | Salatiga |

        | 244 | Surakarta |

        | 245 | Tegal |

        | 247 | Blitar |

        | 248 | Kediri |

        | 249 | Madiun |

        | 251 | Mojokerto |

        | 253 | Singkawang |

        | 254 | Palangka Raya |

        | 255 | Sampit |

        | 257 | Banjarbaru |


        **Taiwan (country_id = 5)**


        | city_id | city |

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

        | 32 | Taipei |


        **Malaysia (country_id = 6)**


        | city_id | city |

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

        | 33 | Kuala Lumpur |

        | 42 | Klang |

        | 43 | Cheras |

        | 46 | Penang |

        | 47 | Bangsar |

        | 48 | Perak |

        | 49 | Selangor |

        | 50 | Johor |


        **United Kingdom (country_id = 7)**


        | city_id | city |

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

        | 34 | London |


        **Thailand (country_id = 9)**


        | city_id | city |

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

        | 35 | Bangkok |

        | 36 | Surat Thani |

        | 45 | Phuket |


        **Japan (country_id = 10)**


        | city_id | city |

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

        | 100 | Tokyo |

        | 101 | Sapporo |

        | 102 | Nagoya |

        | 103 | Osaka |

        | 104 | Yokohama |

        | 105 | Kyoto |

        | 160 | Miyazaki |

        | 161 | Matsuyama |

        | 162 | Akita |

        | 163 | Aomori |

        | 164 | Chiba |

        | 165 | Fukui |

        | 166 | Fukuoka |

        | 167 | Fukushima |

        | 168 | Gifu |

        | 169 | Hiroshima |

        | 170 | Kagoshima |

        | 171 | Kanazawa |

        | 172 | Kobe |

        | 173 | Kochi |

        | 174 | Kofu |

        | 175 | Kumamoto |

        | 176 | Maebashi |

        | 177 | Matsue |

        | 178 | Mito |

        | 179 | Morioka |

        | 180 | Nagano |

        | 181 | Nagasaki |

        | 182 | Naha |

        | 183 | Nara |

        | 184 | Niigata |

        | 185 | Oita |

        | 186 | Okayama |

        | 187 | Otsu |

        | 188 | Saga |

        | 189 | Saitama |

        | 190 | Sendai |

        | 191 | Shizuoka |

        | 192 | Takamatsu |

        | 193 | Tokushima |

        | 194 | Tottori |

        | 195 | Toyama |

        | 196 | Tsu |

        | 197 | Utsunomiya |

        | 198 | Wakayama |

        | 199 | Yamagata |

        | 200 | Yamaguchi |

        | 201 | Yamanashi |

        | 202 | Ibaraki |

        | 203 | Iwate |

        | 204 | Okinawa |

        | 205 | Tochigi |


        **Vietnam (country_id = 11)**


        | city_id | city |

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

        | 259 | Ho Chi Minh |

        | 260 | Da Nang |

        | 261 | Hanoi |

        | 262 | Haiphong |

        | 263 | Can Tho |


        **Argentina (country_id = 23)**


        | city_id | city |

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

        | 115 | Buenos Aires |

        | 116 | Córdoba |

        | 117 | Rosario |

        | 118 | Mendoza |


        **Bangladesh (country_id = 30)**


        | city_id | city |

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

        | 99 | Dhaka |


        **Bolivia (country_id = 38)**


        | city_id | city |

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

        | 119 | Santa Cruz de la Sierra |

        | 120 | La Paz |

        | 121 | Cochabamba |

        | 122 | El Alto |


        **Brazil (country_id = 43)**


        | city_id | city |

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

        | 123 | São Paulo |

        | 124 | Rio de Janeiro |

        | 125 | Salvador |

        | 126 | Brasília |

        | 270 | Copacabana |


        **Colombia (country_id = 59)**


        | city_id | city |

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

        | 127 | Bogotá |

        | 128 | Medellín |

        | 129 | Cali |

        | 130 | Barranquilla |


        **Costa Rica (country_id = 64)**


        | city_id | city |

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

        | 131 | San José |

        | 132 | Alajuela |

        | 133 | Cartago |

        | 134 | Heredia |


        **Ecuador (country_id = 75)**


        | city_id | city |

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

        | 135 | Quito |

        | 136 | Guayaquil |

        | 137 | Cuenca |

        | 138 | Santo Domingo |


        **Guatemala (country_id = 101)**


        | city_id | city |

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

        | 139 | Guatemala City |

        | 140 | Mixco |

        | 141 | Villa Nueva |

        | 142 | Quetzaltenango |


        **Mexico (country_id = 151)**


        | city_id | city |

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

        | 143 | Mexico City |

        | 144 | Guadalajara |

        | 145 | Monterrey |

        | 146 | Puebla |


        **New Zealand (country_id = 167)**


        | city_id | city |

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

        | 147 | Auckland |

        | 148 | Wellington |

        | 149 | Christchurch |

        | 150 | Hamilton |

        | 151 | Tauranga |


        **Nicaragua (country_id = 168)**


        | city_id | city |

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

        | 152 | Managua |

        | 153 | León |

        | 154 | Masaya |

        | 155 | Chinandega |

        | 271 | Granada |

        | 272 | Maderas |


        **Peru (country_id = 182)**


        | city_id | city |

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

        | 156 | Lima |

        | 157 | Arequipa |

        | 158 | Trujillo |

        | 159 | Chiclayo |
    CurrencyId:
      type: integer
      description: |
        Numeric currency id used inside `BranchCreateRequest.currency_id`.
        Each id is paired with the matching ISO 4217 code partners
        already use inside `PriceMap` (the string key under each
        provider entry).

        | id | code |
        |----|------|
        | 1 | AUD |
        | 2 | PHP |
        | 3 | SGD |
        | 4 | IDR |
        | 5 | TWD |
        | 6 | MYR |
        | 7 | GBP |
        | 8 | HKD |
        | 9 | THB |
        | 10 | JPY |
        | 11 | VND |
        | 12 | KHR |
        | 13 | BDT |
        | 14 | ARS |
        | 15 | BOB |
        | 16 | BRL |
        | 17 | COP |
        | 18 | CRC |
        | 19 | USD |
        | 20 | GTQ |
        | 21 | MXN |
        | 22 | NZD |
        | 23 | NIO |
        | 24 | PEN |
      enum:
        - 1
        - 2
        - 3
        - 4
        - 5
        - 6
        - 7
        - 8
        - 9
        - 10
        - 11
        - 12
        - 13
        - 14
        - 15
        - 16
        - 17
        - 18
        - 19
        - 20
        - 21
        - 22
        - 23
        - 24
      x-enum-varnames:
        - AUD
        - PHP
        - SGD
        - IDR
        - TWD
        - MYR
        - GBP
        - HKD
        - THB
        - JPY
        - VND
        - KHR
        - BDT
        - ARS
        - BOB
        - BRL
        - COP
        - CRC
        - USD
        - GTQ
        - MXN
        - NZD
        - NIO
        - PEN
    AvailableTimes:
      type: object
      description: |
        Weekly schedule keyed by weekday number as a string:
        `"0"` = Sunday … `"6"` = Saturday. Every day must have at least
        one slot. To make a section dark for a day, set
        `disabled: true` and pass any placeholder slot (the disabled
        flag wins).
      additionalProperties:
        $ref: '#/components/schemas/DaySchedule'
      example:
        '0':
          disabled: false
          slots:
            - startTime: 900
              endTime: 2200
        '1':
          disabled: false
          slots:
            - startTime: 900
              endTime: 2200
        '2':
          disabled: false
          slots:
            - startTime: 900
              endTime: 2200
        '3':
          disabled: false
          slots:
            - startTime: 900
              endTime: 2200
        '4':
          disabled: false
          slots:
            - startTime: 900
              endTime: 2200
        '5':
          disabled: false
          slots:
            - startTime: 1000
              endTime: 2300
        '6':
          disabled: false
          slots:
            - startTime: 1000
              endTime: 2300
    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
    DaySchedule:
      type: object
      properties:
        disabled:
          type: boolean
          default: false
        slots:
          type: array
          items:
            $ref: '#/components/schemas/TimeSlot'
      required:
        - slots
    TimeSlot:
      type: object
      description: |
        Military-time window inside a single day. Times are integers in
        24-hour form: `1430` = 14:30, `900` = 09:00, `0` = midnight,
        `2359` = end of day. Use multiple slots per day for split
        schedules (e.g. lunch + dinner).
      properties:
        startTime:
          type: integer
          minimum: 0
          maximum: 2359
          example: 900
        endTime:
          type: integer
          minimum: 0
          maximum: 2359
          example: 2200
      required:
        - startTime
        - endTime
  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
    NotFound:
      description: Resource does not exist (or is not visible to your credential).
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Envelope'
              - type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: resource_not_found
  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.

````