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

# Prices and promotions

> Price lists per branch, price synchronization and promotion calculation on a cart

| Permission | Endpoints |
| - | - |
| `pricing` | `GET /pricing/price-lists`, `GET /pricing/price-lists/{id}/prices`, `GET /pricing/promotions`, `POST /pricing/promotions/evaluate` |
| `pricing.write` | `PUT /pricing/price-lists/{id}/prices` |

> [!NOTE]
>
> These are **explicit** permissions: they must be checked one by one in **External integrations**; existing integrations do not receive them automatically.

Designed for online stores and other sales channels: query a branch's price lists, synchronize prices in bulk and calculate a cart's promotions with **the same engine as the FileXpress POS**, so the final price matches across all channels.

## Price lists

```http theme={null}
GET /pricing/price-lists?branch_id={branch_id}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `branch_id` | string | Yes | Branch. |

```bash theme={null}
curl "https://api.filexpress.app/api/external/v1/pricing/price-lists?branch_id=9d1c5a3e-..." \
  -H "X-API-Key: fx_xxxxxxxx"
```

```json theme={null}
{
  "status": true,
  "data": [
    {
      "id": "7b21...",
      "branch_id": "9d1c5a3e-...",
      "name": "Precio tienda en línea",
      "description": "Precios para el canal web",
      "is_default": false,
      "products_count": 342
    }
  ]
}
```

The default list (`is_default: true`) comes first, then the rest by name. **Errors**: `404` `Branch not found` (missing `branch_id` or from another business).

## Prices in a list

```http theme={null}
GET /pricing/price-lists/{id}/prices?search={text}&per_page=100
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `search` | string | No | Searches the product's name or SKU (max. 100). |
| `per_page` | integer | No | 1 to 500. Default 100. |
| `page` | integer | No | Page. |

```json theme={null}
{
  "status": true,
  "data": {
    "price_list": { "id": "7b21...", "name": "Precio tienda en línea", "is_default": false },
    "data": [
      {
        "product_id": "8f3a...",
        "sku": "CAF-250",
        "name": "Café molido 250 g",
        "price": 4.25,
        "base_price": 3.7611,
        "updated_at": "2026-10-01T09:15:00-06:00"
      }
    ],
    "pagination": { "current_page": 1, "last_page": 4, "per_page": 100, "total": 342 }
  }
}
```

| Field | Description |
| - | - |
| `price` | Product price in this list. |
| `base_price` | Price before taxes recorded on the product. |

Sorted by product name. **Errors**: `404` `Price list not found` (nonexistent or from another business); `422` validation.

## Update prices

Creates or updates a list's prices in bulk (synchronization from the store). Requires `pricing.write`.

```http theme={null}
PUT /pricing/price-lists/{id}/prices
```

| Field | Type | Required | Description |
| - | - | - | - |
| `prices` | array | Yes | 1 to 1000 items. |
| `prices[].product_id` | string | Yes | Product in the **same branch** as the list. |
| `prices[].price` | number | Yes | Price (greater than or equal to 0). |

```bash theme={null}
curl -X PUT https://api.filexpress.app/api/external/v1/pricing/price-lists/7b21.../prices \
  -H "X-API-Key: fx_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"prices":[{"product_id":"8f3a...","price":4.25},{"product_id":"8f3b...","price":12.00}]}'
```

```json theme={null}
{
  "status": true,
  "data": {
    "updated": 1,
    "rejected": ["8f3b..."]
  }
}
```

**Rules**

* If the product already has a price in the list it is updated; otherwise, it is created.
* Products that do not belong to the list's branch (or do not exist) are not applied and are returned in `rejected`; the rest are saved.
* If a `product_id` is repeated in the array, the last one is used.

## Promotions

