Cart integration

The agent can read the shopper's cart and change it: add an item in a specific size, change a quantity, switch a line to a different size, or remove a line. It never writes to your store itself. The shopper's cart session belongs to your storefront, so the agent tells your client what to change, and your client makes the change and shows the result.

How it works

  1. Before each chat request, your client reads the current cart from the storefront.
  2. It sends that snapshot as cart in the request body.
  3. Receiving a snapshot adds the cart tools to the agent for that turn.
  4. When the agent reads or changes the cart, the stream sends a cart_action event.
  5. For each change, your client updates the storefront cart and shows whether it worked.
  6. Optionally, your client reports the outcome using the intent token from the event.
  7. On the next turn, your client sends a fresh snapshot that reflects the changes.

The cart snapshot

json
{
  "itemCount": 2,
  "total": 168,
  "currency": "USD",
  "items": [
    {
      "key": "43210987654321:8f2c1a9e",
      "title": "Coastal Linen Shirt",
      "variant": "M / Navy",
      "quantity": 1,
      "price": 89,
      "sku": "CLS-NVY-M",
      "image": "https://cdn.example.com/products/coastal-linen-shirt-navy.jpg"
    },
    {
      "key": "43210987654322:1b7e33c0",
      "title": "Everyday Chino",
      "variant": "32 / Stone",
      "quantity": 1,
      "price": 79,
      "sku": "EDC-STN-32",
      "image": "https://cdn.example.com/products/everyday-chino-stone.jpg"
    }
  ]
}
Field Type Description
itemCount integer Total units across all lines.
total number Cart total in major currency units (89.00, not cents).
currency string ISO 4217 code for total and each line's price.
items array The cart lines. Can be empty.
items[].key string Your storefront's stable id for the line. The agent sends it back as lineKey, so use whatever your cart API needs to find the line.
items[].title string The product name as the shopper sees it.
items[].variant string or null The variant label, or null for single-variant products.
items[].quantity integer Units of this line.
items[].price number The line total in major currency units.
items[].sku string or null The SKU of the variant in the cart. Changing a line's size needs this to find the product in your catalogue. Without it, a size change can only match on the exact product name.
items[].image string or null An absolute image URL, used for receipts. It is never sent to the model.

The server does not validate the snapshot. Send the shape shown above: if a field is missing or wrong, the cart tools report errors to the agent instead of doing anything useful.

Enabling the cart tools

Add cart to the chat request:

bash
curl https://stylor.ai/api/v2/agent/chat \
  -H "Authorization: Bearer $STYLOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{ "role": "user", "content": "Add the navy linen shirt in a medium." }],
    "sessionId": "3b0d4a1e-6f52-4b8a-9d3c-7e2f1a0b9c8d",
    "cart": { "itemCount": 0, "total": 0, "currency": "USD", "items": [] }
  }'
  • Sending a snapshot, even an empty one, enables get_cart, add_to_cart, set_cart_quantity, change_cart_size and remove_from_cart for that turn.
  • Omit cart (or send null) on storefronts where your client cannot change the cart. The agent then has no cart tools at all, so it never offers something it cannot do.
  • Send a fresh snapshot with every request. The agent answers "what's in my cart?" from the snapshot, and uses the line key values in it to target changes.
  • Keep streaming on. With stream: false, cart actions are not returned, so the agent would ask for changes your client never receives.
  • Send sessionId, the session.sessionId from the widget preflight. Without it, add and swap actions carry intent: null and their outcomes cannot be recorded.

The agent changes the cart only when the shopper clearly asks it to. It asks which size before adding anything that comes in more than one.

Cart events

Every cart tool call that has something to show sends one cart_action event. The action field tells you which kind:

action Sent by Meaning Your client must
read get_cart The agent read the snapshot Show it as a receipt. Nothing to change.
add add_to_cart Add a variant Add quantity of sku from the product at url.
quantity set_cart_quantity Set a line's quantity Set line lineKey to quantity (0 removes it).
swap change_cart_size Change a line's size Add sku at the line's quantity, then remove line lineKey.
remove remove_from_cart Remove a line Remove line lineKey.

By the time an event is sent, the server has already checked everything it can. For add and swap that means the item exists in your catalogue, the size is one of its variants, and the variant is in stock. For line actions it means lineKey is in your snapshot. Requests that fail those checks never produce an event: the agent is told why, and usually tells the shopper or asks a follow-up question. No event is sent either when the change would do nothing, such as setting a quantity to its current value or switching to the size the line already has.

The agent is told not to claim a change worked. Your interface is where the shopper sees the confirmation.

read

json
{
  "action": "read",
  "itemCount": 2,
  "total": 168,
  "currency": "USD",
  "items": [
    { "name": "Coastal Linen Shirt", "size": "M / Navy", "quantity": 1, "price": 89, "image": "https://cdn.example.com/products/coastal-linen-shirt-navy.jpg" },
    { "name": "Everyday Chino", "size": "32 / Stone", "quantity": 1, "price": 79, "image": "https://cdn.example.com/products/everyday-chino-stone.jpg" }
  ]
}

