> ## 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.

# Actions

> Every Aftersell Cart SDK action for reading and changing the cart: open, close, add, remove, update quantity, swap a variant, read state, and format money.

Actions **read and change the cart**. They live under `window.aftersell.cart.actions`.

<Note>
  Actions run **after the cart is ready**, inside `ready()` or an [event](/aftersell/cart/sdk-events) handler.
</Note>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  console.log(state.itemCount, 'items');
});
```

<Note>
  **Before the cart loads, actions are stubs.** Each one logs a console warning naming the action, for example `cart.actions.addItem() called before the cart loaded`, and does nothing. The async actions still resolve a Promise, so a `.then()` chain runs normally rather than throwing; `getCart()` returns `null` and `formatMoney()` returns an empty string.

  Nothing breaks if you call one too early, but nothing happens either. Watch the console for that warning when an action appears to do nothing.
</Note>

## Every action

| Action                                                   | Signature                            | Returns                 | What it does                     |
| -------------------------------------------------------- | ------------------------------------ | ----------------------- | -------------------------------- |
| [`open`](#open-and-close)                                | `open()`                             | None                    | Opens the drawer.                |
| [`close`](#open-and-close)                               | `close()`                            | None                    | Closes the drawer.               |
| [`getCart`](#getcart)                                    | `getCart()`                          | `AftersellCart \| null` | Reads the current cart.          |
| [`formatMoney`](#formatmoneycents)                       | `formatMoney(cents)`                 | `string`                | Formats an amount for display.   |
| [`addItem`](#additemvariantid-quantity)                  | `addItem(variantId, quantity?)`      | `Promise`               | Adds a variant.                  |
| [`removeItem`](#removeitemkey)                           | `removeItem(key)`                    | `Promise`               | Removes a line.                  |
| [`updateItemQuantity`](#updateitemquantitykey-quantity)  | `updateItemQuantity(key, quantity)`  | `Promise`               | Sets a line's quantity.          |
| [`replaceLineVariant`](#replacelinevariantkey-variantid) | `replaceLineVariant(key, variantId)` | `Promise`               | Swaps a line's variant.          |
| [`refresh`](#refresh)                                    | `refresh()`                          | `Promise`               | Refetches the cart from Shopify. |
| [`visualRefresh`](#visualrefresh)                        | `visualRefresh()`                    | None                    | Repaints without refetching.     |

<Warning>
  Calling an action from a `cart_updated` handler can loop. Read [the two rules](/aftersell/cart/sdk-events#the-two-rules) first.
</Warning>

***

## Drawer

### open and close

Open or close the cart drawer. Both are synchronous and take no arguments.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Open the drawer from your own cart link.
document.querySelector('#my-cart-link').addEventListener('click', (event) => {
  event.preventDefault();
  window.aftersell.cart.actions.open();
});
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Close it after the shopper does something in a custom block.
window.aftersell.cart.actions.close();
```

***

## Reading

### getCart()

Returns the current [cart object](/aftersell/cart/sdk-cart-object), or `null` before it has loaded. The result is a **copy**, so mutating it won't change the real cart.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  if (!state) return; // the initial load failed

  console.log(state.itemCount, 'items,', state.items.length, 'lines');
  console.log('Total:', window.aftersell.cart.actions.formatMoney(state.totalPrice));
});
```

Because it's a snapshot, don't hold on to the result; read it again each time you need current data. In an event handler you already have the fresh cart as the payload, so `getCart()` is redundant there.

### formatMoney(cents)

Formats a minor-unit amount using your store's money format. Every price in the SDK is in cents, so this is how you turn one into something you can display.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.actions.formatMoney(5779);  // "$57.79"
window.aftersell.cart.actions.formatMoney(0);     // "$0.00"
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Show the cart total in your own header element.
window.aftersell.cart.events.on('cart_updated', (state) => {
  document.querySelector('#header-total').textContent =
    window.aftersell.cart.actions.formatMoney(state.totalPrice);
});
```

