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

# List all products

> Returns paginated products for the organization your access token is scoped to, with optional filters on status, sku, barcode, opaqueId, categories, and price.

```http theme={null}
GET $BASE_URL/v1/products
```

**Returns the products of your access token's organization, newest first, in the standard [list envelope](/api/pagination) (`data` + `meta`). Deleted products are excluded.**

> **Auth:** `Authorization: Bearer <accessToken>` — an organization-scoped token from [Authentication](/api/authentication). Results are always scoped to the token's organization; you cannot see another organization's products.

### Query parameters

`limit` (default `50`, max `250`) and `cursor` follow the shared pagination contract — see [Pagination & filtering](/api/pagination).

### Filterable fields

Filters combine with each other (and with the organization scope) using AND.

| Field | Type | Match | Description |
| - | - | - | - |
| `f[status]` | number | exact / IN | Status code. Comma = IN (`f[status]=1,0`). Deleted records (`-99`) are never returned, regardless of the filter. |
| `f[sku]` | string | exact / IN | Stock keeping unit. |
| `f[barcode]` | string | exact / IN | Product barcode. |
| `f[opaqueId]` | string | exact / IN | Your external product id (see [menu sync](/api/process-transaction#menu-synchronization-lazy-sync)). |
| `f[categories]` | string | exact / IN | Category id — matches when the id is present in the product's category list. |
| `f[price]` | number | exact / IN + operators | Price. Operator syntax is supported: `f[price][gte]=100` (allowed operators: `gt`, `gte`, `lt`, `lte`). |

> Filtering on any other field returns `invalid_filter_field` (400). A non-numeric value on a numeric field returns `invalid_filter_value` (400); operator syntax on fields other than `price` returns `invalid_filter_value`, and an unknown operator name returns `invalid_filter_op` (400).

### Example

```bash theme={null}
curl --request GET \
  --url "$BASE_URL/v1/products?f%5Bstatus%5D=1&f%5Bprice%5D%5Bgte%5D=100&limit=50" \
  --header 'Authorization: Bearer <accessToken>'
```

### Response

```json theme={null}
{
  "data": [
    {
      "id": "1f64ec1b-445e-4762-a8c3-0d1e6d75afe6",
      "name": { "TR": "Latte", "EN": "Latte" },
      "organization": "07f84834-1184-4eb6-82f5-7eda13231fc3",
      "categories": [],
      "tags": [],
      "price": 120,
      "status": 1,
      "sku": "LATTE-M",
      "opaqueId": "pos-item-9001",
      "createdAt": "2026-09-18T08:24:08.415Z",
      "updatedAt": "2026-09-18T08:24:08.415Z"
    }
  ],
  "meta": {
    "total": 1,
    "limit": 50,
    "cursor": "eyJ0IjoiMjAyNi0wOS0uLi4"
  }
}
```

Notes:

* The sort order is fixed: `createdAt` descending (newest first). Pass `cursor` back exactly as received — do not parse it; a malformed cursor returns `invalid_cursor` (400), a `limit` outside 1–250 returns `invalid_limit` (400).
* `meta.total` counts the whole filtered set, independent of paging position; `meta.cursor` is only present when there is a next page.
* Optional fields that have no value (`barcode`, `description`, …) are omitted from the response — you will not receive `null`.
