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

# Cart object

> The shape of the Aftersell Cart SDK cart and its lines: every field on the cart, cart lines, bundles, and selling plans.

One object shape flows through the whole SDK. It's what [`getCart()`](/aftersell/cart/sdk-actions#getcart) returns, what [`cart_loaded` and `cart_updated`](/aftersell/cart/sdk-events) hand your handler, and what a [Custom code block](/aftersell/cart/custom-code-blocks) receives.

<Note>
  **All money is in the currency's minor unit** (cents for USD), never a formatted string. `5779` is \$57.79. Use [`formatMoney`](/aftersell/cart/sdk-actions#formatmoneycents) to display it.
</Note>

## The cart

| Field                  | Type                     | Description                                                                                                                                    |
| ---------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`                | `string`                 | The Shopify cart token.                                                                                                                        |
| `items`                | `AftersellCartLine[]`    | The line items. See [cart lines](#cart-lines).                                                                                                 |
| `itemCount`            | `number`                 | Total item quantity, as the shopper sees it.                                                                                                   |
| `hasSubscriptionItems` | `boolean`                | `true` when at least one line in `items` carries a selling plan, including add-on lines that `itemCount` leaves out. `false` on an empty cart. |
| `totalPrice`           | `number`                 | Current total, in cents.                                                                                                                       |
| `originalTotalPrice`   | `number`                 | Total before discounts, in cents.                                                                                                              |
| `totalDiscount`        | `number`                 | Discount total, in cents.                                                                                                                      |
| `compareAtTotalPrice`  | `number \| null`         | Sum of each line's compare-at (MSRP) × quantity, in cents. `null` when unavailable, so fall back to `originalTotalPrice`.                      |
| `currency`             | `string`                 | Currency code.                                                                                                                                 |
| `discountCodes`        | `string[]`               | Discount codes accepted on the cart, sorted. `[]` when none.                                                                                   |
| `attributes`           | `Record<string, string>` | Cart attributes. Read-only from the SDK.                                                                                                       |

<Warning>
  **`itemCount` is not always the sum of `items`.** `items` mirrors the real Shopify cart, including add-on lines the drawer hides, such as shipping protection. `itemCount` is the shopper-facing number that matches the cart badge. For "how many things has the shopper chosen", use `itemCount`; to iterate over the lines the cart is rendering, use `items`.

  Two things are missing from `items` entirely: lines hidden with [`setHidden`](/aftersell/cart/sdk-hooks#registerlinetransform), and [bundle children](#bundles), which move onto their anchor. Both still count toward the cart totals, which come straight from Shopify.
</Warning>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  console.log(state.itemCount, 'items');
  console.log('Total:', window.aftersell.cart.actions.formatMoney(state.totalPrice));
  console.log('Saved:', window.aftersell.cart.actions.formatMoney(state.totalDiscount));
  console.log('Codes:', state.discountCodes.join(', ') || 'none');
});
```

## Cart lines

