
<SdkPage />

Subbly.js is Subbly's browser-side JavaScript library. It calls the Storefront
API for you, holds one cart per page, and keeps the cart ID and the customer
access token in browser cookies. `Subbly.init` resolves with an SDK instance;
every method in this reference is a method of that instance.

The SDK emits no events: it has no emitter and dispatches no DOM events. The
events you may have seen in Subbly belong to the
[cart widget](/reference/cart-widget), a separate product.

## Load

```js title="Start the SDK"
import Subbly from '@subbly/sdk'

const subbly = await Subbly.init({ apiKey: 'API_KEY' })
```

The package also ships a browser bundle,
`https://cdn.jsdelivr.net/npm/@subbly/sdk`, that defines the global `Subbly`.
It is the class, not an instance, so you still call `Subbly.init(config)`.

## Cookies

The SDK writes two cookies, each for 365 days on `path=/`:

- `subbly_cart_id` — the cart, written by `Subbly.init` when you pass
  `loadCart`, and by `subbly.cart.create`.
- `subbly_access_token` — the customer access token, written by the
  authentication methods and emptied by `subbly.auth.logout`.

Both are plain cookies. The SDK sets no `Secure`, `HttpOnly` or `SameSite`
attribute, so any script on the host can read them.

## Amounts and dates

Amounts are whole numbers in the minor unit of the currency: `1250` in a `USD`
cart means $12.50. Dates and timestamps are strings.

## Currency

The product, bundle and survey methods take an optional `headers` object as
their last argument. Set `x-currency` on it to price that one response in
another currency. When you leave it out, the SDK sends the currency you set with
[`subbly.setCurrency`](/reference/subbly-sdk/setup#set-currency); when you set
neither, it sends no currency header.

The subscription methods also take `headers`, but the SDK adds no currency to
them.

`setCurrency` does not change the cart. The cart has its own `currencyCode`;
change it with `subbly.cart.update({ currencyCode })`. When `Subbly.init` loads
a cart, it calls `setCurrency` with the cart currency for you.

```js title="Price one product in another currency"
subbly.setCurrency('USD')

const product = await subbly.products.load(123)
const inEuros = await subbly.products.load(123, {}, { 'x-currency': 'EUR' })
```

## Expand parameters

Most methods that return a cart or a subscription take an optional `params`
object with an `expand` list. Each entry names a relation the API should fill
in. A relation you do not name comes back `null`, so ask for what you need to
render.

The cart methods accept `coupon`, `gift_card`, `items.product`,
`items.product.parent`, `items.product.parent.pricings`,
`items.bundle.items.product`, `items.bundle.items.product.parent`,
`items.metadata`, `shipping_method`, `shipping_address`, `payment_method`,
`billing_address`, `metadata` and `shipping_carrier`. The funnel accepts that
return a cart take the same list.

The subscription methods accept `product`, `product.parent`,
`bundle.items.product`, `bundle.items.product.parent`, `metadata`, `addons`,
`discounts` and `cancellations`.

The funnel fetches accept `parent`, which loads the parent product of each
offered variant or plan.

```js title="Load a cart with its items and coupon"
await subbly.cart.load(cartId, {
  expand: ['items.product', 'items.product.parent', 'coupon']
})
```

## Errors

A failed request rejects with a plain object, not an `Error`. So
`error instanceof Error` is `false` and `error.message` may be missing. Read
`error.code` instead.

```js title="Handle an error"
try {
  await subbly.cart.addItem({ productId: 3301 })
} catch (error) {
  if (error.code === 'out_of_stock') {
    // tell the customer
  }
}
```

The codes are:

| `code` | What happened |
| :-- | :-- |
| `not_found` | The record does not exist. |
| `bad_request` | The request or the payload is invalid. |
| `out_of_stock` | A product or a survey answer ran out. |
| `payment_requires_action` | The card needs 3-D Secure. The object also carries `paymentIntentId`. |
| `user_email_is_not_unique` | The email already has an account. |
| `unauthenticated` | There is no valid access token. |

When the response has no body, the rejection is a real `Error` carrying the HTTP
status text instead. Handle both shapes.

Two rejections are plain `Error` objects instead, with no `code`:

- `Error('Missing required apiKey config')` — `Subbly.init` with no argument, or
  with an empty or missing `apiKey`. `init` is `async`, so it rejects.
- `Error('Cart ID is required')` — `subbly.cart.load` with no ID given and none
  held.

To install, configure and update the library, see the
[Subbly.js SDK article](/developer-resources/subbly-js-sdk).
