Flavours Partner API

Product

APIs for managing products

List ProductsGET

List all products with pagination and filtering options. per_page is capped at 100: a larger value is clamped to 100, never rejected. Default mode (pagination=page or omitted) is offset pagination and returns the paginator shown below. Keyset mode (pagination=keyset) orders by product id ascending, ignores page, sort and key_by (a sort or key_by that is not a product column is still rejected with 422), and is never cached; data then is an object with data (the products), per_page, after_id (echo of the request), next_after_id (pass it as after_id for the next page; null on the last page), has_more and total (an integer on the first request, when after_id is omitted, null afterwards). Products deleted between two keyset requests do not cause later products to be skipped, and an after_id that is unknown or belongs to another market only positions the cursor inside the caller's own products. A keyset walk is not a snapshot: a product created during the walk with an id below the cursor is not returned, so take a changes-feed position before the first page and replay the feed from it after the last page.

Create ProductPOST

Create a new product with its modifiers, prices, ingredients and recipes.

Detail ProductGET

Get details of a specific product by its ID.

Update ProductPOST

Update an existing product and its modifiers, prices, ingredients and recipes.

Delete ProductDELETE

Delete a product by its ID.

Create Product (Alias)POST

Alias path for `POST /api/v2/products` (routes/api/product.php binds both to ProductController::create). Identical single-product body, response and behaviour — it does not create multiple products.

Delete ProductsDELETE

Delete multiple products by their IDs.

Quick Create ProductPOST

Quickly create a product with a single option.

Quick Create Product (Alias)POST

Alias path for `POST /api/v2/products/quick` (routes/api/product.php binds both to ProductController::majorCreate). Identical single-product body, response and behaviour — it does not create multiple products.

Quick Update ProductPOST

Quickly update a product and its single option.

Sort ProductsPOST

Reorder products within a market and branch.

Delete Products (Alias)DELETE

Alias path for `DELETE /api/v2/products/multi` (routes/api/product.php binds both to ProductController::manyDelete). Identical `products_ids` body, response and behaviour.

List Product CategoriesGET

List product categories with pagination, search, sector/market/branch filtering and the display order: the market order, or with filter[branch_id] the branch order (a branch that never sorted follows the market order, categories the branch has no row for come after its own order). sort_level=market returns the market order even when filter[branch_id] is sent; sort_level=branch requires filter[branch_id].

Create Product CategoryPOST

Create a product category with localized names and at least one of an emoji icon or an image URL (either alone is accepted; neither is rejected).

Detail Product CategoryGET

Get a product category by its ID, including its linked sectors.

Update Product CategoryPOST

Update a product category's icon, image URL or localized names.

Delete Product CategoryDELETE

Delete a product category by its ID.

Sort Product CategoriesPOST

Replace the product category display order. Open to any order-page user (platform admin/merchant or a subscriber). A branch-bound subscriber sorts their OWN branch's order when the request carries a branch_id (any other branch id is ignored, never written) or omits it, and sorts the MARKET-wide order when the request carries an explicit branch_id null (the Product Categories page), which also requires the PRODUCT_CATEGORY_UPDATE grant (403 without it). An unbound saver (admin/merchant or an account-owner subscriber with no branch_id) chooses the scope with branch_id: send a branch_id in the caller's own market to set that branch's per-branch order, or send null/omit it to set the single market-wide global order (category_product_sorts with branch_id null). market_id is the subscriber's own market, or the request market_id for an admin/merchant (ownership-verified). The list with filter[branch_id] reads the branch order; a branch that never sorted follows the market order, and categories the branch has no row for come after its own order, in market order.

Product Category Stats — SummaryGET

Product counts across every product in the caller's scope (all categories plus uncategorized).

Product Category Stats — UncategorizedGET

Product counts for products with no category (category_id IS NULL). Reached at /v2/product-categories/none/stats.

Product Category Stats — By CategoryGET

Product counts (total / active / inactive / sellable) for a single category.

Merge Product CategoriesPOST

Merge one or more duplicate canonical categories into a single winner: re-points products, per-branch sort order and kitchen mappings from the losers onto the winner, migrates per-tenant overrides, then soft-deletes the losers. Order history is never touched. Admin only.

Promote Product CategoryPOST

Promote a private tenant category into a curated global category, discoverable and adoptable by other markets. Admin or merchant only. Optionally attach it to one or more sectors via sector_ids.

Set Product Category StatusPOST

Activate or deactivate a category for the caller's market by writing the caller's per-market is_active override (adopting/creating the config if the market has none yet for a visible global). Tenant-scoped: only the caller's own or in-sector categories, never another market's. Never mints a duplicate canonical.

Similar Product CategoriesGET

Advisory lookup that returns up to 10 existing categories whose canonical name or the caller market's override name equals or contains the given name_en/name_ar, so the client can warn about a near-duplicate before creating one. At least one of name_en/name_ar is required. Tenant-scoped: never returns another market's private categories or overrides. Exact matches are ordered first, then contains, then by name.

List Product IngredientsGET

List the ingredients attached to a product.

Create Product IngredientPOST

Attach an ingredient to a product.