Each entry in `items`, and the `item` on [`item_added`](/aftersell/cart/sdk-events#item_added) and [`item_removed`](/aftersell/cart/sdk-events#item_removed):

| Field                 | Type                             | Description                                                                                                                                      |
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `key`                 | `string`                         | The line's Shopify key. Pass this to the item [actions](/aftersell/cart/sdk-actions).                                                            |
| `productId`           | `number`                         | Shopify product ID.                                                                                                                              |
| `variantId`           | `number`                         | Shopify variant ID.                                                                                                                              |
| `handle`              | `string`                         | Product handle.                                                                                                                                  |
| `title`               | `string`                         | Display title.                                                                                                                                   |
| `productTitle`        | `string`                         | Product title without the variant.                                                                                                               |
| `variantTitle`        | `string \| null`                 | Variant label, or `null`.                                                                                                                        |
| `variantOptions`      | `Array<{ name, value }>`         | Selected options, e.g. `[{ name: 'Size', value: 'Medium' }]`. Shopify emits `Title: Default Title` for a single-variant product.                 |
| `quantity`            | `number`                         | Quantity of this line.                                                                                                                           |
| `linePrice`           | `number`                         | Line price, in cents.                                                                                                                            |
| `finalLinePrice`      | `number`                         | Line price after discounts, in cents.                                                                                                            |
| `originalLinePrice`   | `number`                         | Line price before discounts, in cents.                                                                                                           |
| `compareAtPrice`      | `number \| null`                 | Variant compare-at (MSRP) **per unit**, in cents. `null` when none.                                                                              |
| `properties`          | `Record<string, string> \| null` | Line item properties.                                                                                                                            |
| `internalProperties`  | `Record<string, string>`         | Render-only overlay from [`registerLineTransform`](/aftersell/cart/sdk-hooks#registerlinetransform). Never persisted to Shopify. `{}` when none. |
| `discountAllocations` | `Array<{ title, amount }>`       | Discounts applied to this line. `amount` is in cents. `[]` when none.                                                                            |
| `isGiftCard`          | `boolean`                        | Whether the line is a gift card.                                                                                                                 |
| `sellingPlan`         | `{ id, name } \| null`           | The active subscription plan, or `null` for a one-time purchase.                                                                                 |
| `bundle`              | `AftersellCartBundle \| null`    | [Bundle](#bundles) view model on the anchor line; `null` on non-bundle lines and children.                                                       |
| `metadata`            | `Record<string, unknown>`        | [Enrichment](/aftersell/cart/sdk-hooks#registercartenricher) data keyed by enricher `id`. `{}` until an enricher populates it.                   |

<Warning>
  `properties` can carry shopper-supplied input, such as a product form's custom-text field. Render it as text, never as raw HTML.
</Warning>

### Identifying a line

Use `key` for anything that acts on a line, and `variantId` or `productId` for anything that identifies a *product*:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ✅ Acting on a line: use key.
window.aftersell.cart.actions.removeItem(line.key);

// ✅ Recognising a product: use variantId.
const hasGift = state.items.some((line) => line.variantId === GIFT_VARIANT_ID);
```

The same variant can appear on several lines when the properties differ. Two engraved mugs with different engraving text are two lines sharing one `variantId`. That's why the actions take `key`.

### Prices on a line

Three prices, easy to mix up:

| Want                                | Use                           |
| ----------------------------------- | ----------------------------- |
| What the shopper pays for this line | `finalLinePrice`              |
| What it cost before cart discounts  | `originalLinePrice`           |
| The MSRP strike-through, per unit   | `compareAtPrice` × `quantity` |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Is this line discounted?
const isDiscounted = line.finalLinePrice < line.originalLinePrice;

// Is it free? (A common way to detect a gift line.)
const isFree = line.finalLinePrice === 0;
```

## Bundles

When lines are grouped into a bundle, the **anchor** line carries a `bundle` object. The children are folded into it and no longer appear in `items` on their own. See [Group bundle lines from another app](/aftersell/cart/sdk-use-case-bundles) for how grouping is set up.

| Field          | Type                     | Description                                             |
| -------------- | ------------------------ | ------------------------------------------------------- |
| `id`           | `string`                 | Bundle identifier.                                      |
| `source`       | `'native' \| 'grouped'`  | A Shopify native bundle, or lines grouped by Aftersell. |
| `memberKeys`   | `string[]`               | The `key` of every line in the bundle.                  |
| `children`     | `AftersellBundleChild[]` | The bundle's contents.                                  |
| `displayPrice` | `number`                 | The price shown for the bundle, in cents.               |

Each child carries `key` (`null` for a native component), `title`, `variantTitle`, `quantity`, `perAnchorQty`, `imageUrl`, `finalLinePrice`, `originalLinePrice`, and `compareAtPrice`.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Skip bundle children when totalling your own line list.
const topLevel = state.items.filter((line) => !isBundleChild(line, state));
```

## Subscription plans

A line's active plan is `sellingPlan`, or `null` for a one-time purchase. For a whole-cart answer, read `hasSubscriptionItems` rather than scanning the lines yourself, since it also counts add-on lines that `items` presents but `itemCount` skips:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
if (state.hasSubscriptionItems) {
  // The cart contains at least one subscription line.
}

const subscriptions = state.items.filter((line) => line.sellingPlan);
console.log(subscriptions.length, 'subscription lines');
```

The *available* plans on a line, the ones in the picker, aren't on the cart object. Shape those with [`registerSubscriptionOptionsTransform`](/aftersell/cart/sdk-hooks#registersubscriptionoptionstransform) and [`registerDefaultSubscriptionOptionSelector`](/aftersell/cart/sdk-hooks#registerdefaultsubscriptionoptionselector).

## Where to go next

* **[Actions](/aftersell/cart/sdk-actions)**: read and change the cart.
* **[Events](/aftersell/cart/sdk-events)**: where this object comes from.
* **[Hooks](/aftersell/cart/sdk-hooks)**: add your own data to a line with an enricher.
* **[Use cases](/aftersell/cart/sdk-use-cases)**: complete solutions that read these fields.
