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

# Price Books API

> Manage marketplace channel list-price rules: buyer formulas or markup percent.

Use the Developer Platform price books endpoints to manage marketplace channel list-price rules. Semantics match the workspace Price book UI (`/app/{organizationId}/pricebook`) and the Omni `manage_price_book` tool.

Price books **do not** change canonical product prices in Omni. They rewrite channel list price at publish/catalog time by platform (and optional store/country). A rule uses its buyer formula (`expression`) when one is set, and its markup percent (`marginValue`) otherwise.

## Authentication

Send an OmniCommerce API key or OAuth client-credentials token:

```http theme={null}
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json
```

| Endpoint | Required scope |
| - | - |
| `GET /api/v1/price-books` | `price_books:read` |
| `GET /api/v1/price-books/{ruleId}` | `price_books:read` |
| `POST /api/v1/price-books/preview` | `price_books:read` |
| `POST /api/v1/price-books` | `price_books:write` |
| `PATCH /api/v1/price-books/{ruleId}` | `price_books:write` |
| `DELETE /api/v1/price-books/{ruleId}` | `price_books:write` |

OAuth clients must include the scope on the client. API keys created from organization settings receive all developer scopes by default.

Session-authenticated calls must include `organizationId` in the query (GET) or JSON body (writes).

OpenAPI reference: [API reference](https://docs.omnicommerce.ai/api-reference.md) · Spec: [openapi.json](https://docs.omnicommerce.ai/openapi.json)

## List rules

```http theme={null}
GET /api/v1/price-books
```

### Query parameters

| Parameter | Description |
| - | - |
| `organizationId` | Organization scope for session auth. Optional for single-org OAuth/API keys. |
| `platform` | Filter: `shopee`, `lazada`, `tiktok`, `shopify`, or `whatsapp`. |
| `enabledOnly` | When `true`, only enabled rules. |

### Response

```json theme={null}
{
  "rules": [
    {
      "id": "11111111-1111-4111-8111-111111111111",
      "organizationId": "org_1",
      "platform": "shopee",
      "storeId": null,
      "country": "singapore",
      "base": "list_price",
      "marginType": "markup_percent",
      "marginValue": 10,
      "rounding": "currency_minor",
      "currency": "SGD",
      "expression": null,
      "enabled": true,
      "createdAt": "2026-07-01T00:00:00.000Z",
      "updatedAt": "2026-07-01T00:00:00.000Z"
    }
  ],
  "total": 1
}
```

## Create rule

```http theme={null}
POST /api/v1/price-books
```

### Request body

```json theme={null}
{
  "platform": "shopee",
  "marginValue": 10,
  "country": "singapore",
  "rounding": "currency_minor",
  "currency": "SGD",
  "enabled": true
}
```

| Field | Notes |
| - | - |
| `platform` | Required. `shopee`, `lazada`, `tiktok`, `shopify`, or `whatsapp` |
| `marginValue` | Required markup percent (0–500) |
| `storeId` | Optional store scope; omit/`null` = all stores for platform |
| `country` | Optional country scope |
| `rounding` | `none`, `currency_minor` (default), `nearest_0_05`, `nearest_1` |
| `currency` | Optional ISO 4217 for `currency_minor` rounding |
| `expression` | Optional buyer formula (max 500 chars). Replaces `marginValue` |
| `enabled` | Defaults to `true` |

### Buyer formulas

`expression` computes the listed price from `price`, the catalog price. For BrandsGateway products the catalog price is the landed price that the seller formula wrote, not the supplier cost.

`price` stays in the catalog currency (for example, SGD), including inside `IF` conditions and fixed charges. The formula runs before marketplace currency conversion. When `CURRENCY` names the destination currency, its result is already in that currency and is not converted again. Without `CURRENCY`, the formula result stays in the catalog currency and is converted to the destination currency when needed. Percentage markup rules continue to apply after conversion into the marketplace currency.

* Operators: `+ - * /`, parentheses, and percentages (`9%` is `0.09`).
* `CEILING(number, significance)` rounds up to a multiple, as in Excel.
* `IF(condition, value_if_true, value_if_false)` with `<`, `<=`, `>`, `>=`, `=`, `<>`.
* `CURRENCY(rate, "HKD")` multiplies by `rate` and marks the result as that ISO 4217 currency. A formula can name one currency.

```text theme={null}
CEILING((price + IF(price <= 500, 100, 200)) / 0.8038 * CURRENCY(6.2, "HKD"), 5)
```

Marketplace fee sheets usually give the payout of a listed price, such as `price * (1 - (9% + 2%) * (1 + 9%))` for a 100 → 88.01 Lazada payout. To list at a price that nets the catalog price, divide by that factor instead: `price / (1 - (9% + 2%) * (1 + 9%))` lists 100 at 113.62. The preview response sets `belowCatalogPrice: true` when a formula lists below the catalog price in the same currency.

A formula must use `price`. Formulas that could not be evaluated (wrong argument counts, a comparison outside `IF`, an invalid currency code) are rejected with `400` when the rule is saved. If a saved formula still cannot price a product (for example it divides by zero or produces a price of 0 or less), publish and price updates for that listing fail instead of sending the catalog price.

Scope uniqueness: one rule per `platform` + `storeId` + `country` combination.

Returns `201` with `{ "rule": { ... } }`.

## Get / update / delete

```http theme={null}
GET    /api/v1/price-books/{ruleId}
PATCH  /api/v1/price-books/{ruleId}
DELETE /api/v1/price-books/{ruleId}
```

Update accepts partial fields (`storeId`, `country`, `marginValue`, `rounding`, `currency`, `expression`, `enabled`). Send `"expression": null` to go back to markup percent. Platform cannot be changed after create.

## Preview channel list price

```http theme={null}
POST /api/v1/price-books/preview
```

```json theme={null}
{
  "platform": "shopee",
  "listPrice": 100,
  "country": "singapore",
  "currency": "SGD"
}
```

Optional `ruleId` forces a specific rule instead of most-specific matching.

### Response

```json theme={null}
{
  "listPrice": 100,
  "channelListPrice": 110,
  "applied": true,
  "source": "price_book",
  "markupPercent": 10,
  "rule": { "id": "…", "platform": "shopee", "marginValue": 10 }
}
```

When the rule has a buyer formula, `markupPercent` is omitted, and `currency` is set when the formula uses `CURRENCY`. If the matched formula cannot price `listPrice`, the response carries `formulaError` and `applied` is `false`. With a forced `ruleId`, that case returns `400`.

When no enabled rule matches, `applied` is `false`, `source` is `canonical`, and `channelListPrice` equals `listPrice`.

## Matching priority

Most specific enabled rule wins for a given platform/store/country:

1. platform + store + country
2. platform + store
3. platform + country
4. platform only

## Related

* Workspace UI: `/app/{organizationId}/pricebook`
* Omni tool: `manage_price_book` (default Omni tool)
* Product base price writes: `POST /api/v1/sync` (see [API reference](https://docs.omnicommerce.ai/api-reference.md)) or workspace product price update
* Promotions (discounts/campaigns): [Promotions API](https://docs.omnicommerce.ai/guides/promotions.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.