This is your own snapshot with the fields renamed for display (title becomes name, variant becomes size). It is already complete, so show it as a finished receipt.

add

json
{
  "action": "add",
  "itemId": "8f2c1a9e-4b7d-4c1e-9a3f-2d6b7e5c1a90",
  "name": "Coastal Linen Shirt",
  "url": "https://shop.example.com/products/coastal-linen-shirt",
  "sku": "CLS-NVY-M",
  "size": "M / Navy",
  "quantity": 1,
  "price": 89,
  "currency": "USD",
  "image": "org_5f3a/ds_9c21/8f2c1a9e-4b7d/0.jpg",
  "intent": "eyJ2IjoxLCJqdGkiOiI3ZjFlIn0.q8vT2rN9..."
}

Find the product on your storefront using url, then add quantity (between 1 and 10) of the variant with sku. price is the variant price, falling back to the product price. image is a storage key, not a URL: build the URL as https://stylor.ai/api/v1/image/<key>. url can be an empty string when the catalogue has no URL for the item.

quantity

json
{
  "action": "quantity",
  "lineKey": "43210987654322:1b7e33c0",
  "name": "Everyday Chino",
  "size": "32 / Stone",
  "from": 1,
  "quantity": 2,
  "price": null,
  "image": "https://cdn.example.com/products/everyday-chino-stone.jpg"
}

Set the line to quantity (between 0 and 50); from is the quantity before the change. price is always null, because the line total changes with the quantity.

swap

json
{
  "action": "swap",
  "lineKey": "43210987654321:8f2c1a9e",
  "name": "Coastal Linen Shirt",
  "fromSize": "M / Navy",
  "size": "L / Navy",
  "sku": "CLS-NVY-L",
  "url": "https://shop.example.com/products/coastal-linen-shirt",
  "quantity": 1,
  "image": "org_5f3a/ds_9c21/8f2c1a9e-4b7d/0.jpg",
  "intent": "eyJ2IjoxLCJqdGkiOiI0YzJhIn0.Zt7mK1pQ..."
}

Add quantity of the new sku first. Remove line lineKey only after the add succeeds. This way a failed add leaves the shopper with the item they already had, and the agent has already told them this is how size changes work. image is the product's storage key, or the line's own image URL when the product has no stored image.

remove

json
{
  "action": "remove",
  "lineKey": "43210987654322:1b7e33c0",
  "name": "Everyday Chino",
  "size": "32 / Stone",
  "quantity": 1,
  "price": 79,
  "image": "https://cdn.example.com/products/everyday-chino-stone.jpg"
}

Remove the line. price is the line total being removed.

Handling cart events in the client

Show the receipt as soon as the event arrives. Do the change in the background, and mark the receipt as done or failed when the storefront responds. When several changes arrive in one turn, apply them in the order they arrived.

javascript
async function handleCartAction(action) {
  const receipt = showReceipt(action, action.action === 'read' ? 'done' : 'pending');
  if (action.action === 'read') return;

  try {
    switch (action.action) {
      case 'add':
        await storefront.addToCart({ url: action.url, sku: action.sku, quantity: action.quantity });
        break;
      case 'quantity':
        await storefront.setQuantity({ lineKey: action.lineKey, quantity: action.quantity });
        break;
      case 'swap':
        await storefront.addToCart({ url: action.url, sku: action.sku, quantity: action.quantity });
        await storefront.removeLine({ lineKey: action.lineKey });
        break;
      case 'remove':
        await storefront.removeLine({ lineKey: action.lineKey });
        break;
    }
    receipt.markDone();
    await reportOutcome(action, null);
  } catch (err) {
    receipt.markFailed(err.message);
    await reportOutcome(action, err.message);
  }
}

Reporting outcomes with intent tokens

add and swap events include intent, a signed, single-use token covering what the server verified: the item, the size and the price. Report whether the change succeeded by sending it to the v1 Track events endpoint, under the event name that matches the action:

action Event name
add cart.item_added
swap cart.size_changed

The product, size and price are read from the token. Your client only reports the outcome:

bash
curl https://stylor.ai/api/v1/events \
  -H "X-Stylor-Session: $STYLOR_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "eventId": "2e3f4a5b-6c7d-4e8f-8a9b-0c1d2e3f4a5b",
      "name": "cart.item_added",
      "occurredAt": "2026-09-12T14:05:02.118Z",
      "seq": 9,
      "intent": "eyJ2IjoxLCJqdGkiOiI3ZjFlIn0.q8vT2rN9...",
      "data": { "outcome": "ok", "error": null }
    }]
  }'
  • The events endpoint is authenticated with the session token from the widget preflight, not with your API key.
  • A token is tied to the sessionId you sent with the chat request, is valid for five minutes, and can be used only once.
  • When intent is null (no sessionId was sent), skip the report.
  • quantity, remove and read actions have no intent and nothing to report.

See the v1 Track events reference for the full event schema and rejection reasons.

After the turn

The snapshot you sent is from before the agent's changes. Read the cart again after your client has applied them, and send the new snapshot with the shopper's next message. The agent then works from the real cart, including any change that failed.