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

# Mix and Match Bundles

> Metafields and Metaobject Structure for Mix and Match Bundles

<Tabs>
  <Tab title="Structure">
    <ResponseField name="dynamic_add_ons_bundle_config" type="Metaobject">
      Mix and Match Settings Metaobject

      <Expandable title="fields" defaultOpen>
        <ResponseField name="bundle_id" type="string" required>
          ID of the bundle within the app
        </ResponseField>

        <ResponseField name="products" type="list of product references" required>
          List of unique product references selected within the app
        </ResponseField>

        <ResponseField name="categories" type="list of categories" required>
          Category Properties selected within the app

          <Expandable title="fields">
            <ResponseField name="title" type="string" required>
              Title of the Category
            </ResponseField>

            <ResponseField name="quantity" type="number">
              Maximum amount of products to be selected from this category.
              Only to be used when `Quantity as an option` is unchecked.
            </ResponseField>

            <ResponseField name="products" type="list of products" required>
              List of products and the selected variants with overriden prices

              <Expandable title="fields">
                <ResponseField name="id" type="number" required>
                  ID of the product
                </ResponseField>

                <ResponseField name="variants" type="{key: value}" required>
                  <Note>Object is a Key Value Pair `{key: value}`</Note>

                  <Expandable title="fields">
                    <ResponseField name="key" type="number" required>
                      Variant ID
                    </ResponseField>

                    <ResponseField name="value" type="decimal" required>
                      Overridden Price
                    </ResponseField>
                  </Expandable>
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="options_v2" type="list of options">
          Only present in case `Quantity as an option` is checked in the app. <br />

          <Expandable title="fields">
            <ResponseField name="title" type="string">
              Title of the Option
            </ResponseField>

            <ResponseField name="variant_id" type="string">
              Integer Variant ID of Quantity Option
            </ResponseField>

            <ResponseField name="quantity" type="number">
              Quantity of Products to be selected for this option
            </ResponseField>

            <ResponseField name="add_on" type="json">
              Add On Properties for the Option

              <Expandable title="fields">
                <ResponseField name="minimum" type="number">
                  Minimum number of Add Ons that can be selected
                </ResponseField>

                <ResponseField name="maximum" type="number">
                  Maximum number of Add Ons that can be selected
                </ResponseField>
              </Expandable>
            </ResponseField>

            <ResponseField name="add_on_products" type="list of products">
              Add-on products available for this option. When no option-specific products are configured, FoxSell uses the bundle-level `add_on_products`.

              <Expandable title="fields">
                <ResponseField name="id" type="number">
                  Integer Shopify Product ID
                </ResponseField>

                <ResponseField name="variants" type="{key: value}">
                  <Note>Object is a Key Value Pair `{key: value}`</Note>

                  <Expandable title="fields">
                    <ResponseField name="key" type="number">
                      Integer Shopify Product Variant ID
                    </ResponseField>

                    <ResponseField name="value" type="decimal">
                      Add-on variant price
                    </ResponseField>
                  </Expandable>
                </ResponseField>
              </Expandable>
            </ResponseField>

            <ResponseField name="price" type="json">
              Pricing Properties for the Option

              <Expandable title="fields">
                <ResponseField name="strategy" type="string">
                  Pricing Strategy (`fixed_pricing` or `dynamic_pricing`)
                </ResponseField>

                <ResponseField name="value" type="decimal">
                  Pricing Value:

                  * Price in case of `fixed_pricing` strategy
                  * Discount percentage in case of `dynamic_pricing` strategy
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="add_on_products" type="list of product references">
          List of product references selected as add ons within the app
        </ResponseField>

        <ResponseField name="add_on_product_properties" type="{key: value}">
          <Note>Object is a Key Value Pair `{key: value}`</Note>

          <Expandable title="fields">
            <ResponseField name="key" type="string">
              Shopify Product GID
              <Note>Example: `gid://shopify/Product/XXXXXXX`</Note>
            </ResponseField>

            <ResponseField name="value" type="json">
              <Expandable title="fields">
                <ResponseField name="variants" type="{key: value}">
                  <Note>Object is a Key Value Pair `{key: value}`</Note>

                  <Expandable title="fields">
                    <ResponseField name="key" type="string">
                      Shopify Variant GID
                      <Note>Example: `gid://shopify/ProductVariant/XXXXXXX`</Note>
                    </ResponseField>

                    <ResponseField name="value" type="decimal">
                      Price Override
                    </ResponseField>
                  </Expandable>
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="settings" type="json">
          To be used in case `Quantity as an option` is unchecked

          <Expandable title="fields">
            <ResponseField name="add_on" type="json">
              Add On Properties for the Option

              <Expandable title="fields">
                <ResponseField name="minimum" type="number">
                  Minimum number of Add Ons that can be selected
                </ResponseField>

                <ResponseField name="maximum" type="number">
                  Maximum number of Add Ons that can be selected
                </ResponseField>
              </Expandable>
            </ResponseField>

            <ResponseField name="price" type="json">
              Pricing Properties for the Option

              <Expandable title="fields">
                <ResponseField name="strategy" type="string">
                  Pricing Strategy (`fixed_pricing` or `dynamic_pricing`)
                </ResponseField>

                <ResponseField name="value" type="decimal">
                  Pricing Value:

                  * Price in case of `fixed_pricing` strategy
                  * Discount percentage in case of `dynamic_pricing` strategy
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="options" type="list of objects {key: value}" deprecated>
          <Warning>**Deprecated**: Use `options_v2` instead. This field will be removed in a future version.</Warning>
          <Note>List of objects with Key Value pair. `[{key: value}]`</Note>
          Only present in case `Quantity as an option` is checked in the app. <br />

          <Expandable title="fields">
            <ResponseField name="key" type="string">
              Bundle Variant GID
              <Note>Example: `gid://shopify/ProductVariant/XXXXXXX`</Note>
            </ResponseField>

            <ResponseField name="value" type="json">
              Option Properties

              <Expandable title="fields">
                <ResponseField name="title" type="string">
                  Title of the Option
                </ResponseField>

                <ResponseField name="quantity" type="number">
                  Quantity of Products to be selected for this option
                </ResponseField>

                <ResponseField name="add_on" type="json">
                  Add On Properties for the Option

                  <Expandable title="fields">
                    <ResponseField name="minimum" type="number">
                      Minimum number of Add Ons that can be selected
                    </ResponseField>

                    <ResponseField name="maximum" type="number">
                      Maximum number of Add Ons that can be selected
                    </ResponseField>
                  </Expandable>
                </ResponseField>

                <ResponseField name="price" type="json">
                  Pricing Properties for the Option

                  <Expandable title="fields">
                    <ResponseField name="strategy" type="string">
                      Pricing Strategy (`fixed_pricing` or `dynamic_pricing`)
                    </ResponseField>

                    <ResponseField name="value" type="decimal">
                      Pricing Value:

                      * Price in case of `fixed_pricing` strategy
                      * Discount percentage in case of `dynamic_pricing` strategy
                    </ResponseField>
                  </Expandable>
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="product_properties" type="{key: value}" deprecated>
          <Warning>**Deprecated**: Use `categories` instead. This field will be removed in a future version.</Warning>
          <Note>Object is a Key Value Pair `{key: value}`</Note>

          <Expandable title="fields">
            <ResponseField name="key" type="string">
              Shopify Product GID
              <Note>Example: `gid://shopify/Product/XXXXXXX`</Note>
            </ResponseField>

            <ResponseField name="value" type="json">
              <Expandable title="fields">
                <ResponseField name="variants" type="{key: value}">
                  <Note>Object is a Key Value Pair `{key: value}`</Note>

                  <Expandable title="fields">
                    <ResponseField name="key" type="string">
                      Shopify Variant GID
                      <Note>Example: `gid://shopify/ProductVariant/XXXXXXX`</Note>
                    </ResponseField>

                    <ResponseField name="value" type="decimal">
                      Price Override
                    </ResponseField>
                  </Expandable>
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Tab>

  <Tab title="Template Metafields">
    FoxSell stores the main Mix and Match bundle configuration in the `dynamic_add_ons_bundle` metafield. Every Mix and Match template reads that base metafield, and templates can also store additional template-specific JSON metafields on the same bundle product in the same app namespace.

    <Info>
      Template metafields are in addition to `dynamic_add_ons_bundle`, not replacements for it.
    </Info>

    <Tabs>
      <Tab title="Base Metafield">
        <ResponseField name="dynamic_add_ons_bundle" type="metaobject" required>
          Required for every Mix and Match template. This metafield stores the shared bundle structure documented in the Structure tab, including categories, products, options, add-ons, pricing settings, and the bundle ID.
        </ResponseField>

        ```liquid theme={null}
        {% liquid
          assign app_namespace = 'app--67872686081'
          assign dynamic_add_ons_bundle_config = product.metafields[app_namespace].dynamic_add_ons_bundle.value
        %}
        ```
      </Tab>

      <Tab title="Step Template">
        <ResponseField name="step" type="json">
          Additional settings used by the Step template.

          <Expandable title="fields" defaultOpen>
            <ResponseField name="categories_metadata" type="list of objects" required>
              Step display and quantity rules. Items are matched to `dynamic_add_ons_bundle.categories` by array index.

              <Expandable title="fields">
                <ResponseField name="title" type="string" required>
                  Step title shown in the template.
                </ResponseField>

                <ResponseField name="description" type="string">
                  Supporting text shown for the step.
                </ResponseField>

                <ResponseField name="min_quantity" type="number" required>
                  Minimum number of products the shopper must select in this step. `0` makes the step optional.
                </ResponseField>

                <ResponseField name="max_quantity" type="number" required>
                  Maximum number of products the shopper can select in this step. `10000` is used for unlimited maximum quantity.
                </ResponseField>
              </Expandable>
            </ResponseField>

            <ResponseField name="add_on_settings" type="json" required>
              Controls whether add-ons are picked automatically or shown as a separate step.

              <Expandable title="fields">
                <ResponseField name="strategy" type="string" required>
                  Add-on flow strategy. Values: `add_on_step` or `automatic_add`.
                </ResponseField>

                <ResponseField name="title" type="string">
                  Add-on step title. Used when `strategy` is `add_on_step`.
                </ResponseField>

                <ResponseField name="description" type="string">
                  Add-on step supporting text. Used when `strategy` is `add_on_step`.
                </ResponseField>

                <ResponseField name="enforce_single_add_on_variant" type="boolean">
                  Present when `strategy` is `automatic_add`. Add-ons are saved with one variant per product.
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>

        ```liquid theme={null}
        {% liquid
          assign app_namespace = 'app--67872686081'
          assign step_template_config = product.metafields[app_namespace].step.value
        %}
        ```

        ```json theme={null}
        {
          "categories_metadata": [
            {
              "title": "Step 1",
              "description": "Choose your cleanser",
              "min_quantity": 1,
              "max_quantity": 1
            }
          ],
          "add_on_settings": {
            "strategy": "add_on_step",
            "title": "Add-ons",
            "description": "Complete your routine"
          }
        }
        ```
      </Tab>

      <Tab title="Glow Template">
        <ResponseField name="glow" type="json">
          Additional settings used by the Glow template.

          <Expandable title="fields" defaultOpen>
            <ResponseField name="quantity_rules" type="json" required>
              Controls how option quantities are validated when the bundle uses quantity options.

              <Expandable title="fields">
                <ResponseField name="strategy" type="string" required>
                  Quantity matching rule. Values: `fixed` or `any`.
                </ResponseField>

                <ResponseField name="max" type="string" required>
                  Maximum quantity behavior. Values: `cap_at_highest` or `no_cap`.
                </ResponseField>
              </Expandable>
            </ResponseField>

            <ResponseField name="add_on_settings" type="json" required>
              Controls add-on flow and whether add-on products are shared or configured per option.

              <Expandable title="fields">
                <ResponseField name="strategy" type="string" required>
                  Add-on flow strategy. Values: `add_on_step` or `automatic_add`.
                </ResponseField>

                <ResponseField name="product_scope" type="string" required>
                  Add-on product scope. Values: `shared` or `per_option`.
                </ResponseField>

                <ResponseField name="enforce_single_add_on_variant" type="boolean">
                  Present when `strategy` is `automatic_add`. Add-ons are saved with one variant per product.
                </ResponseField>
              </Expandable>
            </ResponseField>
          </Expandable>
        </ResponseField>

        ```liquid theme={null}
        {% liquid
          assign app_namespace = 'app--67872686081'
          assign glow_template_config = product.metafields[app_namespace].glow.value
        %}
        ```

        ```json theme={null}
        {
          "quantity_rules": {
            "strategy": "fixed",
            "max": "cap_at_highest"
          },
          "add_on_settings": {
            "strategy": "add_on_step",
            "product_scope": "shared"
          }
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Liquid Section">
    <Tip>
      Add this snippet as a section in your theme to see the values of the metafield

      **Note**: The `namespace` and `key` are constant for all the stores
    </Tip>

    ```liquid theme={null}
    {% liquid
      assign app_namespace = 'app--67872686081'

      assign dynamic_add_ons_bundle_config = product.metafields[app_namespace].dynamic_add_ons_bundle.value
      assign bundle_id = dynamic_add_ons_bundle_config.bundle_id
      assign options = dynamic_add_ons_bundle_config.options_v2.value

      assign products = dynamic_add_ons_bundle_config.products.value

      assign add_on_products = dynamic_add_ons_bundle_config.add_on_products.value
      assign add_on_product_properties = dynamic_add_ons_bundle_config.add_on_product_properties.value

      assign categories = dynamic_add_ons_bundle_config.categories.value
      assign settings = dynamic_add_ons_bundle_config.settings.value
    %}

    <p><strong>Bundle ID</strong>: {{ bundle_id }}</p>

    <p><strong>Options:</strong></p>
    {% for option in options %}
      {{ option | json }}
    {% endfor %}

    <p><strong>Categories</strong></p>

    {% for category in categories %}
      <p><strong>Title:</strong> {{ category.title }}</p>
      <p><strong>Category Max Quantity (Only used if Options are Empty):</strong> {{ category.quantity }}</p>
      <p><strong>Products</strong></p>
      {% for p in category.products %}
        {% liquid
          assign int_product_id = p.id | times: 1
          assign product_ref = null
          paginate products by 128
            for ref in products
              if ref.id == int_product_id
                assign product_ref = ref
                break
              endif
            endfor
          endpaginate
          assign product_variants = p.variants
        %}
        <p><strong>Product ID:</strong> {{ p.id }}</p>
        <p><strong>Product Title:</strong> {{ product_ref.title }}</p>

        {% for variant_data in product_variants %}
          {% liquid
            assign variant_id = variant_data[0] | times: 1
            assign variant_price = variant_data[1]
            assign variant = product_ref.variants | where: 'id', variant_id | first
          %}

          {% if variant %}
            <p><strong>Variant ID:</strong> {{ variant_id }}</p>
            <p><strong>Variant Overridden Price:</strong> {{ variant_price }}</p>
            <p><strong>Variant Ref:</strong> {{ variant | json }}</p>
          {% endif %}
        {% endfor %}
      {% endfor %}
    {% endfor %}

    <p><strong> Add On Products</strong></p>
    {% for p in add_on_products %}
      {% assign product_gid = 'gid://shopify/Product/' | append: p.id %}
      <p><strong>Title:</strong> {{ p.title }}</p>
      <p><strong>Properties:</strong> (Format: {variant_id: price})</p>
      <p>{{ add_on_product_properties[product_gid] | json }}</p>
    {% endfor %}

    <p><strong>Settings</strong></p>
    {{ settings | json }}

    {% schema %}
    {
      "name": "FoxSell M&M Add Ons Test",
      "settings": [],
      "presets": [
        {
          "name": "FoxSell M&M Add Ons Test"
        }
      ]
    }
    {% endschema %}
    ```
  </Tab>

  <Tab title="Add to Cart Properties">
    Below is a simplified example that uses the `fetch` API to add a bundle to the cart. The `id` is the variant ID of the bundle variant and quantity is the amount of the variant that you want to add to the cart.

    A unique bundle identifier is generated using the bundle ID (assumed to be stored in a variable called `bundleId`) and the current date. This identifier is passed as a line item property, `__foxsell:dynamic_add_on_bundle_id`. The bundled items that need to be added are passed in the property `__foxsell:dynamic_add_on_bundle_items`.

    ```javascript theme={null}
    const currentDate = Date.now()
    const identifier = `${bundleId}_${currentDate}`

    let formData = {
      items: [
        {
          id: 51594067837240, // current bundle variant id
          quantity: 1,
          properties: {
            "__foxsell:dynamic_add_on_bundle_id": identifier,
            "__foxsell:dynamic_add_on_bundle_items": [
              {
                "variantId": 51594057843001,
                "quantity": 1,
                "category": "category1", // empty for 'addOns
                "type": "product", // 'product' or 'addOns'
                "properties": { "_location": "warehouse1" } // optional metadata per selection
              },
              {
                "variantId": 51594057843000,
                "quantity": 1,
                "type": "addOns", // 'product' or 'addOns'
                "properties": { "_location": "warehouse2" } // optional metadata per selection
              }
              ... // more items
            ]
          }
        }
      ]
    }

    fetch(window.Shopify.routes.root + 'cart/add.js', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(formData)
    })
    .then(response => {
      return response.json();
    })
    .catch((error) => {
      console.error('Error:', error);
    });
    ```
  </Tab>

  <Tab title="FoxSell JS Object">
    <Tip>
      Add this snippet in your `liquid` file to access bundle config via `window.foxsell` object

      **Note**: The `namespace` and `key` are constant for all the stores
    </Tip>

    ```liquid theme={null}
    {% liquid
      assign app_namespace = 'app--67872686081'

      assign dynamic_add_ons_bundle_config = product.metafields[app_namespace].dynamic_add_ons_bundle.value
      assign bundle_id = dynamic_add_ons_bundle_config.bundle_id
      assign options = dynamic_add_ons_bundle_config.options_v2.value

      assign products = dynamic_add_ons_bundle_config.products.value

      assign add_on_products = dynamic_add_ons_bundle_config.add_on_products.value
      assign add_on_product_properties = dynamic_add_ons_bundle_config.add_on_product_properties.value

      assign categories = dynamic_add_ons_bundle_config.categories.value
      assign settings = dynamic_add_ons_bundle_config.settings.value
    %}

    <script>
      window.foxsell = window.foxsell || {};
      window.foxsell.bundleConfig = {
        id: "{{ bundle_id }}",
        options: {{ options | json }},
        categories: {{ categories | json }},
        settings: {{ settings | json }},
        products: {{ products | json }},
        addOnProducts: {{ add_on_products | json }},
        addOnProductProperties: {
          {% for product_property in add_on_product_properties %}
            {% assign property_value = product_property[1] %}
            '{{ product_property[0] | replace: "gid://shopify/Product/", "" }}': {
              variants: {
                {% for variant in property_value['variants'] %}
                    '{{ variant[0] | replace: "gid://shopify/ProductVariant/", ""}}': {{ variant[1] }},
                {% endfor %}
              }
            },
          {% endfor %}
        }
      }
    </script>
    ```
  </Tab>

  <Tab title="Storefront API">
    <Tip>
      Use the Shopify Storefront API to query the metafields namespace containing your bundle configuration and any template-specific metafields.

      **Note**: The `namespace` and `key` are constant for all the stores
    </Tip>

    <Tabs>
      <Tab title="Base Config">
        Query `dynamic_add_ons_bundle` for every Mix and Match template.

        ```graphql theme={null}
        fragment Product on Product {
          id
          title
          handle
          dynamic_add_ons_bundle: metafield(
            namespace: "app--67872686081"
            key: "dynamic_add_ons_bundle"
          ) {
            id
            namespace
            key
            value
            type
            reference {
              ... on Metaobject {
                id
                handle
                type
                fields {
                  key
                  type
                  value
                  references(first: 10) {
                    nodes {
                      ... on Product {
                        id
                        handle
                        title
                      }
                    }
                  }
                }
              }
            }
          }
        }
        ```
      </Tab>

      <Tab title="Step Template">
        Query `step` along with `dynamic_add_ons_bundle` for Step template bundles. The `step.value` field contains a JSON string.

        ```graphql theme={null}
        fragment StepTemplateProduct on Product {
          id
          title
          handle
          dynamic_add_ons_bundle: metafield(
            namespace: "app--67872686081"
            key: "dynamic_add_ons_bundle"
          ) {
            id
            namespace
            key
            value
            type
            reference {
              ... on Metaobject {
                id
                handle
                type
                fields {
                  key
                  type
                  value
                }
              }
            }
          }
          step: metafield(
            namespace: "app--67872686081"
            key: "step"
          ) {
            id
            namespace
            key
            value
            type
          }
        }
        ```
      </Tab>

      <Tab title="Glow Template">
        Query `glow` along with `dynamic_add_ons_bundle` for Glow template bundles. The `glow.value` field contains a JSON string.

        ```graphql theme={null}
        fragment GlowTemplateProduct on Product {
          id
          title
          handle
          dynamic_add_ons_bundle: metafield(
            namespace: "app--67872686081"
            key: "dynamic_add_ons_bundle"
          ) {
            id
            namespace
            key
            value
            type
            reference {
              ... on Metaobject {
                id
                handle
                type
                fields {
                  key
                  type
                  value
                }
              }
            }
          }
          glow: metafield(
            namespace: "app--67872686081"
            key: "glow"
          ) {
            id
            namespace
            key
            value
            type
          }
        }
        ```
      </Tab>

      <Tab title="Add to Cart">
        <Tip>
          Use Shopify's Storefront API `cartCreate` mutation to add a Mix and Match Bundle to the cart.

          Bundle items are passed as cart line `attributes`.
        </Tip>

        ```graphql theme={null}
        mutation {
          cartCreate(input: {
            lines: [
              {
                merchandiseId: "gid://shopify/ProductVariant/XXXXXXX"
                quantity: 1
                attributes: [
                  {
                    key: "__foxsell:dynamic_add_on_bundle_id"
                    value: "BUNDLE_ID"
                  }
                  {
                    key: "__foxsell:dynamic_add_on_bundle_items"
                    value: "[{\"variantId\":VARIANT_ID_1,\"category\":\"Category1\",\"quantity\":1,\"type\":\"product\"},{\"variantId\":VARIANT_ID_2,\"category\":\"Category2\",\"quantity\":1,\"type\":\"product\",\"properties\":{\"Engraving\":\"Hello World\"}}]"
                  }
                ]
              }
            ]
          }) {
            cart {
              id
              checkoutUrl
              lines(first: 10) {
                edges {
                  node {
                    id
                    quantity
                    merchandise {
                      ... on ProductVariant {
                        id
                        title
                      }
                    }
                    attributes {
                      key
                      value
                    }
                  }
                }
              }
            }
            userErrors {
              field
              message
            }
          }
        }
        ```

        <ResponseField name="merchandiseId" type="string" required>
          The Shopify Product Variant GID of the bundle variant being added to the cart.
          <Note>Example: `gid://shopify/ProductVariant/48292426973435`</Note>
        </ResponseField>

        #### Cart Line Attributes

        <ResponseField name="__foxsell:dynamic_add_on_bundle_id" type="string" required>
          The bundle ID from FoxSell. This can be found in the `bundle_id` field of the `dynamic_add_ons_bundle_config` metaobject.
        </ResponseField>

        <ResponseField name="__foxsell:dynamic_add_on_bundle_items" type="string (JSON)" required>
          A JSON-encoded array of the selected bundle items. Each item in the array contains:

          <Expandable title="fields" defaultOpen>
            <ResponseField name="variantId" type="number" required>
              The integer Variant ID of the selected product
            </ResponseField>

            <ResponseField name="category" type="string" required>
              The category title this item belongs to (e.g. `"Top"`, `"Bottom"`). Use an empty string for add-on items.
            </ResponseField>

            <ResponseField name="quantity" type="number" required>
              The quantity of this item in the bundle
            </ResponseField>

            <ResponseField name="type" type="string" required>
              Either `"product"` for regular bundle items or `"addOns"` for add-on items
            </ResponseField>

            <ResponseField name="properties" type="object">
              Optional custom line item properties for the bundle component. Use this to pass custom data (e.g. engravings, personalizations) that will appear as line item properties on the order.
              <Note>Example: `{"Engraving": "Hello World"}`</Note>
            </ResponseField>
          </Expandable>
        </ResponseField>
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="FoxSell TS Types">
    ```typescript theme={null}
    interface ProductProperties {
      _location?: string;
      [key: string]: any;
    }

    interface SelectedProduct {
      variantId: string;
      quantity: number;
      category?: string;
      type: 'product' | 'addOns';
      properties?: ProductProperties;
    }

    interface PriceConfig {
      strategy: 'fixed_pricing' | 'dynamic_pricing';
      value: number;
    }

    interface AddOnConfig {
      minimum: number;
      maximum: number;
    }

    interface OptionV2 {
      title: string;
      variantId: string;
      quantity: number;
      addOn: AddOnConfig;
      additionalInfo: any[];
      price: PriceConfig;
    }

    interface CategoryProduct {
      id: string;
      variants: Record<string, number>;
    }

    interface Category {
      title: string;
      quantity: number;
      products: CategoryProduct[];
    }

    interface AddOnProductProperties {
      variants: Record<string, number>;
    }

    interface Settings {
      addOn: AddOnConfig;
      price: PriceConfig;
    }

    interface Config {
      bundleId: string;
      products: string[];
      categories: Category[];
      optionsV2: OptionV2[];
      addOnProducts: string[];
      addOnProductProperties: Record<string, AddOnProductProperties>;
      settings: Settings;
    }
    ```

    <Tip>
      Thanks to Sid from [Numbered](https://numbered.com/)
    </Tip>
  </Tab>
</Tabs>
