# JavaScript API

> The methods of window.Smootify by task, for the cart, products, the customer, the market and consent, with short examples and how each kind of method answers.

Status: Ready (Smootify 2.0)  
1.x page: https://docs.smootify.io/docs/custom-code/js/api  
URL: https://www.smootify.io/docs/developers/javascript-api  
Updated: 2026-10-12

`window.Smootify` gives your scripts the cart, the products, the logged-in customer, the visitor’s market and their consent. This page goes through the main methods by task, with short examples. Every method, with its full signature, is in the Reference under [API](https://www.smootify.io/docs/reference/api).

> [!NOTE]
> Call the methods from a `window.Smootify.push` function, so they run once Smootify is ready: see [Run your scripts with Smootify](https://www.smootify.io/docs/developers/scripts). The examples on this page are the body of such a function, where `Smootify` is its argument.

```js
window.Smootify = window.Smootify || [];
window.Smootify.push(async (Smootify) => {
  const cart = await Smootify.getCart();
  console.log(cart.totalQuantity, Smootify.formatMoney(cart.cost.totalAmount.amount));
});
```

## Methods, events, filters or the AJAX API

Your code works with Smootify in four ways. This page covers the methods; the other three have their own pages:

- **Methods** (`window.Smootify`, [JavaScript API](https://www.smootify.io/docs/developers/javascript-api)): your code asks and Smootify answers. Use them to read the cart, the products, the customer or the market, add to the cart, open the mini cart. For example `getCart()`, `addToCart()`, `getCustomer()`. Call them from a `push` function; most return a Promise.
- **Events** (`Smootify.on`, [Events](https://www.smootify.io/docs/developers/events)): Smootify reports what happened. Use them to keep your own counter in sync, track additions, start analytics after consent. For example `cart_updated`, `added_to_cart`, `consent_changed`. What the handler returns is ignored; `once` stops after the first event.
- **Filters** (`Smootify.addFilter`, [Filters](https://www.smootify.io/docs/developers/filters)): Smootify hands a value to your function before it uses it. Use them to check or enrich the lines added to the cart, change the checkout address or a draft order, choose the metafields the page asks for. For example `add_to_cart_lines`, `checkout_url`. Answer the value changed, nothing to keep it, or `false` to stop.
- **AJAX API** (`/cart.js`, [Shopify AJAX API](https://www.smootify.io/docs/developers/ajax-api)): for apps written for Shopify themes. An app's snippet runs as it is: Smootify answers the theme endpoints on the page, with a theme's JSON. For example `/cart.js`, `/cart/add.js`, `/products/{handle}.js`. Same cart as the page; start the snippet with `type="smootify-load"`.

## How the methods answer

- **Most methods return a Promise.** `isLoggedIn()`, `formatMoney()`, `selectVariant()` and the listeners answer at once.
- **Some resolve with Shopify’s errors instead of the cart.** `addToCart`, `applyCouponCode` and `applyGiftCard` resolve with the cart, or with a list of errors (`{ message, field, code }`). Tell them apart with `Array.isArray`.
- **The other cart methods reject with Shopify’s errors.** Use `try` / `catch` around `updateCartLines`, `removeCartLines`, `removeCouponCode`, `removeGiftCard`, `updateCartNote`, `updateCartAttributes` and `checkout`.
- **Ids are Shopify’s global ids.** A variant is `gid://shopify/ProductVariant/<id>`, a cart line `gid://shopify/CartLine/<id>`, a product `gid://shopify/Product/<id>`.
- **The data has the Storefront API shape**: the cart, the products and the customer carry the fields Smootify asks Shopify for, and `null` where Shopify has no value. The shapes are in `smootify.d.ts`.

Cart errors also go out with the [`smootify:errors`](https://www.smootify.io/docs/reference/api/events/errors) event.

## Cart

| Method | What it does |
|---|---|
| `getCart()`, `getCartID()` | the cart, or its id |
| `addToCart(lines, goToCheckout)` | adds lines; `true` goes on to checkout |
| `updateCartLines(lines)`, `removeCartLines(ids)`, `clearCart()` | change, remove or empty the lines |
| `applyCouponCode(code)`, `removeCouponCode(code)` | discount codes |
| `applyGiftCard(code)`, `removeGiftCard(id)` | gift cards; the id is from `cart.appliedGiftCards` |
| `updateCartNote(note)`, `updateCartAttributes(attributes)` | the cart note and the cart attributes |
| `checkout()` | goes to checkout, as the cart’s checkout button |
| `openCart()`, `closeCart()` | open or close the page’s mini cart |
| `reloadCart()` | asks Shopify for the cart again |

Add a variant from your own button, then open the mini cart:

```js
document.querySelector("[data-add-sample]")?.addEventListener("click", async () => {
  const answer = await Smootify.addToCart([
    { merchandiseId: "gid://shopify/ProductVariant/44012345678901", quantity: 1 },
  ]);
  if (Array.isArray(answer)) console.warn(answer[0]?.message);
  else await Smootify.openCart();
});
```

Every addition goes through the [`add_to_cart_lines`](https://www.smootify.io/docs/developers/filters) filters, then fires `added_to_cart` and `cart_updated`. A line can carry `attributes` (`[{ key, value }]`, shown at checkout, hidden when the key starts with `_`) and a `sellingPlanId`.

Change the quantity of a line:

```js
const cart = await Smootify.getCart();
const line = cart.lines.nodes[0];
if (line) {
  try {
    await Smootify.updateCartLines([{ id: line.id, quantity: line.quantity + 1 }]);
  } catch (errors) {
    console.warn(errors);
  }
}
```

A code that Shopify accepts but does not apply to this cart comes back in `cart.discountCodes` with `applicable: false`, not as an error.

`checkout()` goes through the [`checkout_url`](https://www.smootify.io/docs/developers/filters) filters, like the cart’s button. `openCart()` and `closeCart()` work on a mini cart (a `smootify-cart` that opens), not on a cart page.

Two cart methods serve add-ons: `addBoxToCart(lines, { title, image }, goToCheckout)` adds lines as one [Magic Box](https://www.smootify.io/docs/features/magic-box), and `getCartAsDraftOrderData(email, note, customAttributes)` gives the cart as a draft order (see [Quotes and B2B](https://www.smootify.io/docs/features/quotes)).

## Products

- **`getProductByHandle(handle)`**: a product by its handle, or `null` when the visitor’s market does not sell it.
- **`getProductsById(ids)`**: several products by their global ids, as an object keyed by id. Products the market does not sell are missing from it.
- **`selectVariant(element, variantId)`**: selects a variant of a `smootify-product` on the page. The element is the product or any element inside it; the id is the variant number or its global id. It answers `false` when the product has no such variant.

```js
const product = await Smootify.getProductByHandle("oak-chair");
if (product) console.log(product.title, product.variants.nodes.length);

const card = document.querySelector("smootify-product");
if (card) Smootify.selectVariant(card, 44012345678901);
```

The product page’s product carries every variant. Elsewhere a product carries its first 250.

## Customer

The customer methods need Shopify’s customer accounts and the `newCustomerAccountsPublicKey` option: see [Customer accounts](https://www.smootify.io/docs/build/account).

- **`getCustomer()`**: the logged-in customer, or `null` for a guest, once the session is known.
- **`isLoggedIn()`**: answers at once, and is `false` until the session is known. Prefer `getCustomer()` at load.
- **`passwordlessLogin()`** starts Shopify’s login, **`logout()`** ends the session on the site and on Shopify.
- **`queryCustomer(fields)`**: a Customer Account API query on the customer. `fields` is what goes inside `customer { … }`, and the answer is `{ customer }`.
- **`setCustomerMetafields(...metafields)`**: writes metafields of the logged-in customer (`namespace` is `custom` when left out). In Shopify, the metafield definition needs **Customer accounts access** set to **Read and write**.
- **`getNewCustomerAccessToken()`**: the Customer Account API token of the session, `""` for a guest.

```js
const customer = await Smootify.getCustomer();
if (customer) {
  const { customer: data } = await Smootify.queryCustomer(
    "orders(first: 3, reverse: true) { nodes { name totalPrice { amount currencyCode } } }",
  );
  console.log(customer.displayName, data);
}
```

## Market and money

- **`getLocalization()`**: the visitor’s country (with its currency), language and market, and the countries and languages the store sells in.
- **`changeCountryByIsoCode(isoCode)`** and **`changeMarketLanguage(languageCode)`**: change the market. They answer `true` when it changed, and the page follows the new market.
- **`formatMoney(amount)`**: an amount in the store’s money format. It takes a number in cents (`1250`) or an amount as Shopify sends it (`"12.50"`).

```js
const { country, language } = await Smootify.getLocalization();
console.log(country.isoCode, country.currency.isoCode, language.isoCode);

const changed = await Smootify.changeCountryByIsoCode("DE");
if (changed) console.log("Prices are now for Germany");
```

The codes are Shopify’s: `DE` for a country, `IT` for a language.

## Consent

- **`getConsent()`**: the visitor’s consent, from Shopify’s Customer Privacy API. It loads Shopify’s consent script if the page has not loaded it yet.
- **`setConsent(choices)`**: saves the visitor’s choices for `marketing`, `analytics`, `preferences` and `saleOfData`, and answers the new consent.

```js
const consent = await Smootify.getConsent();
if (consent.allowed.analytics) {
  // start your own analytics
}
await Smootify.setConsent({ analytics: true, marketing: false });
```

To react when the visitor answers the banner, listen to [`consent_changed`](https://www.smootify.io/docs/reference/api/events/consent-changed). The banner itself is in [Cookie consent](https://www.smootify.io/docs/features/cookie-consent).

## Toasts

`toast(name, items)` shows the page’s `[toast="name"]` template, the way Smootify shows its own [toasts](https://www.smootify.io/docs/build/toasts), with `items` written into its `[toast-item]` parts. It answers `false` when the page has no such template.

```js
Smootify.toast("added-to-cart", { title: "Gift wrap", price: "€4.00" });
Smootify.toast("booking-saved", { message: "See you on Tuesday at 10:00" });
```

A name of your own works when the designer adds a template with that name, such as `[toast="booking-saved"]`: only your code shows it. A name ending in `-error` falls back to the `[toast="error"]` template when its own is missing.

## Storefront API

`query(query, variables)` sends a Storefront API query with the store’s access and answers its `data`. It rejects with the GraphQL errors. Use it for data no attribute shows.

The query runs as you write it. To get the visitor’s prices and translations, add Shopify’s `@inContext` directive with the country and language from `getLocalization()`:

```js
const { country, language } = await Smootify.getLocalization();
const data = await Smootify.query(
  `query ($handle: String!, $country: CountryCode, $language: LanguageCode)
  @inContext(country: $country, language: $language) {
    product(handle: $handle) { title tags }
  }`,
  { handle: "oak-chair", country: country.isoCode, language: language.isoCode },
);
console.log(data);
```

## Where people get stuck

> [!STUCK]
> - **`addToCart` seems to succeed but nothing is added.** It resolved with Shopify's errors, such as a sold-out variant. Check the answer with `Array.isArray`.
> - **An error thrown by `updateCartLines` is not caught.** These methods reject: `await` them inside `try` / `catch`.
> - **`isLoggedIn()` is `false` for a logged-in customer.** The session is not known yet. Use `await Smootify.getCustomer()`.
> - **Products come back empty.** `getProductsById` takes global ids (`gid://shopify/Product/…`), and a product that is not published to the store's sales channel, or not sold in the visitor's market, is not returned.
> - **`query` shows prices in the wrong currency.** The query has no `@inContext` directive.
> - **`setCustomerMetafields` answers errors.** The metafield definition in Shopify does not give customer accounts write access.

## Coming from 1.x

The 21 methods of 1.x keep their names and arguments. 2.0 adds `push`, `on`, `once`, `addFilter`, `isLoggedIn`, `getCustomer`, `logout`, `getLocalization`, `getConsent`, `setConsent`, `getProductByHandle`, `removeCouponCode`, `removeGiftCard`, `updateCartNote`, `updateCartAttributes`, `checkout`, `selectVariant`, `openCart` and `closeCart`.

- `window.Smootify.store` and the other internals of 1.x are gone: only the methods of `window.Smootify` are supported.
- `formatMoney` accepts its two extra 1.x arguments and ignores them.
- `passwordlessLogin` and `queryCustomer` work only with Shopify’s new customer accounts.
