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

# Pricing groups

> Sell the same item at different prices per branch tier — mall, airport, street — without cloning menus.

A **pricing group** is a named price tier on your business — think
"Mall", "Airport", "Street side". Each branch can be assigned to **at
most one** group, and each item can carry a separate price map **per
group**. When klikit builds a store's menu, the branch's group picks
which price map applies.

Use pricing groups when the *same menu* needs different price points per
location tier. If only one or two items differ at one store, a per-store
price override (`zoneStores` on
[`updateItemPrice`](/partner-api/api-reference/updateItemPrice)) is the
lighter tool.

## How a store price is resolved

For each item on a store (brand × branch) menu:

1. Does the **branch** have a `price_group_id`?
2. Does the item's `groupPrices` map have an entry for that group id?
3. If yes to both → that entry's provider/currency prices are used for
   the store.
4. Otherwise → the item's base `prices` map applies.

There is no partial merge: the group entry replaces the base prices
wholesale for that store, so a group entry must carry every provider you
sell on (klikit `"1"` included), same as the base
[`PriceMap`](/partner-api/api-reference#/components/schemas/PriceMap).

## Managing your groups

Full CRUD lives under `/v1/partner/price/groups` — reads behind
`brands:read`, writes behind `brands:update`:

| Operation           | Endpoint                                                                   | Notes                                                                        |
| ------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| List enabled groups | [`GET /price/groups`](/partner-api/api-reference/listPriceGroups)          | `[{id, title}, …]` for your business.                                        |
| Browse the catalog  | `GET /price/groups?all=true`                                               | Every selectable group platform-wide.                                        |
| Create / enable     | [`POST /price/groups`](/partner-api/api-reference/createPriceGroup)        | `{"title": "Mall"}` → `{id, title}`. Same-titled catalog entries are reused. |
| Replace the set     | [`PUT /price/groups`](/partner-api/api-reference/replacePriceGroups)       | `{"price_group_ids": [2, 3]}`; `[]` clears.                                  |
| Rename              | [`PATCH /price/groups/{id}`](/partner-api/api-reference/renamePriceGroup)  | Only while no *other* business uses the group.                               |
| Disable             | [`DELETE /price/groups/{id}`](/partner-api/api-reference/deletePriceGroup) | Removes it from your set; idempotent.                                        |

Two catalog realities behind create/rename/disable:

* **The catalog is shared and capped at 64 groups platform-wide.**
  `POST` reuses an existing entry with the same title
  (case-insensitive) instead of minting a duplicate; a full catalog
  returns `400` — pick an existing title from `?all=true` instead.
* **Rename and disable never touch other businesses.** Rename is
  rejected while another business has the group enabled, and disable
  only clears the group from *your* set — the catalog entry survives.

Your current branch assignments are visible on
[`listBranches`](/partner-api/api-reference/listBranches) /
[`getBranch`](/partner-api/api-reference/getBranch): each branch carries
`price_group_id` and `price_group_title` (`null` = base prices).

## Setting per-group prices on items

`groupPrices` is accepted anywhere `prices` is:
[`createItem`](/partner-api/api-reference/createItem),
[`updateItem`](/partner-api/api-reference/updateItem), and
[`updateItemPrice`](/partner-api/api-reference/updateItemPrice). The
shape is one extra nesting level over `PriceMap` — the outer key is the
**group id as a string**:

```bash theme={null}
curl -sS -u "$KEY:$SECRET" -X PATCH \
  "$BASE/menus/items/price/306077" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": {
      "1": { "IDR": { "price": 50000, "takeAwayPrice": 50000 } }
    },
    "groupPrices": {
      "2": { "1": { "IDR": { "price": 65000, "takeAwayPrice": 65000 } } },
      "3": { "1": { "IDR": { "price": 80000, "takeAwayPrice": 80000 } } }
    }
  }'
```

Read as: base price IDR 50,000; branches in group `2` ("Mall") sell at
65,000; branches in group `3` ("Airport") at 80,000. Prices are in
display units (not cents), exactly like the base `PriceMap`.

Tier prices can also be set at **creation time** — when building a menu
via [`createItem`](/partner-api/api-reference/createItem), include
`groupPrices` next to `prices` in the same payload:

```bash theme={null}
curl -sS -u "$KEY:$SECRET" -X POST \
  "$BASE/menus/items" \
  -H "Content-Type: application/json" \
  -d '{
    "title":        { "en": "Margherita Pizza" },
    "description":  { "en": "Tomato, mozzarella, basil" },
    "isEnabled":    true,
    "vat":          0,
    "visibilities": { "1": true },
    "prices": {
      "1": { "IDR": { "price": 85000, "takeAwayPrice": 85000 } }
    },
    "groupPrices": {
      "2": { "1": { "IDR": { "price": 99000, "takeAwayPrice": 99000 } } },
      "3": { "1": { "IDR": { "price": 120000, "takeAwayPrice": 120000 } } }
    },
    "fromAPI": true
  }'
```

The same field works on modifier options (`onlyModifier: true`), so a
"Large" upcharge can differ per tier too.

Business-level edits like the one above follow the same rollout
round-trip as any other menu change — see the
[menu-publish reference](/partner-api/api-reference#tag/menu-publish):

1. `POST /menus/sync` — replay business state into the store overrides.
2. `POST /menus/publish` — rebuild the cached store menu blob that
   [`getMenu`](/partner-api/api-reference/getMenu) serves.

## Assigning a branch to a pricing group

Set `price_group_id` via
[`updateBranch`](/partner-api/api-reference/updateBranch):

```bash theme={null}
curl -sS -u "$KEY:$SECRET" -X PATCH \
  "$BASE/branches/1293" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Kuta Beachwalk", "price_group_id": 2 }'
```

<Warning>
  Branch updates treat a **missing** `price_group_id` as "clear it". If the
  branch currently has a pricing group and your `PATCH` body omits the
  field, the assignment is set back to `null` and the branch reverts to
  base prices. Once a branch is on a pricing group, include
  `price_group_id` in **every** branch update you send.
</Warning>

## The full recipe

1. `POST /price/groups {"title": "Mall"}` → note the returned `id`.
2. `PATCH /branches/{id}` with `price_group_id` on each mall branch.
3. `PATCH /menus/items/price/{itemID}` with `groupPrices` keyed by that
   group id, for every item that prices differently.
4. `POST /menus/sync`, then `POST /menus/publish`.
5. Verify with [`getMenu`](/partner-api/api-reference/getMenu) for a
   branch on the group.

## Gotchas

* **The omit-clears-it PATCH semantic** on branch updates (warning
  above). This is the most common way an integration silently knocks a
  branch back to base prices.
* **Group entries don't inherit.** A `groupPrices["2"]` entry with only
  provider `"1"` leaves your aggregator providers without a price on
  mall branches — repeat every provider/currency pair you sell on.
* **Disabling a group doesn't unprice it.** Branches keep their
  `price_group_id` and items keep their `groupPrices` entries; disable
  only trims the list. Move branches off the group first if you want
  them back on base prices.
* **Sync + publish still apply.** A group price edit that "didn't take"
  is almost always a skipped `publishMenu` — the same trap as every
  other menu write.
