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

# Send cart events from your storefront

> Tell the Privy code about the visitor's cart from a headless or custom storefront.

On Shopify, BigCommerce, Wix, Weebly and WordPress-rendered WooCommerce stores, the Privy code reads the visitor's cart by itself. On a headless or custom storefront it can't, so your storefront sends it with `window.privyEvents`.

The cart value powers **Cart Value** display targeting and the cart value in your displays, such as a free-shipping bar. On a headless WooCommerce store, the cart's items also power abandoned cart and abandoned checkout flows.

<Note>
  These events run in the visitor's browser. They update the Privy code on the page, and on a WooCommerce store they also save the cart for abandoned cart flows. To send orders to Privy, use the [Orders API](/docs/api-reference/order-placed) from your server.
</Note>

## Quick start

The whole integration is three calls, shown in the **Quick start** example:

1. Set up the queue once per page.
2. `identify` when the shopper signs in or gives you their email.
3. `cart_updated` whenever the cart changes, including when it's emptied after a purchase.

<Tip>
  Using an AI coding assistant? Give it the Markdown version of this page: [help.privy.com/docs/api-reference/browser-events.md](https://help.privy.com/docs/api-reference/browser-events.md).
</Tip>

The sections below cover each call in detail.

## Set up the queue

Add `window.privyEvents = window.privyEvents || [];` before your first `push`, anywhere on the page.

You can push events before the Privy code has loaded. They wait in the queue and run in order once it loads.

## Events

### Cart events

Send one whenever the cart changes: an item added, removed or updated, or the cart emptied.

```js theme={null}
window.privyEvents.push({
  event: 'cart_updated',
  total: 4599,           // $45.99, in minor units
  currency: 'USD',
  currency_minor_unit: 2 // optional, defaults to 2
});

// Cart emptied, or the shopper just bought
window.privyEvents.push({ event: 'cart_updated', total: 0 });
```

| Field | Required | Description |
| - | - | - |
| `event` | Yes | `cart_updated`, `added_to_cart` or `checkout_started`. All three update the cart the same way. On WooCommerce, `checkout_started` also starts a checkout. |
| `total` | Yes | The cart's merchandise total in minor units (cents for USD): the items minus discounts, **without** shipping or tax. A number or a numeric string. |
| `currency` | No | A three-letter ISO 4217 code, like `USD`. |
| `currency_minor_unit` | No | How many decimal places the currency has. Defaults to `2`. Use `0` for currencies like JPY. |

Send `total: 0` when the cart is emptied, including after a purchase.

<Tip>
  If your storefront uses the WooCommerce Store API, its cart response has these values in `totals`. Send `total_items` minus `total_discount` as `total`, and pass `currency_code` and `currency_minor_unit` as they are. Send `total_discount` as `discount`. Don't use `total_price`, which includes shipping and tax.
</Tip>

### Cart items (WooCommerce)

On a headless WooCommerce store, add the cart's items and id to your cart events so Privy's abandoned cart flows can email the cart.

<Note>
  Cart items are currently supported for WooCommerce stores only. Abandoned carts from Custom Integration storefronts aren't currently supported, but those storefronts can still send cart totals for **Cart Value** targeting.
</Note>

```js theme={null}
window.privyEvents.push({
  event: 'cart_updated',
  cart_id: 't_0d5c3f9a8b7e6d5c4b3a2f1e0d9c8b', // your cart's id, the same on every push
  total: 4100,            // items minus discounts, as above
  discount: 500,          // optional: the discount already taken off total
  currency: 'USD',
  items: [
    { product_id: 101, quantity: 2 },
    { product_id: 210, variation_id: 211, quantity: 1 }
  ]
});
```

| Field | Required | Description |
| - | - | - |
| `cart_id` | For items | A stable id for the cart, up to 255 characters, as a string or a whole number. With the WooCommerce Store API, use the `user_id` from the `Cart-Token` header's payload. A guest's cart is saved right away. A cart under any other id is saved once the shopper is a Privy contact. |
| `items` | No | The cart's items, up to 50. Send `[]` when the cart is emptied. |
| `items[].product_id` | Yes | The WooCommerce product id. For a variation, the parent product's id. |
| `items[].variation_id` | No | The variation's id, for a variable product. |
| `items[].quantity` | Yes | A whole number, 1 or more. |
| `discount` | No | The discount in minor units. Privy adds it back to `total` to match the cart to its order. |

Items with missing or invalid ids or quantities are skipped. Privy reads product names, images and links from your store's catalog, so don't send them.

The cart link in abandoned cart emails goes to your WordPress site's address, so redirect it to your storefront.

<Note>
  Privy saves the cart as soon as `identify` or a signup on a Privy display tells it who the shopper is, and links it to their contact on their next page. Carts are saved only for stores connected to Privy through WooCommerce.
</Note>

### Identify

Tell Privy who the visitor is, for example when they sign in.

```js theme={null}
window.privyEvents.push({
  event: 'identify',
  email: 'shopper@example.com'
});
```

| Field | Required | Description |
| - | - | - |
| `event` | Yes | `identify` |
| `email` | Yes | The visitor's email address. |

## Things to know

* **Push when the cart changes, not on every render.** In a framework like React, push from your cart's change handler, not from a component that re-renders. Repeated pushes with the same total are throttled, but each one still does work in the browser.
* **Cart events are ignored on platforms Privy already reads.** On the platforms listed at the top of this page, the Privy code reads the cart itself and ignores cart events from `privyEvents`. `identify` works everywhere.
* **`checkout_started` starts a checkout on WooCommerce.** On a headless WooCommerce store, send it when the shopper reaches checkout, with the cart's id and items (see [Cart items](#cart-items-woocommerce)), so abandoned checkout flows can email them. Elsewhere it only updates the cart.
* **Calling the WooCommerce Store API from another domain:** if your storefront and WordPress are on different domains, WordPress only answers your storefront's requests once its domain is allowed. Add it with WordPress's `allowed_http_origins` filter.

<RequestExample>
  ```js Quick start theme={null}
  // 1. Once per page, before any push.
  //    Safe to run before the Privy code loads.
  window.privyEvents = window.privyEvents || [];

  // 2. When the shopper signs in or gives you their email.
  window.privyEvents.push({
    event: 'identify',
    email: 'shopper@example.com'
  });

  // 3. Whenever the cart changes, including when
  //    it's emptied after a purchase.
  window.privyEvents.push({
    event: 'cart_updated',
    total: 4599, // $45.99: items minus discounts, in cents
    currency: 'USD'
  });
  ```
</RequestExample>