```http theme={null}
GET /pricing/promotions?branch_id={branch_id}&active_only=true
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `branch_id` | string | Yes | Branch. |
| `active_only` | boolean | No | Default `true`: only those that are active and valid today. With `false` it returns all of the branch's promotions. |

```json theme={null}
{
  "status": true,
  "data": [
    {
      "id": "5c90...",
      "name": "15% en café",
      "description": null,
      "discount_type": "percentage",
      "discount_value": 15.0,
      "combo_price": null,
      "apply_on": "unit_price",
      "min_quantity": 2,
      "start_date": "2026-10-01",
      "end_date": "2026-10-31",
      "is_active": true,
      "priority": 10,
      "conditions": [
        { "type": "category", "id": "c12e..." }
      ]
    }
  ]
}
```

| Field | Description |
| - | - |
| `discount_type` | `percentage` (percentage), `fixed` (fixed amount), `override_price` (special price: `discount_value` is the new price) or `combo`. |
| `apply_on` | `unit_price`: discount per unit of the item. `subtotal`: discount on the item's subtotal, returned as an order discount. |
| `combo_price` | Combo price (`combo` only). |
| `min_quantity` | Minimum item quantity for it to apply. |
| `conditions[].type` | `product`, `category` or `brand`. With no conditions, the promotion applies to all products. |

Sorted from highest to lowest `priority`. **Errors**: `404` `Branch not found`.

## Calculate a cart's promotions

```http theme={null}
POST /pricing/promotions/evaluate
```

| Field | Type | Required | Description |
| - | - | - | - |
| `branch_id` | string | Yes | Branch whose promotions are evaluated. |
| `items` | array | Yes | 1 to 500 lines. |
| `items[].product_id` | string | Yes | Product. |
| `items[].quantity` | number | Yes | Quantity (greater than or equal to 0). |
| `items[].unit_price` | number | Yes | Unit price used in the cart. |

```bash theme={null}
curl -X POST https://api.filexpress.app/api/external/v1/pricing/promotions/evaluate \
  -H "X-API-Key: fx_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"branch_id":"9d1c5a3e-...","items":[{"product_id":"8f3a...","quantity":2,"unit_price":4.25},{"product_id":"8f3c...","quantity":1,"unit_price":6.00}]}'
```

```json theme={null}
{
  "status": true,
  "data": {
    "items": [
      {
        "product_id": "8f3a...",
        "quantity": 2,
        "unit_price": 4.25,
        "promo_discount": 0.6375,
        "promo_id": "5c90...",
        "promo_name": "15% en café",
        "promo_type": "percentage"
      },
      {
        "product_id": "8f3c...",
        "quantity": 1,
        "unit_price": 6.0,
        "promo_discount": 0,
        "promo_id": null,
        "promo_name": null,
        "promo_type": null
      }
    ],
    "order_discounts": [
      { "promo_id": "5d02...", "promo_name": "Combo desayuno", "promo_type": "combo", "discount": 1.25 }
    ],
    "totals": {
      "subtotal": 14.5,
      "item_discounts": 1.28,
      "order_discounts": 1.25,
      "total": 11.98
    }
  }
}
```

| Field | Description |
| - | - |
| `items[].promo_discount` | Discount **per unit** from the promotion applied to the item (promotions with `apply_on: unit_price`). |
| `order_discounts[]` | Order-level discounts: promotions with `apply_on: subtotal` and combos. |
| `totals.item_discounts` | Sum of `promo_discount × quantity`. |
| `totals.total` | `subtotal − item_discounts − order_discounts` (never less than 0). |

**Rules**

* Only active promotions whose start and end dates include today are evaluated.
* **Only one promotion wins per item**: the highest-priority one that applies to the product (by product, category or brand) and meets `min_quantity`.
* `percentage` discounts a percentage; `fixed`, a fixed amount (not exceeding the base); `override_price`, the difference down to the special price.
* A **combo** applies when all of its products are in the cart: it discounts the sum of their unit prices minus `combo_price`.
* If the branch has no valid promotions, items are returned without promotion fields and `order_discounts` is empty.
* The calculation does not save anything: use it to show the final price and then send the discounts when [creating the sale](https://docs.filexpress.app/en/docs/api/facturacion/ventas.md).

## Errors

| Code | When |
| - | - |
| `401` | Missing or invalid API key. |
| `403` | The integration does not have the `pricing` permission (or `pricing.write` to update prices). |
| `404` | `Branch not found` / `Price list not found` (nonexistent or from another business). |
| `422` | Validation (`Validation errors` with `errors`). |


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