Detail Product IngredientGET

Get a single ingredient attached to a product.

Update Product IngredientPOST

Update an ingredient attached to a product.

Delete Product IngredientDELETE

Detach an ingredient from a product.

List Product ModifiersGET

List the modifiers of a product.

Create Product ModifierPOST

Add a modifier to a product.

Detail Product ModifierGET

Get a single modifier of a product.

Update Product ModifierPOST

Update a modifier of a product.

Delete Product ModifierDELETE

Remove a modifier from a product.

List Product PricesGET

List the payment-method prices of a product.

Upsert Product PricePOST

Add or update a product price for a payment method or order type.

Detail Product PriceGET

Get a single price of a product.

Delete Product PriceDELETE

Remove a price from a product.

List Product RecipesGET

List the recipes attached to a product.

Create Product RecipePOST

Attach a recipe to a product.

Detail Product RecipeGET

Get a single recipe attached to a product.

Update Product RecipePOST

Update a recipe attached to a product.

Delete Product RecipeDELETE

Detach a recipe from a product.

List Addon GroupsGET

List the business add-on group library.

Create Addon GroupPOST

Add a reusable add-on group to the business library.

Detail Addon GroupGET

Get a single add-on group with its options.

Update Addon GroupPOST

Rename an add-on group or change its selection limits.

Delete Addon GroupDELETE

Delete an add-on group and its options (blocked while attached to products).

List Addon Group OptionsGET

Page through every option of an add-on group, in the same id order the product list embeds them. The product list serves at most 100 options per group (`addon_groups[].options`, with `options_count` at `X-API-Version >= 2.9.0`); this is the overflow fetch for the rest. `per_page` is capped at 100.

Create Addon Group OptionPOST

Add an option to an add-on group with its pricing mode.

Detail Addon Group OptionGET

Get a single option of an add-on group.

Update Addon Group OptionPOST

Update an option of an add-on group, including its pricing mode.

Delete Addon Group OptionDELETE

Remove an option from an add-on group.

List Product Addon GroupsGET

List the add-on groups attached to a product, in their per-product order.

Sort Product Addon GroupsPOST

Reorder the add-on groups attached to a product.

Attach Addon GroupPOST

Attach an add-on group to a product, appended after the groups it already carries.

Detach Addon GroupDELETE

Detach an add-on group from a product. The group and its options stay in the library.

Barcode VariantsGET

Paginated, searchable list of every barcode-bearing modifier for the export drawer table. Each row carries the owning product (localized name + id), the variant's own localized name and its `kind` (`OPTION` for a product option, `ADD_ON` for an add-on, `ADD_ON_GROUP` for an add-on-group option), the `barcode` value and `barcode_type`, and the `id` the frontend sends back to `/export/product-barcode`. Only rows with a non-empty barcode are returned, scoped to the caller's tenant (market). `search` matches the barcode, the variant name, and the product name. Add-on and add-on-group barcodes are included (the older product-key export dropped them).

Barcode TypesGET

List every supported barcode symbology with its validation rule set (charset, min/max length, whether a check digit is validated/auto-computed). The frontend mirrors this exact rule set for parity.

Generate BarcodePOST

Produce a valid barcode value for a symbology. Code128/Code39/Code93 derive from the SKU when supplied (or a random alphanumeric); retail types (EAN/UPC) return a check-digit-valid number. Never emits an invalid retail code.

Preview BarcodeGET

Stream a freshly rendered PNG for a modifier's stored barcode, or for a what-if value/type override, on a white (default) or transparent background. `padding` (boolean, default true) wraps the bars in a uniform quiet-zone margin (white or transparent to match the background); send `padding=0` for bars flush to the edge. Nothing is stored. Responds with image/png bytes.

List Modifier IngredientsGET

List the ingredients attached to a product modifier.

Create Modifier IngredientPOST

Attach an ingredient to a product modifier.

Detail Modifier IngredientGET

Get a single ingredient attached to a product modifier.

Update Modifier IngredientPOST

Update an ingredient attached to a product modifier.

Delete Modifier IngredientDELETE

Detach an ingredient from a product modifier.

Preview Modifier ChargePOST

Compute a per-modifier before/after price, tax, and total for a paginated list of product modifiers against a priceable (order type or payment method), reflecting saved overrides or a staged overrides payload.

List Modifier PricesGET

List the payment-method prices of a product modifier.

Upsert Modifier PricePOST

Add or update a product modifier price for a payment method or order type.

Detail Modifier PriceGET

Get a single price of a product modifier.

Delete Modifier PriceDELETE

Remove a price from a product modifier.

List Modifier RecipesGET

List the recipes attached to a product modifier.

Create Modifier RecipePOST

Attach a recipe to a product modifier.

Detail Modifier RecipeGET

Get a single recipe attached to a product modifier.

Update Modifier RecipePOST

Update a recipe attached to a product modifier.

Delete Modifier RecipeDELETE

Detach a recipe from a product modifier.

Generate SKUPOST

Generate a random SKU that is not currently used by any option/add-on in the given market (and branch, when given). The value is not reserved — it is still validated for uniqueness again when the caller submits it on create/update.