Override the format with [`configure({ money_format })`](/aftersell/cart/sdk-configure#money_format).

***

## Changing the cart

<Note>
  The item actions identify a line by its Shopify **`key`**, not by variant ID, because a cart can hold the same variant on several lines with different properties. Read it from `getCart().items[n].key`.
</Note>

### addItem(variantId, quantity?)

Adds a variant to the cart. `quantity` defaults to `1`. Resolves once the cart has settled.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Add one, then show the shopper.
window.aftersell.cart.actions.addItem(41720671830082).then(() => {
  window.aftersell.cart.actions.open();
});
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Add a specific quantity.
window.aftersell.cart.actions.addItem(41720671830082, 3);
```

Adding a variant already in the cart increases that line's quantity rather than creating a second line, as long as the existing line has no line item properties. A line carrying properties is a distinct line, so you get a new one.

### removeItem(key)

Removes a line entirely.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Remove any free line from the cart.
const gift = window.aftersell.cart.actions
  .getCart()
  .items.find((line) => line.finalLinePrice === 0);
if (gift) window.aftersell.cart.actions.removeItem(gift.key);
```

### updateItemQuantity(key, quantity)

Sets a line's quantity. Passing `0` removes the line.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const line = window.aftersell.cart.actions.getCart().items[0];
if (line) window.aftersell.cart.actions.updateItemQuantity(line.key, 3);
```

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Cap a line at one unit.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items.forEach((line) => {
    if (line.variantId === LIMITED_VARIANT_ID && line.quantity > 1) {
      window.aftersell.cart.actions.updateItemQuantity(line.key, 1);
    }
  });
});
```

That second example is safe to run from `cart_updated` because the `> 1` check is false on the second pass. See [the two rules](/aftersell/cart/sdk-events#the-two-rules).

### replaceLineVariant(key, variantId)

Swaps a line's variant while keeping its quantity and properties. Useful for a size or flavor switcher inside the cart.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const line = window.aftersell.cart.actions.getCart().items[0];
window.aftersell.cart.actions.replaceLineVariant(line.key, 41720671862850);
```

<Warning>
  The line's **selling plan resets** on a swap. A subscription line becomes a one-time purchase unless you reapply a plan.
</Warning>

The swap is an add followed by a remove, not an edit in place, so the result is a **new line**: it gets a new `key` and lands at the end of the cart. Re-read `getCart()` afterwards rather than reusing the key you passed in.

***

## Refreshing

### refresh()

Refetches the cart from Shopify. Use it after something outside the SDK changed the cart and the drawer didn't notice.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// After a raw Ajax API call of your own.
fetch('/cart/add.js', { method: 'POST', /* … */ })
  .then(() => window.aftersell.cart.actions.refresh())
  .then(() => { window.aftersell.cart.actions.open(); });
```

Most of the time you don't need this, since Aftersell already listens for Shopify's standard cart events and refetches on its own. Reach for it when a custom integration bypasses those.

### visualRefresh()

Re-runs the render transforms without refetching the cart from Shopify. You rarely need it: registering (or unregistering) a [line transform](/aftersell/cart/sdk-hooks#registerlinetransform), [comparator](/aftersell/cart/sdk-hooks#registerlinecomparator), [enricher](/aftersell/cart/sdk-hooks#registercartenricher), or either [subscription hook](/aftersell/cart/sdk-hooks#registersubscriptionoptionstransform) triggers one for you. Only the two add-to-cart hooks don't, since they change nothing already on screen.

Reach for it when something a transform *depends on* changes but the cart itself hasn't:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// A currency switcher changed the format: repaint prices already on screen.
window.myTheme.onCurrencyChange((currency) => {
  window.aftersell.cart.configure({ money_format: FORMATS[currency] });
  window.aftersell.cart.actions.visualRefresh();
});
```

***

## Notes and edge cases

* **Async actions resolve when the change settles.** Awaiting one lets you sequence work after the cart has actually updated.
* **`getCart()` returns a copy.** Mutating it does nothing to the real cart.
* **There's no action for discount codes.** Applied codes are readable on the cart (`discountCodes`, `totalDiscount`) and per line (`discountAllocations`); shoppers apply them through the [Discount code](/aftersell/cart/discount-code-block) block.
* **There's no action for cart attributes or notes.** Attributes are readable on the cart object; shoppers write notes through the [Notes](/aftersell/cart/notes-block) block.
* **To hide a line rather than remove it**, use [`registerLineTransform`](/aftersell/cart/sdk-hooks#registerlinetransform). Removing changes the shopper's total; hiding doesn't.

## Where to go next

* **[Cart object](/aftersell/cart/sdk-cart-object)**: what `getCart()` hands back.
* **[Events](/aftersell/cart/sdk-events)**: when to run these actions.
* **[Hooks](/aftersell/cart/sdk-hooks)**: change how a line renders instead of changing the cart.
* **[Use cases](/aftersell/cart/sdk-use-cases)**: complete solutions to common requests.
