# StoreConnect Support

This guide is for developers building custom storefront experiences on top of StoreConnect's Salesforce CPQ bundles. It explains the storefront endpoints that initialize a bundle configuration, apply product-option changes (including in bulk), and add the configured bundle to the cart.

Storefront endpoints are not part of the bearer-token REST API described in [StoreConnect API](storeconnect-api). They authenticate with the visitor's storefront session cookie and return JavaScript that the storefront evaluates. Call them from within the storefront (same origin), where the session cookie and CSRF token are already in place. CPQ must be enabled on the store or these routes do not exist.

For the store-operator setup of CPQ bundles, see [Product bundles with Salesforce CPQ](product-bundles-with-salesforce-cpq).

:::note
This guide covers CPQ bundles only. If your store uses StoreConnect native bundles instead, use the endpoint described in [Add StoreConnect native bundles to the cart](product-bundle-cart-endpoint). To add several pre-chosen products to the cart in one request without any bundle, see [Direct to cart link](direct-to-cart).
:::

## The storefront bundle experience

A CPQ bundle is built up as a configuration that lives in the visitor's session, keyed by the session and the bundle's lead product. There is no bundle ID to pass around. Instead, the session cookie is the handle that ties every request together.

The bundle lifecycle has three stages:

1. Initialize the configuration for a lead product.
2. Apply product-option changes, either one at a time or many at once with the bulk endpoint.
3. Add the configured bundle to the cart.

Every request in a single flow must carry the same session cookie.

## Endpoints

| Method and path | Purpose |
|---|---|
| `POST /cpq/bundles` | Initialize a bundle configuration for a lead product. |
| `PATCH /cpq/bundles/{lead_product_sfid}/product_options/{sfid}` | Apply a single product-option change. |
| `PATCH /cpq/bundles/{lead_product_sfid}/product_options/bulk_update` | Apply many product-option changes in one request. |
| `POST /cpq/bundles/{lead_product_sfid}/add_to_cart` | Add the configured bundle to the cart. |
| `POST /cpq/bundles/{lead_product_sfid}/add_preset_to_cart` | Build a known configuration and add it to the cart in a single request. |
| `POST /cart/cart_items` | Add one or more already-configured bundles — optionally alongside standalone products — to the cart in a single request. Returns JSON. |

`lead_product_sfid` is the Salesforce ID of the bundle's lead product.

## Flow A — build a bundle, then add it to the cart

Use this flow when the customer configures the bundle interactively.

### Step 1 — Initialize the configuration

```
POST /cpq/bundles
```

Form body:

```
lead_product[sfid] = a1r0p000000i6eYAAW
```

This creates the default configuration in the session and renders the bundle edit form. You must initialize before applying any option changes. The option endpoints fail with a "bundle not initialized" error if no configuration exists in the session yet.

### Step 2 — Apply product-option changes in bulk

```
PATCH /cpq/bundles/{lead_product_sfid}/product_options/bulk_update
```

Send the full set of option changes in a single `product_options` collection. Each entry's `sfid` is a CPQ product option ID, and `feature` is the product feature ID it belongs to:

```
product_options[][sfid]          = a2X0p000000AbcdEAE
product_options[][quantity]      = 2
product_options[][feature]       = a2Y0p000000FghiEAE
product_options[][single_option] = false

product_options[][sfid]          = a2X0p000000JklmEAE
product_options[][quantity]      = 1
product_options[][feature]       = a2Y0p000000NopqEAE
```

Per-entry fields:

- `sfid` — the CPQ product option ID (required).
- `quantity` — the quantity for this option.
- `feature` — the CPQ product feature ID the option belongs to.
- `single_option` — `true` or `false`; whether the feature allows only one option.
- `variants` — a map of variant selections, where the keys are encoded variant values.

Why bulk: the endpoint takes the session lock once, loads the configuration once, applies every change, and renders the price once at the end — roughly N times faster than sending N single-option requests for the same inputs.

:::warning
The bulk endpoint is fail-fast. If any single entry is invalid and raises an error, the entire bundle configuration is reset to its initial state, not just the offending entry. Validate your entries before sending them.
:::

:::note
The number of entries per request is capped per store by the `cpq.bulk_options.max_batch_size` store variable (default 50). Exceeding the cap returns a bad-request response and applies none of the changes. Split larger configurations across multiple bulk requests.
:::

You can call `bulk_update` repeatedly; each call layers onto the same session configuration. The single-option endpoint (`product_options/{sfid}`) does the same work one option at a time and is what the default storefront uses.

### Step 3 — Add the bundle to the cart

```
POST /cpq/bundles/{lead_product_sfid}/add_to_cart
```

Form body:

```
quantity = 1
after    = cart
```

- `quantity` — number of bundles to add (minimum 1).
- `after` — where to redirect after adding: `cart`, `checkout`, or `product`. Any other value returns the visitor to the page they came from.

On success, the response redirects to the chosen page and the configuration is cleared from the session. If the configuration is incomplete or invalid (for example, a required feature has no option selected, or a quantity rule is not satisfied) the request fails and the bundle edit form is re-rendered with an error. A bundle must pass validation before it can be added to the cart.

