RECIPE: Shop and Buy a Product

When to use this recipe

Any visitor request about a Wix Stores catalog or a purchase. Examples:

  • "What do you sell?" / "Do you have journals?" / "Show me only the Wear items"
  • "What colours does the journal come in? Is every colour in stock?"
  • "Is the Breathing Cards deck available?" / "Is anything on sale?"
  • "Add a Focus Candle in Fig and two mugs to my cart" / "Make it one mug, remove the candle"
  • "I'm ready to pay" / "Do you ship or can I pick up?"

Inputs you need before STEP 4

InputHow to get it
Visitor tokenSTEP 1 (GenerateVisitorToken, or reuse one already in the conversation)
Product id, slug, price range, stock status, optionsSTEP 2 (POST /stores/v3/products/query or /search)
Category id (only for "show me the X category")STEP 3 (POST /categories/v1/categories/query)
variantId for the chosen size/colour/scent (mandatory for any product with options; use the single variant otherwise)STEP 4 (POST /stores/v3/products/query-variants)
Cart line ids (for quantity changes / removal)STEP 5 response (cart.lineItems[].id)
checkoutIdSTEP 7 (POST /ecom/v1/carts/current/create-checkout)

Decision tree — pick the shortest path

  • "What do you sell?" / "Do you have X?" → STEP 1 + STEP 2. Match client-side, case-insensitively, over name, plainDescription, option names. Offer to add to cart.
  • "Show me only the " → STEP 1 + STEP 3 (find the category) + STEP 2b (search by category).
  • "What sizes/colours/scents?" / "Is in stock?" → STEP 2 for the product, then STEP 4 (variants carry per-choice stock).
  • "Is X available?" / "Anything on sale?" → STEP 2 only: inventory.availabilityStatus and compareAtPriceRange.
  • "Add X to my cart" → STEP 2 → STEP 4 (resolve variantId) → STEP 5 → STEP 6 (totals).
  • "Change quantity / remove" → STEP 6 with the line ids from the last cart response.
  • "I'm ready to pay" → STEP 7 (create checkout) → STEP 8 (redirect session). Never build /checkout?checkoutId= yourself (see Common errors #7).

All calls below run with the visitor token through CallWixSiteAPI / ExecuteWixAPI. Prefer CallWixSiteAPI for any write whose failure you need to explain — ExecuteWixAPI currently hides the real HTTP error (see Common errors #1).


STEP 1: Get a visitor token

Call GenerateVisitorToken once and reuse the access_token for every step. The cart is owned by the visitor identity: a new token means an empty cart and a 404 OWNED_CART_NOT_FOUND on checkout.


STEP 2: List or find products

POST https://www.wixapis.com/stores/v3/products/query

Copy

Filterable fields on this endpoint: id, slug, visible, handle, options.id, created/updated dates. name is not filterable — fetch up to 50–100 and match client-side. For one product by URL slug: "filter": { "slug": "breathing-cards" }.

What you need from the response (verified 2026-09-16)

Copy
  • Price to show: actualPriceRange.minValue.formattedAmount (already in the site currency). If min ≠ max say "from …".
  • "On sale" = compareAtPriceRange.minValue.amount > actualPriceRange.minValue.amount.
  • inventory.availabilityStatus === "OUT_OF_STOCK" → say "sold out"; still list it.
  • options[] tells you what the visitor must choose before STEP 5.

STEP 2b: Products in a category

POST https://www.wixapis.com/stores/v3/products/search — the only product endpoint that can filter by category (query rejects directCategoriesInfo).

Copy

Body is wrapped in search (a top-level filter is ignored). Operator is $matchItems with the key id; $hasSome is rejected. Response is { "products": [ … same shape as STEP 2 … ] }.


STEP 3: Categories (only when the visitor names one)

POST https://www.wixapis.com/categories/v1/categories/query

Copy
  • treeReference is top-level and mandatory. An empty filter is rejected (INVALID_FILTER), hence the $exists condition.
  • Response: { "categories": [{ "id": "8f01c2bf-…", "name": "Wear", "slug": "wear", "visible": true }] }. Match the visitor's word to name client-side ("clothes" → Wear).

STEP 4: Resolve the variant (size / colour / scent + per-choice stock)

POST https://www.wixapis.com/stores/v3/products/query-variantsnot /stores/v3/variants/query (404).

Copy
Copy

Match the visitor's words to optionChoices[].optionChoiceNames.choiceName (case-insensitive, accept synonyms like "grey" for "Charcoal" only if you say so). A product without options has exactly one variant — use it. If the requested choice has inStock: false, offer the choices that are in stock.


STEP 5: Add to the cart

POST https://www.wixapis.com/ecom/v1/carts/current/add-to-cart

Copy
Copy

Same call works for Restaurants dishes with the Orders app id — a visitor can hold shop and café items in one cart (verified: Flat White + planner, ₪105).


STEP 6: Change quantities, remove lines, read totals

Copy

estimate-totals{ "cart": {…}, "priceSummary": { "subtotal": { "amount": "70.00", "formattedAmount": "₪70.00" }, "total": {…} } }. Quote formattedAmount; never hardcode a currency symbol.


STEP 7: Create the checkout

POST https://www.wixapis.com/ecom/v1/carts/current/create-checkout

Copy

{ "checkoutId": "ca727402-…" }. GET https://www.wixapis.com/ecom/v1/checkouts/<checkoutId> then shows lineItems, priceSummary.total and shippingInfo.carrierServiceOptions[].shippingOptions[] (title, cost.price.formattedAmount, logistics.pickupDetails for pickup) — use it to answer "do you ship or can I pick up?".


STEP 8: Send the visitor to pay

Do not use the checkoutUrl returned by POST /ecom/v2/carts/<checkoutId>/get-checkout-url or GET /ecom/v1/checkouts/<id>/checkout-url on a headless site: it is https://<site>/checkout?checkoutId=…, a Wix-editor page that a headless frontend does not have (404). Create a redirect session instead:

POST https://www.wixapis.com/_api/redirects-api/v1/redirect-session

Copy

redirectSession.fullUrl is the hosted checkout. Say "pay here to complete the order" and stop: payment cannot be completed through the API, and on a site without a payment provider the hosted page will show that payment is unavailable.


Common errors and how to avoid them

1. ExecuteWixAPI says "Visitor token rejected (HTTP 403)" for every failure

The sandbox reports any non-2xx (400 ticketReservation must not be empty, 403 No payment method configured, 404) as a token problem. Do not mint a new token (you would lose the cart). Re-issue the single failing request through CallWixSiteAPI to read the real status and message.

2. POST /stores/v3/variants/query404

The variants endpoint is POST /stores/v3/products/query-variants. Docs search for "product variants" lists it as Query Variants.

3. 400 catalogItemId has size 0

You built the cart body before the product query resolved (or read _id off a REST response). Product ids are products[].id in REST.

4. 400 lineItems[0].id is not a valid GUID

Quantity updates need the cart line id from the last cart response, not the product id. Read the cart first if you no longer have it.

5. 404 OWNED_CART_NOT_FOUND on create-checkout

The cart belongs to a different visitor token (you minted a new one) or nothing was added. Reuse the token from STEP 1 and re-check GET /ecom/v1/carts/current.

6. Category filter returns nothing / 400 not declared as filterable

Use /products/search with "search": { "filter": { "directCategoriesInfo.categories": { "$matchItems": [{ "id": … }] } } }. query cannot filter by category, and $hasSome is not accepted.

7. Checkout URL points to /checkout?checkoutId=

That page exists only on Wix-editor sites. Use the redirect session (STEP 8).

8. Adding a product with options without variantId

Cart V1/V2 rejects the line. Always resolve the variant in STEP 4 first; a "no options" product still has one variant.

9. Treating checkoutId as an order

A checkout is unpaid. Only the hosted page (or POST /ecom/v1/checkouts/{id}/create-order after payment) produces an order. Never say "your order is placed".


Constants

ConstantValue
Wix Stores catalog appId (cart catalogReference.appId)215238eb-22a5-4c36-9e7b-e7c08025e04e
Categories tree reference{ "appNamespace": "@wix/stores", "treeKey": null }
Product read fields worth requesting["CURRENCY", "PLAIN_DESCRIPTION"]

Product, variant, category, line and checkout ids are per site and per visitor — discover them during the conversation.


References

Last updated: 17 September 2026

Did this help?