Carts and checkout
Make and fill a cart, estimate shipping and hand it to the shop's checkout page with a single-use link.
A cart is named by its token. Make one with createCart, keep its cartToken, and reach it again with cart. The CartSession that cart returns sends the token as the X-Cart-Token header for you. Every change answers with the whole cart, with its lines, totals and coupon.
Each write takes options last: idempotencyKey, signal and headers (see retries).
Create a cart
storefront.createCart(options?: RequestOptions): Promise<Cart>
A new empty cart, named by the returned cartToken. Keep the token, in the shopper's session or a cookie, to come back to the cart. Made on a client with a shopper's access token, the cart is theirs and priced for them. Limited like cart writes (30 a minute, 300 an hour per visitor).
Parameters
| Parameter |
Type |
|
options |
RequestOptions |
idempotencyKey, signal and headers (see retries). |
Returns
The new Cart:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | Not found, or the shop's plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
const cart = await storefront.createCart();
localStorage.setItem('cartToken', cart.cartToken);
Open a cart
storefront.cart(cartToken: string): CartSession
A CartSession for the cart a token names. Its methods below act on that cart, and its token property holds the token.
Parameters
| Parameter |
Type |
|
cartToken |
string |
The cart's token. |
Returns
A CartSession. No request is made.
Example
const session = storefront.cart(cart.cartToken);
Get the cart
session.get(): Promise<Cart>
The cart as it is now, priced as the shop's own cart page prices it.
Returns
A Cart:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled: this shop takes no orders through a cart. |
429 | Rate limited. |
Example
const cart = await session.get();
Add a product
session.addItem(productId: number, quantity?: number, options?: RequestOptions): Promise<Cart>
Adds a product, or more of one already in the cart. No stock is held until the cart is claimed at checkout. Limited like cart writes.
Parameters
| Parameter |
Type |
|
productId |
number |
The product's id. |
quantity |
number |
How many, from 1 to 10000. Default 1. |
options |
RequestOptions |
idempotencyKey, signal and headers. |
Returns
The changed Cart:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Example
const cart = await session.addItem(12, 2);
Change a line's quantity
session.updateItem(itemId: number, quantity: number, options?: RequestOptions): Promise<Cart>
Sets a line's quantity. A quantity of 0 removes the line. Limited like cart writes.
Parameters
| Parameter |
Type |
|
itemId |
number |
The cart line's id (items[].id). |
quantity |
number |
The new quantity, from 0 to 10000. |
options |
RequestOptions |
idempotencyKey, signal and headers. |
Returns
The changed Cart:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Example
const updated = await session.updateItem(cart.items[0].id, 3);
Remove a line
session.removeItem(itemId: number, options?: RequestOptions): Promise<Cart>
Removes a line from the cart.
Parameters
| Parameter |
Type |
|
itemId |
number |
The cart line's id (items[].id). |
options |
RequestOptions |
idempotencyKey, signal and headers. |
Returns
The changed Cart:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Example
const updated = await session.removeItem(cart.items[0].id);
Apply a coupon
session.applyCoupon(code: string, options?: RequestOptions): Promise<Cart>
Checked as the shop's own cart page checks it. A coupon only a known customer can use (first order, per customer) stays on the cart for checkout to decide. Limited to 10 attempts a minute and 60 an hour per visitor.
Parameters
| Parameter |
Type |
|
code |
string |
The coupon code, up to 50 characters, any case. |
options |
RequestOptions |
idempotencyKey, signal and headers. |
Returns
The changed Cart:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | The code is unknown or can't be used (errors.code), or an idempotency refusal. |
429 | Rate limited. |
Example
const cart = await session.applyCoupon('WELCOME10');
Remove the coupon
session.removeCoupon(options?: RequestOptions): Promise<Cart>
Removes the cart's coupon. Removing an auto-applied coupon stops coupons auto-applying to this cart.
Parameters
| Parameter |
Type |
|
options |
RequestOptions |
idempotencyKey, signal and headers. |
Returns
The changed Cart:
| Field | Type | Description |
|---|
cartToken | string | Send it as X-Cart-Token. |
mode | string | enquiry: a wholesale member's cart, sent to the shop as a quotation request. One of checkout, enquiry. |
customerId | integer or null | The shopper the cart belongs to; null for a guest cart. |
items | array of CartItem | |
items[].id | integer | |
items[].productId | integer | |
items[].slug | string or null | |
items[].title | string or null | |
items[].image | string (uri) or null | |
items[].quantity | integer | |
items[].unitPrice | Money | Fields: amount, currency, formatted. |
items[].lineTotal | Money | Fields: amount, currency, formatted. |
totalQuantity | integer | |
subtotal | Money | Fields: amount, currency, formatted. |
discount | Money | Fields: amount, currency, formatted. |
coupon | object or null | |
coupon.code | string | |
coupon.autoApplied | boolean | |
couponError | string or null | Why the coupon on the cart doesn't apply now. |
offers | array of string | Labels of the offers applied. |
taxAdded | Money | Fields: amount, currency, formatted. |
taxIncluded | Money | Fields: amount, currency, formatted. |
totalBeforeShipping | Money | Fields: amount, currency, formatted. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | cart_change_refused with the shopper-worded reason (out of stock…), validation errors, or an idempotency refusal. |
429 | Rate limited. |
Estimate shipping
session.estimateShipping(destination: ShippingDestination, options?: RequestOptions): Promise<ShippingEstimate>
The shipping rules the shopper could choose at checkout, priced for a destination as checkout prices them. Only when the shop turned its shipping calculator on (shippingEstimates from shop()). A shop without zones ships anywhere, free. Limited to 30 a minute and 300 an hour per visitor.
Parameters
| Parameter |
Type |
|
destination |
ShippingDestination |
The destination's fields, below. |
options |
RequestOptions |
idempotencyKey, signal and headers. |
Request fields
| Field | Type | Description |
|---|
shippingCountryCode required | string | ISO 3166-1 alpha-3. At most 3 characters. |
shippingCity | string or null | At most 100 characters. |
shippingState | string or null | At most 255 characters. |
shippingPostalCode | string or null | At most 50 characters. |
shippingAddressLine1 | string or null | At most 1000 characters. |
shippingStateId | integer or null | |
shippingCityId | integer or null | |
shippingNeighbourhoodId | integer or null | |
shippingLocationUnlisted | boolean or null | |
Returns
A ShippingEstimate:
| Field | Type | Description |
|---|
shipsTo | boolean | Whether the shop ships to the country. |
shipsFree | boolean | True for a shop without shipping zones (ships anywhere, free). |
quotes | array of object | Empty for an empty or digital-only cart. |
quotes[].methodId | integer | |
quotes[].name | string | |
quotes[].cost | number | A plain amount in the shop's base currency (not a Money object). |
quotes[].isLiveRate | boolean | |
quotes[].isCarrierPrice | boolean | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | shipping_estimates_off, cart_not_found, or the shop's plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
const estimate = await session.estimateShipping({
shippingCountryCode: 'SAU',
shippingCity: 'Riyadh',
});
if (estimate.shipsTo) {
estimate.quotes.forEach((quote) => console.log(quote.name, quote.cost));
}
Get a checkout link
session.checkout(options?: CheckoutOptions, request?: RequestOptions): Promise<CheckoutLink>
A single-use link to the shop's checkout page with this cart, valid for 30 minutes, for the shopper's browser to open now. Once opened, the cart belongs to that browser and the token no longer reaches it. When requiresSignIn is true, the cart belongs to a shopper, who must be signed in on the shop. A wholesale member's cart (mode enquiry) leads to the shop's cart page, where it is sent as a quotation request. Limited like cart writes.
Parameters
| Parameter |
Type |
|
options |
CheckoutOptions |
The checkout's fields, below. |
request |
RequestOptions |
idempotencyKey, signal and headers. |
Request fields
| Field | Type | Description |
|---|
returnPath | string or null | Path on the shop's site (Settings → Integration API) the receipt's "Continue shopping" links to. Starts with a single /. At most 500 characters. |
cartPath | string or null | Path on the shop's site checkout's back link leads to. Starts with a single /. At most 500 characters. |
attribution | object or null | The visit's UTM tags and click ids, recorded when the link is opened. |
attribution.utmSource | string or null | At most 255 characters. |
attribution.utmMedium | string or null | At most 255 characters. |
attribution.utmCampaign | string or null | At most 255 characters. |
attribution.fbclid | string or null | At most 255 characters. |
attribution.gclid | string or null | At most 255 characters. |
attribution.gbraid | string or null | At most 255 characters. |
attribution.wbraid | string or null | At most 255 characters. |
attribution.dclid | string or null | At most 255 characters. |
attribution.msclkid | string or null | At most 255 characters. |
attribution.ttclid | string or null | At most 255 characters. |
attribution.liFatId | string or null | At most 255 characters. |
attribution.twclid | string or null | At most 255 characters. |
Returns
A CheckoutLink:
| Field | Type | Description |
|---|
checkoutUrl | string (uri) | Open it in the shopper's browser. Single use. |
expiresAt | string (date-time) | |
mode | string | One of checkout, enquiry. |
requiresSignIn | boolean | The browser must be signed in on the checkout host as the cart's owner. |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Storefront key of this shop, or invalid_customer_token: a bearer token not valid for this shop. |
402 | The shop is suspended (message only). |
403 | origin_not_allowed (publishable key from an unlisted site), secret_key_in_browser, or the shop is not live (message only). |
404 | cart_not_found: the token names no open cart of this shop (opened at checkout, checked out, or another's), or the plan has no Integration API. |
409 | cart_disabled, or idempotency_in_progress. |
422 | Invalid paths or attribution, cart_empty, or an idempotency refusal. |
429 | Rate limited. |
Example
const { checkoutUrl } = await session.checkout({
returnPath: '/thanks',
cartPath: '/cart',
attribution: { utmSource: 'newsletter', utmCampaign: 'november' },
});
window.location.assign(checkoutUrl);