## Flow B — add a known configuration in one request

If you already know the full bundle configuration, skip the initialize and option steps entirely:

```
POST /cpq/bundles/{lead_product_sfid}/add_preset_to_cart
```

Form body:

```
bundle[products][] = 01t0p000001eJrlAAE
bundle[products][] = 01t0p000001eJsqAAE
quantity           = 1
```

- `bundle[products][]` — the product IDs to include in the bundle. Each is matched to its product option on the lead product; the default quantity for each option is applied. A product supplied as a variant is resolved to its master product option automatically.
- `quantity` — number of bundles to add (minimum 1).

This maps the supplied products to a complete configuration, writes it to the session, and adds it to the cart in a single request. On success it redirects to the cart.

## Flow C — add several configured bundles (and standalone items) in one request

When a customer has configured more than one bundle, or a mix of bundles and standalone products, you can add them all to the cart in a single request instead of calling `add_to_cart` once per bundle. Cart validation runs once, at the end, rather than after every item, which avoids intermediate states where independent bundles sharing a product interfere with each other.

```
POST /cart/cart_items
```

Unlike the other endpoints in this guide, this one returns JSON rather than JavaScript, so it suits a custom front end that manages its own rendering.

Each configured bundle must already exist in the session. Build it first with Flow A, steps 1 and 2. Reference it by its lead product ID; there is no bundle ID to pass.

Request body:

```
bundles[][lead_product_id] = a1r0p000000i6eYAAW
bundles[][quantity]        = 1

items[][product_id]        = 01t0p000001eJrlAAE
items[][quantity]          = 2
```

- `bundles` — the configured bundles to add. Each entry needs `lead_product_id` (the bundle's lead product) and `quantity` (number of bundles, minimum 1). The configuration is read from the session cache; a lead product with no configuration in the session is skipped.
- `items` — standalone products to add. Each entry needs `product_id` and `quantity`.

Send either collection on its own or both together. The combined number of bundles and items is capped per store by the `bulk_add_to_cart.max_batch_size` store variable (default 100); exceeding the cap adds nothing and returns a bad-request response.

The response is JSON:

- Success: `{ "success": true, "cart_item_count": 7 }`
- Failure: `{ "success": false, "errors": ["Product name: reason", ...] }` with a 422 status.

:::note
This is an all-or-nothing endpoint for external callers. Whatever the outcome, every referenced bundle configuration is cleared from the session afterward. If a call fails, re-initialize the bundle configuration (Flow A) before trying again. Do not resubmit against the now-cleared session.
:::

:::note
`POST /products/bulk_add_to_cart` is a deprecated alias for this endpoint, kept so existing custom code on the old path keeps working. Use `POST /cart/cart_items` for new work.
:::

## Calling the endpoints

The endpoints are designed to be called from within the storefront, where the session cookie and CSRF token are already present and Rails handles them automatically through the bundle forms. A request from custom JavaScript on the storefront looks like this:

```js

const body = new URLSearchParams();
body.append("product_options[][sfid]", "a2X0p000000AbcdEAE");
body.append("product_options[][quantity]", "2");
body.append("product_options[][feature]", "a2Y0p000000FghiEAE");
body.append("product_options[][sfid]", "a2X0p000000JklmEAE");
body.append("product_options[][quantity]", "1");
body.append("product_options[][feature]", "a2Y0p000000NopqEAE");

await fetch(`/cpq/bundles/${leadProductSfid}/product_options/bulk_update`, {
  method: "PATCH",
  headers: {
    "X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content,
    "X-Requested-With": "XMLHttpRequest",
  },
  body,
  credentials: "same-origin",
});
```

The responses from the configuration endpoints (initialize, single and bulk option updates, and the two per-bundle `add_to_cart` routes) are JavaScript, intended to update the storefront in place (re-render the bundle form, update the price, or redirect) — not JSON. The bulk cart endpoint in Flow C (`POST /cart/cart_items`) is the exception: it returns JSON.

## Important notes about CPQ bundles

- Configuration lives in the session, keyed by session and lead product. The session cookie ties the requests in a flow together.
- All routes require CPQ to be enabled on the store.
- `bulk_update` applies many option changes in one request and is fail-fast: any invalid entry resets the whole configuration.
- The bulk batch size is capped by the `cpq.bulk_options.max_batch_size` store variable (default 50).
- `add_to_cart` validates the configuration before adding; an invalid or incomplete bundle is rejected.
- `add_preset_to_cart` is a one-shot alternative when the configuration is already known.
- `POST /cart/cart_items` (Flow C) adds several configured bundles and standalone items in one request and returns JSON; it clears the referenced bundle configurations from the session afterward, whether it succeeds or fails. Its batch size is capped by the `bulk_add_to_cart.max_batch_size` store variable (default 100).

---

## Follow StoreConnect

- [Email Newsletter](https://getstoreconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://getstoreconnect.com/partners)
- [News](https://getstoreconnect.com/articles/news)
- [Events](https://getstoreconnect.com/articles/events)
- [Feature Comparison](https://getstoreconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://getstoreconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

---

StoreConnect Support — https://support.storeconnect.com