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

# Variants

> Pizzas whose size and crust resolve to a different product id.

Some POS systems keep a separate PLU for each configuration of an item. In this example every pizza size and crust combination is its own product, but customers see one card per pizza and answer two questions. The answers decide which product id goes on the order. See [Products — variants](https://firespark.cloud/docs/concepts/products.md#variants-and-variant-groups) for the concepts.

## What this example includes

| Base product | Variant groups | Variant products | Notes |
| - | - | - | - |
| `pepperoni` | `size`, `crust` | `pep-m-thin`, `pep-m-thick`, `pep-l-thin`, `pep-l-thick` | Every combination exists |
| `hawaiian` | `size`, `crust` | `haw-m-thin`, `haw-m-thick`, `haw-l-thick` | No large thin crust; `haw-m-thick` is out of stock |

* Both bases share the same `size` and `crust` groups. Each option lists the products of both pizzas in `product_ids`.
* Large pizzas have an `extra-toppings` modifier group. Medium pizzas have none. Modifier groups live on the variant products, never on the base.
* Size options have an image and a description; crust options don't.

```mermaid theme={null}
flowchart TB
    HAW["hawaiian (base)"]
    HAW --> SIZE["size: medium | large"]
    HAW --> CRUST["crust: thin | thick"]
    SIZE --> R{"selected options"}
    CRUST --> R
    R -->|"medium + thin"| HMT["haw-m-thin"]
    R -->|"medium + thick"| HMK["haw-m-thick (out of stock)"]
    R -->|"large + thick"| HLK["haw-l-thick"]
    HLK --> TOP["extra-toppings"]
```

## What the customer sees

Taking the Hawaiian pizza as an example:

1. The card shows **From \$12.99**. The starting price was omitted, so the sync computed it from the cheapest selectable variant (`haw-m-thin`).
2. The detail view asks **Size** first, then **Crust**, and shows modifier groups only after a variant is resolved.
3. After **Large** is picked, **Thin** disappears because there is no large thin Hawaiian. **Thick** is then the only option left, so it's selected automatically.
4. After **Medium** is picked, **Thick** stays visible but disabled because `haw-m-thick` is out of stock.
5. The cart line is `haw-l-thick` at \$17.99, plus any extra toppings. The `hawaiian` base never appears in an order.

## Request

```bash theme={null}
curl --request PUT \
  --url 'https://firespark.cloud/api/integrations/v1/menus?fulfillment_id=delivery&store_id=downtown&channel_id=app&brand_id=0001' \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d @variants.json
```

```json variants.json theme={null}
{
  "style": "DEFAULT",
  "products": [
    {
      "id": "pepperoni",
      "name": {
        "en_us": "Pepperoni pizza",
        "es_ec": "Pizza de pepperoni"
      },
      "description": {
        "en_us": "Pick a size and crust.",
        "es_ec": "Elige tamaño y masa."
      },
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 0,
        "anchor_price": 0,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [],
        "variant_group_ids": [
          "size",
          "crust"
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "hawaiian",
      "name": {
        "en_us": "Hawaiian pizza",
        "es_ec": "Pizza hawaiana"
      },
      "description": {
        "en_us": "Pick a size and crust.",
        "es_ec": "Elige tamaño y masa."
      },
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 0,
        "anchor_price": 0,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [],
        "variant_group_ids": [
          "size",
          "crust"
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "pep-m-thin",
      "name": {
        "en_us": "Pepperoni pizza Medium Thin",
        "es_ec": "Pizza de pepperoni Mediana Delgada"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 11.99,
        "anchor_price": 11.99,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [],
        "variants": [
          {
            "product_id": "pepperoni",
            "variant_group_option_ids": [
              "medium",
              "thin"
            ]
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "pep-m-thick",
      "name": {
        "en_us": "Pepperoni pizza Medium Thick",
        "es_ec": "Pizza de pepperoni Mediana Gruesa"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 12.99,
        "anchor_price": 12.99,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [],
        "variants": [
          {
            "product_id": "pepperoni",
            "variant_group_option_ids": [
              "medium",
              "thick"
            ]
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "pep-l-thin",
      "name": {
        "en_us": "Pepperoni pizza Large Thin",
        "es_ec": "Pizza de pepperoni Grande Delgada"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 15.99,
        "anchor_price": 15.99,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [
          "extra-toppings"
        ],
        "modifiers": [],
        "variants": [
          {
            "product_id": "pepperoni",
            "variant_group_option_ids": [
              "large",
              "thin"
            ]
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "pep-l-thick",
      "name": {
        "en_us": "Pepperoni pizza Large Thick",
        "es_ec": "Pizza de pepperoni Grande Gruesa"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 16.99,
        "anchor_price": 16.99,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [
          "extra-toppings"
        ],
        "modifiers": [],
        "variants": [
          {
            "product_id": "pepperoni",
            "variant_group_option_ids": [
              "large",
              "thick"
            ]
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "haw-m-thin",
      "name": {
        "en_us": "Hawaiian pizza Medium Thin",
        "es_ec": "Pizza hawaiana Mediana Delgada"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 12.99,
        "anchor_price": 12.99,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [],
        "variants": [
          {
            "product_id": "hawaiian",
            "variant_group_option_ids": [
              "medium",
              "thin"
            ]
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "haw-m-thick",
      "name": {
        "en_us": "Hawaiian pizza Medium Thick",
        "es_ec": "Pizza hawaiana Mediana Gruesa"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 13.99,
        "anchor_price": 13.99,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [],
        "variants": [
          {
            "product_id": "hawaiian",
            "variant_group_option_ids": [
              "medium",
              "thick"
            ]
          }
        ]
      },
      "availability": {
        "status": "OUT_OF_STOCK",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "haw-l-thick",
      "name": {
        "en_us": "Hawaiian pizza Large Thick",
        "es_ec": "Pizza hawaiana Grande Gruesa"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 1,
        "maximum_quantity": 10,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 17.99,
        "anchor_price": 17.99,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [
          "extra-toppings"
        ],
        "modifiers": [],
        "variants": [
          {
            "product_id": "hawaiian",
            "variant_group_option_ids": [
              "large",
              "thick"
            ]
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "extra-cheese",
      "name": {
        "en_us": "Extra cheese",
        "es_ec": "Queso extra"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 0,
        "maximum_quantity": 3,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 1.5,
        "anchor_price": 1.5,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [
          {
            "modifier_group_id": "extra-toppings",
            "minimum_quantity": 0,
            "maximum_quantity": 2,
            "is_tax_inclusive": true,
            "tax_rate": 0.15,
            "price": 1.5,
            "anchor_price": 1.5,
            "anchor_price_percentage": 0,
            "default_quantity": 0,
            "charge_above_quantity": 0,
            "display_type": "QUANTITY"
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    },
    {
      "id": "jalapenos",
      "name": {
        "en_us": "Jalapeños",
        "es_ec": "Jalapeños"
      },
      "description": null,
      "image_url": null,
      "status": "ACTIVE",
      "bundled_items": [],
      "pricing": {
        "minimum_quantity": 0,
        "maximum_quantity": 3,
        "is_tax_inclusive": true,
        "tax_rate": 0.15,
        "price": 1.0,
        "anchor_price": 1.0,
        "anchor_price_percentage": 0,
        "modifier_group_ids": [],
        "modifiers": [
          {
            "modifier_group_id": "extra-toppings",
            "minimum_quantity": 0,
            "maximum_quantity": 2,
            "is_tax_inclusive": true,
            "tax_rate": 0.15,
            "price": 1.0,
            "anchor_price": 1.0,
            "anchor_price_percentage": 0,
            "default_quantity": 0,
            "charge_above_quantity": 0,
            "display_type": "QUANTITY"
          }
        ]
      },
      "availability": {
        "status": "AVAILABLE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    }
  ],
  "categories": [
    {
      "id": "pizzas",
      "name": {
        "en_us": "Pizzas",
        "es_ec": "Pizzas"
      },
      "description": null,
      "status": "ACTIVE",
      "items": [
        {
          "type": "PRODUCT",
          "id": "pepperoni"
        },
        {
          "type": "PRODUCT",
          "id": "hawaiian"
        }
      ],
      "availability": {
        "status": "ACTIVE",
        "schedules": null,
        "out_of_stock_until": null
      },
      "metadata": {}
    }
  ],
  "modifier_groups": [
    {
      "id": "extra-toppings",
      "title": {
        "en_us": "Extra toppings",
        "es_ec": "Toppings extra"
      },
      "description": null,
      "display_type": "LIST",
      "modifier_ids": [
        "extra-cheese",
        "jalapenos"
      ],
      "minimum_quantity": 0,
      "maximum_quantity": 3,
      "charge_above_quantity": null,
      "metadata": {}
    }
  ],
  "variant_groups": [
    {
      "id": "size",
      "title": {
        "en_us": "Size",
        "es_ec": "Tamaño"
      },
      "description": null,
      "display_type": "GRID",
      "metadata": {},
      "variants": [
        {
          "id": "medium",
          "name": {
            "en_us": "Medium",
            "es_ec": "Mediana"
          },
          "description": {
            "en_us": "30 cm",
            "es_ec": "30 cm"
          },
          "image_url": "https://cdn.example.com/pizza/medium.png",
          "product_ids": [
            "pep-m-thin",
            "pep-m-thick",
            "haw-m-thin",
            "haw-m-thick"
          ],
          "metadata": {}
        },
        {
          "id": "large",
          "name": {
            "en_us": "Large",
            "es_ec": "Grande"
          },
          "description": {
            "en_us": "40 cm",
            "es_ec": "40 cm"
          },
          "image_url": "https://cdn.example.com/pizza/large.png",
          "product_ids": [
            "pep-l-thin",
            "pep-l-thick",
            "haw-l-thick"
          ],
          "metadata": {}
        }
      ]
    },
    {
      "id": "crust",
      "title": {
        "en_us": "Crust",
        "es_ec": "Masa"
      },
      "description": null,
      "display_type": "LIST",
      "metadata": {},
      "variants": [
        {
          "id": "thin",
          "name": {
            "en_us": "Thin",
            "es_ec": "Delgada"
          },
          "description": null,
          "image_url": null,
          "product_ids": [
            "pep-m-thin",
            "pep-l-thin",
            "haw-m-thin"
          ],
          "metadata": {}
        },
        {
          "id": "thick",
          "name": {
            "en_us": "Thick",
            "es_ec": "Gruesa"
          },
          "description": null,
          "image_url": null,
          "product_ids": [
            "pep-m-thick",
            "pep-l-thick",
            "haw-m-thick",
            "haw-l-thick"
          ],
          "metadata": {}
        }
      ]
    }
  ],
  "recommendations": [],
  "menus": [
    {
      "id": "all-day",
      "name": {
        "en_us": "All day",
        "es_ec": "Todo el día"
      },
      "description": null,
      "schedules": null,
      "metadata": {},
      "category_ids": [
        "pizzas"
      ],
      "status": "ACTIVE"
    }
  ]
}
```

## Key points

* **The base opens the questions.** `hawaiian` sets `pricing.variant_group_ids: ["size", "crust"]` and has empty `modifier_group_ids`.
* **The base price is never charged.** Bases use `price: 0` here, so the card shows the computed **From** price (cards show it whenever `starting_price` is higher than `price`).
* **Each variant product says how it's selected.** `haw-l-thick` sets `pricing.variants: [{ "product_id": "hawaiian", "variant_group_option_ids": ["large", "thick"] }]`, with one option from each of the base's groups.
* **Each option lists its products.** The `large` option lists every large pizza of both bases. The sync rejects the payload if an option and a product's `pricing.variants` disagree.
* **Missing combinations are just omitted.** There's no `haw-l-thin`, so customers can't reach that combination.
* **Only categories list the base.** The variant products aren't in any category; they're included in the menu because a base in the menu points to them.
* **A product can be a variant of several bases.** Add one `pricing.variants` entry per base, each with its own options.

## Related

> [card] [Products — variants](https://firespark.cloud/docs/concepts/products.md#variants-and-variant-groups)
>
> Concepts and validation rules

> [card] [Multi-level modifier groups](https://firespark.cloud/docs/integrations-api/examples/menu-sync/multi-level-modifiers.md)
>
> Extras that stack on top of a product


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