Cart JS SDK
Make cart operations and listen to events. Used in Duda's native ecommerce, paid booking, membership, and more.
The Public Ecom SDK is a stable JavaScript API for cart, checkout, orders, and payments. It is exposed on the page as window.dmAPI.ecomm and is intended for third-party scripts and integrations. The window.dmAPI.ecomm namespace is only defined when the cart is installed on the site; otherwise it is undefined.
Relevant features
Use this SDK to work with the cart and checkout on sites using:
- Duda's Native Store
- Duda's Bookings with paid appointment types
- Duda's Membership with paid plans
Accessing the SDK
The SDK is available globally after the cart loads:
// Wait for the SDK to be ready (e.g. after app boot)
await window.dmAPI.ecomm.ready;
// Use the SDK — async methods resolve to a [result, error] tuple
const [cart, error] = await window.dmAPI.ecomm.getCart({ cart_id: '...' });window.dmAPI.ecomm— Public API instance. Available as soon as the loader runs; usereadyto wait until the app has finished initializing.ready— APromise<void>that resolves when the SDK is fully initialized and methods can be called.
Availability: The window.dmAPI.ecomm namespace is only defined when the one of the relevant features is installed on the site. If ecom is not installed, window.dmAPI.ecomm will be undefined. Check for its presence before calling the API (e.g. if (window.dmAPI?.ecomm) { ... }).
Result tuples: Async methods do not throw. Instead they resolve to a [result, error] tuple: on success [result, undefined], on failure [undefined, error] where error is an EcomSDKError. Destructure and check error before using the result. The synchronous helpers (openCart, closeCart, clearCart, formatMoney, login) return their value directly and never produce a tuple. See Error handling for details. — note that formatMoney and login validate their input and throw InvalidPayloadSDKError if it is malformed. One async method, clearCart, is not tuple-wrapped: it returns a Promise<void> that rejects on failure. See Error handling for details.
Catalog data
Some methods require passing IDs from the store catalog.
These can be retrieved using the Store JS API.
Usage example
The following example waits for the SDK, adds a product to the cart, applies a discount, sets contact information, formats a price, and subscribes to cart updates. Async methods resolve to a [result, error] tuple — check error before using the result. All IDs and values are fake and for illustration only.
(async function () {
await window.dmAPI.ecomm.ready;
const sdk = window.dmAPI.ecomm;
// Subscribe to cart updates (e.g. for analytics or UI sync)
window.dmAPI.subscribeEvent('event-ecomm-cartUpdated', (event) => {
// sync a custom cart badge, trigger analytics, etc.
});
// Open the cart UI so the customer sees it (synchronous, never throws)
sdk.openCart();
// Add a product to the cart
const [, addError] = await sdk.addProduct({
product_id: 'prod_tshirt_001',
variation_id: 'var_tshirt_001_blue_m',
quantity: 2,
});
if (addError) {
// handle the failure (show a toast, abort the flow, etc.)
return;
}
// Apply a discount code
const [, discountError] = await sdk.addDiscount({ code: 'WELCOME10' });
if (discountError) {
// the code may be invalid or expired
}
// Set contact information
await sdk.setContactInformation({
email: '[email protected]',
marketing_opt_in: true,
});
// Format a price for display (synchronous; uses the cart currency and locale by default)
const formatted = sdk.formatMoney({ amount: 2999 });
// Fetch the current cart
const [cart, cartError] = await sdk.getCart({ cart_id: 'cart_abc123' });
if (cartError) {
// handle missing or inaccessible cart
}
// Fetch the logged-in customer's orders
const [orders, ordersError] = await sdk.getOrders({ limit: 5, offset: 0 });
if (ordersError) {
// handle the failure (e.g. not authenticated)
}
})();Same flow with a one-off product purchase (buy now, no cart UI):
await window.dmAPI.ecomm.ready;
const sdk = window.dmAPI.ecomm;
const [, error] = await sdk.buyProduct({
product_id: 'prod_ebook_001',
variation_id: 'var_ebook_001_default',
quantity: 1,
});
if (error) {
// handle the failure
}
// On success, the cart is created automatically; complete checkout via the cart UI.Reference
Cart UI
openCart()
openCart()Opens the cart UI (drawer or panel).
Returns: void
Example:
window.dmAPI.ecomm.openCart();closeCart()
closeCart()Closes the cart UI.
Returns: void
Example:
window.dmAPI.ecomm.closeCart();Cart lifecycle
cart
cartThe current cart, as a CartResponseDto — the same shape the cart methods return. This is a property, not a method: reading it costs nothing and performs no request, so you don't need to await it or handle an error tuple.
Type: CartResponseDto | null — null when the visitor has no cart yet, or after clearCart().
Example:
await window.dmAPI.ecomm.ready;
const cart = window.dmAPI.ecomm.cart;
if (cart) {
console.log(cart.id, cart.total, cart.items.length);
}It reflects the latest cart the store received, so re-read it after any cart operation rather than holding on to an old value:
const [, error] = await window.dmAPI.ecomm.addProduct({
product_id: 'prod_tshirt_001',
variation_id: 'var_tshirt_001_blue_m',
});
if (!error) {
console.log(window.dmAPI.ecomm.cart.items.length); // includes the new item
}You can also keep it in sync by re-reading it inside an event handler:
window.dmAPI.subscribeEvent('event-ecomm-cartUpdated', () => {
render(window.dmAPI.ecomm.cart);
});Freshness: cart is updated by the cart operations performed on this page — it is not polled, so it will not pick up changes made elsewhere (another tab, another device, or server-side expiry).
getCart fetches from the server, but it never updates cart: the fresh data is returned in the [cart, error] tuple and nowhere else. Use the returned value directly.
const id = window.dmAPI.ecomm.cart?.id;
const [freshCart, error] = await window.dmAPI.ecomm.getCart({ cart_id: id });
if (!error) {
render(freshCart); // use the returned cart...
// ...not window.dmAPI.ecomm.cart, which is still the pre-fetch value
}Treat it as read-only: the object is detached from the store's internal state, so modifying it will not change the actual cart — but every reader gets the same object, so a mutation is visible to other scripts on the page until the next cart update replaces it. Use the SDK methods to change the cart, and copy the object first if you need to transform it.
createCart(payload?)
createCart(payload?)Creates a new cart. If no cart exists, one is created automatically when needed; use this when you want to prefill email, addresses, metadata, or items.
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | No | Customer email. |
shipping_address | PersonalAddressRequestDto | No | Prefilled shipping address. |
billing_address | PersonalAddressRequestDto | No | Prefilled billing address. |
metadata | object | No | Custom metadata. |
items | AddItemRequestDto[] | No | Initial line items. |
timezone | string | No | Customer timezone (defaults to browser). |
Returns: Promise<[CreateCartResponse, undefined] | [undefined, EcomSDKError]> — [cart, undefined] on success (e.g. CartResponseDto); [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.createCart({
email: '[email protected]',
metadata: { source: 'landing_page' },
});
if (error) {
// handle the failure
}getCart(payload)
getCart(payload)Fetches a cart from the server by ID.
To read the current cart, use the cart property instead — it is already available and costs no request. Reach for getCart when you need to read a different cart (for example a cart ID from an abandoned-cart link), or to force a fresh read of a cart that may have changed outside this page.
| Parameter | Type | Required | Description |
|---|---|---|---|
cart_id | string | Yes | Cart ID. |
Returns: Promise<[GetCartResponse, undefined] | [undefined, EcomSDKError]> — [cart, undefined] on success; [undefined, error] on failure.
cart_id is required; calling getCart() with no argument throws InvalidPayloadSDKError. The current cart's ID is available as window.dmAPI.ecomm.cart?.id.
Example:
const [cart, error] = await window.dmAPI.ecomm.getCart({
cart_id: 'cart_abc123',
});
if (error) {
// handle missing or inaccessible cart
}getCart is a pure read. It does not make the fetched cart current, and it does not update the cart property — even when you pass the current cart's ID. The fetched cart is returned in the tuple and nowhere else, so read it from there:
const [freshCart, error] = await window.dmAPI.ecomm.getCart({
cart_id: window.dmAPI.ecomm.cart.id,
});
// freshCart → what the server has now
// dmAPI.ecomm.cart → unchanged by this callclearCart()
clearCart()Clears the current cart (removes all items and resets cart state).
Returns: Promise<void> — resolves once the cart has been cleared.
Example:
await window.dmAPI.ecomm.clearCart();Cart items
addProduct(payload)
addProduct(payload)Adds a product (catalog item) to the current cart.
| Parameter | Type | Required | Description |
|---|---|---|---|
product_id | string | Yes | Product identifier. |
variation_id | string | Yes | Variation identifier. |
quantity | number | No | Quantity (default 1). |
metadata | object | No | Item metadata. |
product_customizations | ProductCustomizationPayload[] | No | Options/customizations. |
Returns: Promise<[AddProductResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.addProduct({
product_id: 'prod_tshirt_001',
variation_id: 'var_tshirt_blue_m',
quantity: 2,
});
if (error) {
// handle the failure
}buyProduct(payload)
buyProduct(payload)Adds a product to the cart in a “buy now” flow (e.g. from product page). Similar to addProduct with store catalog source.
| Parameter | Type | Required | Description |
|---|---|---|---|
product_id | string | Yes | Product identifier. |
variation_id | string | Yes | Variation identifier. |
plan_id | string | No | Subscription plan ID if applicable. |
quantity | number | No | Quantity. |
metadata | object | No | Item metadata. |
product_customizations | ProductCustomizationPayload[] | No | Customizations. |
cart_options | CreateCartPayload | No | Options used to prefill the cart that is auto-created for the purchase (email, addresses, metadata, timezone, items). Same shape as createCart. |
Returns: Promise<[BuyProductResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.buyProduct({
product_id: 'prod_ebook_001',
variation_id: 'var_ebook_default',
quantity: 1,
});
if (error) {
// handle the failure
}Prefilling the cart with cart_options
cart_optionsThe "buy now" methods (buyProduct, buyMembership, buyBooking) create and use a cart automatically. Pass cart_options to seed that cart in the same call — it accepts the same fields as createCart (email, shipping_address, billing_address, metadata, items, timezone). This avoids a separate createCart round-trip when you already know the customer's details.
// Buy now and prefill the auto-created cart with the customer's email,
// a shipping address, and order metadata in a single call.
const [cart, error] = await window.dmAPI.ecomm.buyProduct({
product_id: 'prod_ebook_001',
variation_id: 'var_ebook_default',
quantity: 1,
cart_options: {
email: '[email protected]',
metadata: { source: 'product_page' },
shipping_address: {
full_name: 'Jane Doe',
address_1: '123 Main St',
city: 'Boston',
region: 'MA',
country: 'US',
postal_code: '02101',
},
},
});
if (error) {
// handle the failure
}If a cart already exists,
cart_optionsoverwrites its matching details (same behavior as callingcreateCartdirectly).
updateItemQuantity(payload)
updateItemQuantity(payload)Updates the quantity of a cart line item.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Line item ID. |
quantity | number | Yes | New quantity. |
Returns: Promise<[UpdateItemQuantityResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.updateItemQuantity({
id: 'item_xyz789',
quantity: 3,
});
if (error) {
// handle the failure
}removeItem(payload)
removeItem(payload)Removes a line item from the cart.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Line item ID. |
Returns: Promise<[RemoveItemResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.removeItem({
id: 'item_xyz789',
});
if (error) {
// handle the failure
}buyMembership(payload)
buyMembership(payload)Adds a membership (subscription plan) to the cart.
| Parameter | Type | Required | Description |
|---|---|---|---|
plan_id | string | Yes | Membership plan ID. |
metadata | object | No | Optional metadata. |
cart_options | CreateCartPayload | No | Options used to prefill the cart that is auto-created for the purchase (email, addresses, metadata, timezone, items). Same shape as createCart. |
Returns: Promise<[BuyMembershipResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.buyMembership({
plan_id: 'plan_premium_annual',
});
if (error) {
// handle the failure
}buyBooking(payload)
buyBooking(payload)Adds a booking to the cart.
| Parameter | Type | Required | Description |
|---|---|---|---|
event_name | string | Yes | Event name. |
event_type_id | number | Yes | Event type ID. |
slot_start | string | Yes | Slot start time. |
slot_duration | number | No | Duration. |
time_zone | string | Yes | Timezone. |
metadata | object | No | Optional metadata. |
fields_responses | Record<string, string> | No | Form/field responses. |
customer_email | string | No | Customer email. |
customer_name | string | No | Customer name. |
hosts_ids | number[] | No | Host IDs. |
cart_options | CreateCartPayload | No | Options used to prefill the cart that is auto-created for the booking (email, addresses, metadata, timezone, items). Same shape as createCart. Note: customer_email takes precedence over cart_options.email. |
Returns: Promise<[BuyBookingResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.buyBooking({
event_name: 'Yoga Workshop',
event_type_id: 1,
slot_start: '2025-06-15T10:00:00',
time_zone: 'America/New_York',
customer_email: '[email protected]',
});
if (error) {
// handle the failure
}Discounts
addDiscount(payload)
addDiscount(payload)Applies a discount coupon code to the cart.
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Discount (coupon) code. |
force_apply | boolean | No | Force apply even if validation is strict. |
Returns: Promise<[AddDiscountResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure (e.g. invalid or expired code).
Example:
const [cart, error] = await window.dmAPI.ecomm.addDiscount({ code: 'SAVE20' });
if (error) {
// the code may be invalid or expired
}removeDiscount(payload)
removeDiscount(payload)Removes an applied discount from the cart.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Discount (application) ID. |
Returns: Promise<[RemoveDiscountResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.removeDiscount({
id: 'disc_abc123',
});
if (error) {
// handle the failure
}Checkout steps
setContactInformation(payload)
setContactInformation(payload)Sets the contact step (email, marketing opt-in, custom fields).
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
email | string | Yes | Customer email. | |
marketing_opt_in | boolean | No | Marketing consent. | |
custom_fields | `Record<string, string | null>` | No | Custom field values. |
Returns: Promise<[SetContactInformationResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.setContactInformation({
email: '[email protected]',
marketing_opt_in: true,
});
if (error) {
// handle the failure
}setShippingAddress(payload)
setShippingAddress(payload)Sets the shipping address and whether it is also used for billing.
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
shipping_address | PersonalAddressRequestDto | Yes | Shipping address. | |
use_shipping_address_as_billing_address | boolean | Yes | Use same address for billing. | |
custom_fields | `Record<string, string | null>` | No | Custom fields. |
Returns: Promise<[SetShippingAddressResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.setShippingAddress({
shipping_address: {
full_name: 'Jane Doe',
address_1: '123 Main St',
city: 'Boston',
region: 'MA',
country: 'US',
postal_code: '02101',
},
use_shipping_address_as_billing_address: true,
});
if (error) {
// handle the failure
}setFulfillmentMethod(payload)
setFulfillmentMethod(payload)Sets the fulfillment method (shipping, pickup, etc.).
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
shipping_instructions | string | No | Shipping notes. | |
shipping_courier_id | string | No | Required for shipping. | |
shipping_localized_name | string | No | Display name for shipping. | |
pickup_location_id | string | No | Required for pickup. | |
custom_fields | `Record<string, string | null>` | No | Custom fields. |
Returns: Promise<[SetFulfillmentMethodResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.setFulfillmentMethod({
shipping_instructions: 'Leave at front door',
shipping_courier_id: 'courier_standard',
});
if (error) {
// handle the failure
}setBillingAddress(payload)
setBillingAddress(payload)Sets the billing address.
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
billing_address | PersonalAddressRequestDto | Yes | Billing address. | |
custom_fields | `Record<string, string | null>` | No | Custom fields. |
Returns: Promise<[SetBillingAddressResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.setBillingAddress({
billing_address: {
full_name: 'Jane Doe',
address_1: '123 Main St',
city: 'Boston',
region: 'MA',
country: 'US',
postal_code: '02101',
},
});
if (error) {
// handle the failure
}setMetadata(payload, cartId?)
setMetadata(payload, cartId?)Sets or clears cart-level metadata.
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
metadata | `object | null` | No | Key-value metadata; null to clear. |
cartId | string | No | Cart ID (second argument; defaults to current cart). |
Returns: Promise<[SetMetadataResponse, undefined] | [undefined, EcomSDKError]> — [updatedCart, undefined] on success; [undefined, error] on failure.
Example:
const [cart, error] = await window.dmAPI.ecomm.setMetadata({
metadata: { order_ref: 'REF-12345' },
});
if (error) {
// handle the failure
}Orders
getOrder(payload)
getOrder(payload)Fetches a single order by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
order_id | string | Yes | Order ID. |
Returns: Promise<[GetOrderResponse, undefined] | [undefined, EcomSDKError]> — [order, undefined] on success (OrderResponseDto); [undefined, error] on failure.
Example:
const [order, error] = await window.dmAPI.ecomm.getOrder({
order_id: 'ord_abc123',
});
if (error) {
// handle the failure
}getOrders(payload?)
getOrders(payload?)Fetches the current customer’s orders with optional pagination and sort order.
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
offset | number | No | Pagination offset. | |
limit | number | No | Page size. | |
direction | `'asc' | 'desc'` | No | Sort direction. |
Returns: Promise<[GetOrdersResponse, undefined] | [undefined, EcomSDKError]> — [orders, undefined] on success (OrdersResponseDto); [undefined, error] on failure.
Example:
const [orders, error] = await window.dmAPI.ecomm.getOrders({
limit: 10,
offset: 0,
direction: 'desc',
});
if (error) {
// handle the failure
}Subscriptions & customer
getSubscriptions(payload)
getSubscriptions(payload)Fetches the current customer’s subscriptions with pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | number | Yes | Page size. |
offset | number | Yes | Pagination offset. |
Returns: Promise<[GetSubscriptionsResponse, undefined] | [undefined, EcomSDKError]> — [subscriptions, undefined] on success; [undefined, error] on failure.
Example:
const [subscriptions, error] = await window.dmAPI.ecomm.getSubscriptions({
limit: 10,
offset: 0,
});
if (error) {
// handle the failure
}getSubscription(payload)
getSubscription(payload)Fetches a single subscription by ID (must belong to the current customer).
| Parameter | Type | Required | Description |
|---|---|---|---|
subscription_id | string | Yes | Subscription ID. |
Returns: Promise<[GetSubscriptionResponse, undefined] | [undefined, EcomSDKError]> — [subscription, undefined] on success; [undefined, error] on failure.
Example:
const [subscription, error] = await window.dmAPI.ecomm.getSubscription({
subscription_id: 'sub_abc123',
});
if (error) {
// handle the failure
}requestSubscriptionCancellation(payload)
requestSubscriptionCancellation(payload)Requests cancellation for a subscription.
| Parameter | Type | Required | Description |
|---|---|---|---|
subscription_id | string | Yes | Subscription ID. |
Returns: Promise<[RequestSubscriptionCancellationResponse, undefined] | [undefined, EcomSDKError]> — [updatedSubscription, undefined] on success; [undefined, error] on failure.
Example:
const [subscription, error] =
await window.dmAPI.ecomm.requestSubscriptionCancellation({
subscription_id: 'sub_abc123',
});
if (error) {
// handle the failure
}updateCustomer(payload)
updateCustomer(payload)Updates the logged-in customer profile (PATCH /customer/me). Requires an authenticated session.
| Parameter | Type | Required | Description |
|---|---|---|---|
fullname | string | No | Full name. |
marketing_opt_in | boolean | No | Marketing consent. |
language | string | No | Language code. |
phone | string | No | Phone number. |
shipping_address | PersonalAddressRequestDto | No | Shipping address fields. |
Returns: Promise<[UpdateCustomerResponse, undefined] | [undefined, EcomSDKError]> — [updatedCustomer, undefined] on success (CustomerResponseDto); [undefined, error] on failure.
Example:
const [customer, error] = await window.dmAPI.ecomm.updateCustomer({
fullname: 'Jane Doe',
phone: '+15551234567',
});
if (error) {
// handle the failure
}Utilities
formatMoney(payload)
formatMoney(payload)Formats a number as a money string using the store’s currency and locale (or overrides).
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount to format (e.g. cents). |
override | object | No | Override currency, locale, or custom format. |
override.currency | string | No | Currency code. |
override.locale | string | No | Locale for formatting. |
override.customFormat | CurrencyCustomFormat | No | Custom format options. |
Returns: string — Formatted money string (e.g. "$10.00").
Example (default behavior): uses the cart/store currency and locale settings.
window.dmAPI.ecomm.formatMoney({
amount: 1999,
});Example (override currency/locale/custom format):
window.dmAPI.ecomm.formatMoney({
amount: 1999,
override: {
currency: 'USD',
locale: 'en-US',
customFormat: {
groupSeparator: ',',
decimalSeparator: '.',
keepTrailingZeroesDecimals: true,
symbol: '$',
symbolPlacement: 'BEFORE_PRICE',
},
},
});Events
window.dmAPI.subscribeEvent(eventName, handler)
window.dmAPI.subscribeEvent(eventName, handler)Subscribes to cart lifecycle events via the DM API event bus. This is the supported way to listen for ecom events.
| Parameter | Type | Required | Description |
|---|---|---|---|
eventName | string | Yes | One of the event names below. |
handler | (event) => void | Yes | Callback invoked when the event is published. The event shape depends on the event (e.g. { type, cart } for cart events). |
Event names:
| Event | Description |
|---|---|
event-ecomm-cartCreated | A new cart was created. |
event-ecomm-cartUpdated | Cart was updated (items, address, etc.). |
event-ecomm-cartCompleted | Cart was completed (order placed). |
event-ecomm-cartCleared | Cart was cleared. |
Example:
window.dmAPI.subscribeEvent('event-ecomm-cartUpdated', (event) => {
// Handle cart update
});Authentication
login(payload)
login(payload)Redirects the user to the login page. After sign-in, the user is redirected back; you can pass a return URL and query params.
| Parameter | Type | Required | Description |
|---|---|---|---|
returnUrl | string | No | URL to return to after login (defaults to current page). |
returnUrlParams | Record<string, string> | No | Query parameters to add to the return URL. |
Returns: void (redirects the browser).
Example:
window.dmAPI.ecomm.login({
returnUrl: '/account',
returnUrlParams: { tab: 'orders' },
});Payload types
Several method payloads reference named object types instead of inlining their fields (for example, product_customizations on addProduct is a ProductCustomizationPayload[]). This section defines each of those types, what it is used for, and — where the shape has nuance — how to build a valid value.
Types shown as TypeScript are exactly as validated by the SDK before a request is sent; a payload that fails this validation throws InvalidPayloadSDKError (see Error handling). Field types not covered here (string, number, boolean, object, Record<string, string | null>) are primitives or self-explanatory maps and are documented inline in each method's parameter table.
CartResponseDto
CartResponseDtoThe cart. This is what every cart method resolves to — createCart, getCart, addProduct, addDiscount, the checkout step setters, and so on.
| Field | Type | Description | |||
|---|---|---|---|---|---|
id | string | Cart ID. Pass this to getCart. | |||
mode | `'LIVE' | 'TEST'` | Whether the cart belongs to a live or test store. | ||
status | `'IN_PROGRESS' | 'ABANDONED'` | Server-side cart status. A completed cart becomes an order — see getOrder. | ||
summary | PurchaseSummaryResponseDto | Totals breakdown (subtotal, discounts, shipping, fees, taxes). Prefer this over the legacy fields. | |||
language | `string | null` | Cart language. | ||
email | `string | null` | Customer email, once the contact step is filled. | ||
currency | string | Currency code. Pass to formatMoney to render amounts. | |||
items | CartLineItemResponseDto[] | Line items. Not a quantity count — see the note below. | |||
billing_address | `PersonalAddressResponseDto | null` | Billing address. | ||
shipping_address | `PersonalAddressResponseDto | null` | Shipping address. | ||
shipping_method | `CartShippingMethodResponseDto | null` | The selected shipping method. | ||
shipping_instructions | `string | null` | Free-text delivery instructions. | ||
pickup | `PickupLocationResponseDto | null` | The selected pickup location, when fulfilling by pickup. | ||
discounts | DiscountResponseDto[] | Applied discounts. | |||
taxes | CartTaxResponseDto[] | Calculated taxes. | |||
subtotal | number | Legacy subtotal. Prefer summary.subtotal_amount. | |||
total | number | Cart total. | |||
created | string | When the cart was created. | |||
updated | string | When the cart last changed. | |||
user_agent | `string | null` | User agent recorded on the cart. | ||
ip_address | `string | null` | IP address recorded on the cart. | ||
metadata | `object | null` | Whatever you stored with setMetadata. | ||
tax_provider | `'BUILT_IN' | 'AVALARA' | 'UNKNOWN' | null` | Which provider calculated the taxes. |
available_shipping_rates | `{rates: ShippingRateResponseDto[]} | null` | Rates to offer for the current address. Pass a rate id to setFulfillmentMethod. | ||
available_pickup_locations | PickupLocationResponseDto[] | Pickup locations to offer. | |||
tax_mode | `'TAXES_EXCLUDED_FROM_PRICE' | 'TAX_INCLUDED_WITH_NET_PRICE' | 'TAX_INCLUDED_WITH_GROSS_PRICE'` | Whether displayed prices already include tax. | |
custom_fields | CartCustomFieldResponseDto[] (optional) | Checkout custom fields and their current values. | |||
accepts_marketing | boolean | Whether the customer opted into marketing. | |||
payment | `CartPaymentResponseDto | null` | Payment methods and payment session state. | ||
side_effects | `{code: string}[] | null` | Notices the store raised while updating the cart (e.g. an item became unavailable). | ||
errors | { shipping_methods?: { readable_code: string, message: string } } | Per-area failures that did not fail the whole request — most commonly a shipping-rate lookup. |
Counting items: items is the list of distinct line items, so items.length is not the number of products in the cart. Sum the quantities:
const [cart] = await window.dmAPI.ecomm.getCart({ cart_id: 'cart_abc123' });
const itemCount = (cart?.items ?? []).reduce(
(total, item) => total + item.quantity,
0,
);Subscriptions: a cart contains a subscription when one of its items has a non-null plan (see CartLineItemResponseDto):
const subscriptionItem = cart?.items.find((item) => item.plan !== null);PurchaseSummaryResponseDto
PurchaseSummaryResponseDtoThe totals breakdown on cart.summary. Every *_amount is in the cart's currency.
| Field | Type | Description | |
|---|---|---|---|
subtotal_amount | `number | null` | Items subtotal, after item-level discounts. |
original_subtotal_amount | `number | null` (optional) | Subtotal before discounts, when it differs. |
discount_amount | `number | null` | Total savings from applied discounts. |
shipping_amount | `number | null` | Shipping cost. |
fees | `{id, name, amount, description?}[] | null` | Additional fees. |
net_total_amount | `number | null` | Total before taxes. |
taxes | `{id, name, percentage, total_amount}[] | null` | Taxes, itemized. |
total_taxes | number | Sum of all taxes. | |
total_amount | number | Grand total — what the customer pays. |
CartLineItemResponseDto
CartLineItemResponseDtoOne line in cart.items.
| Field | Type | Description | |||
|---|---|---|---|---|---|
id | string | Line item ID. Pass to updateItemQuantity / removeItem. | |||
added | string | When the item was added. | |||
product_id | `string | null` | Catalog product ID. | ||
variation_id | `string | null` | Catalog variation ID. | ||
external_product_id | `string | null` | Product ID in an external catalog. | ||
external_variation_id | `string | null` | Variation ID in an external catalog. | ||
name | string | Display name. | |||
description | `string | null` | Display description. | ||
image | `string | null` | Image URL. | ||
sku | `string | null` | SKU. | ||
plan | `{ id, name, frequency: 'WEEKLY' | 'MONTHLY' | 'YEARLY', tag_line } | null` | Subscription plan, when the item is a subscription. |
options | { name, value }[] | Chosen variation options (size, colour, …). | |||
product_customizations | ProductCustomizationResponseDto[] | Buyer-supplied customizations. | |||
quantity | number | Quantity. | |||
shippable | boolean | Whether the item requires shipping. | |||
unit_price | number | Price for one unit. | |||
unit_weight | `number | null` | Weight of one unit. | ||
unit_dimensions | `{height, width, length} | null` | Dimensions of one unit; each value may be null. | ||
combined_weight | `number | null` | unit_weight × quantity. | ||
discounts | `{ id, code, name, savings, type: 'RATE' | 'AMOUNT', rate_or_amount_value }[]` | Discounts applied to this line. | ||
taxes | { id, name, rate, amount }[] | Taxes on this line. | |||
subtotal | number | Line subtotal. | |||
original_subtotal | `number | null` (optional) | Line subtotal before discounts. | ||
total | number | Line total. | |||
metadata | Record<string, unknown> | Metadata stored on the line item. | |||
booking_intent | `{slot_start, slot_duration, time_zone, reservation_valid_until, fields_responses} | null` | Reserved slot, for items added with buyBooking. |
ProductCustomizationResponseDto
ProductCustomizationResponseDtoA buyer-supplied customization on a line item. Discriminate on type:
type ProductCustomizationResponseDto =
| {
type: 'TEXT';
id: string;
label: string;
value?: string;
price_modifier?: number;
}
| {
type: 'BOOLEAN';
id: string;
label: string;
value: boolean;
price_modifier?: number;
}
| {
type: 'OPTIONS';
id: string;
label?: string | null;
value: string[];
options_price_modifiers?: Array<number | null>;
price_modifier?: number;
}
| {
type: 'FILE';
id: string;
label: string;
value: {
filename: string;
mimetype: string;
size: number;
url: string;
key: string;
handle: string;
uploaded_at: string;
}[];
price_modifier?: number;
};PersonalAddressRequestDto
PersonalAddressRequestDtoThe address shape used for cart, checkout, and customer addresses. Used by createCart (shipping_address, billing_address), setShippingAddress, setBillingAddress, updateCustomer (shipping_address), and inside CreateCartPayload.
The name and street can each be supplied in one of two forms — a combined field or its split parts — but not both. The unused alternative must be omitted or null:
- Name: either
full_name, or bothfirst_nameandlast_name. - Street: either
address_1, or bothstreet_nameandstreet_number.
type PersonalAddressRequestDto = (
// Name — pick ONE form
| { full_name: string; first_name?: null; last_name?: null }
| { full_name?: null; first_name: string; last_name: string }
) & (
// Street — pick ONE form
| { address_1: string; street_name?: null; street_number?: null }
| { address_1?: null; street_name: string; street_number: string }
) & {
address_2?: string | null;
city: string; // required
region?: string | null;
sub_locality?: string | null;
country: string; // required
postal_code: string; // required
phone?: string | null;
};Example (combined form — the common case):
{
full_name: 'Jane Doe',
address_1: '123 Main St',
city: 'Boston',
region: 'MA',
country: 'US',
postal_code: '02101',
}Example (split form):
{
first_name: 'Jane',
last_name: 'Doe',
street_name: 'Main St',
street_number: '123',
city: 'Boston',
region: 'MA',
country: 'US',
postal_code: '02101',
}ProductCustomizationPayload
ProductCustomizationPayloadA single product customization (option) submitted with addProduct or buyProduct via product_customizations. It is a discriminated union on type — the shape of value depends on the customization's type. id is the identifier of the customization field configured on the product.
type ProductCustomizationPayload =
| { type: 'TEXT'; id: string; value?: string }
| { type: 'BOOLEAN'; id: string; value: boolean }
| { type: 'OPTIONS'; id: string; value?: string[] }
| { type: 'CHECKBOX'; id: string; value?: string[] }
| { type: 'FILE'; id: string; value?: FileAttachment[] /* max 10 */ };
// value entry for a FILE customization
interface FileAttachment {
url: string;
filename: string;
size: number;
mimetype: string;
handle: string;
key: string;
uploaded_at: string | Date; // ISO string, or a Date (serialized to ISO)
}Notes:
CHECKBOXis a client-side convenience alias. It is normalized toOPTIONSbefore the request is sent, soCHECKBOXandOPTIONSare interchangeable on the wire.valueis optional forOPTIONS,CHECKBOX, andFILE: an untouched field can be submitted with no value, which the backend treats as an empty selection.FILEaccepts at most 10 attachments, and everyFileAttachmentfield is required.
Example:
const [cart, error] = await window.dmAPI.ecomm.addProduct({
product_id: 'prod_tshirt_001',
variation_id: 'var_tshirt_blue_m',
quantity: 1,
product_customizations: [
{ type: 'TEXT', id: 'engraving', value: 'Happy Birthday' },
{ type: 'BOOLEAN', id: 'gift_wrap', value: true },
{ type: 'OPTIONS', id: 'size', value: ['medium'] },
],
});AddItemRequestDto
AddItemRequestDtoA line item used to seed a cart via the items array of createCart / CreateCartPayload. It is a discriminated union on source; the base fields below are shared, and each source adds its own fields.
interface AddItemRequestDto {
source: 'STORE_CATALOG' | 'MEMBERSHIPS' | 'BOOKINGS';
plan_id?: string;
quantity?: number;
metadata?: object;
}Source-specific shapes:
// source: 'STORE_CATALOG' — a catalog product
interface AddProductRequestDto extends AddItemRequestDto {
source: 'STORE_CATALOG';
product_id: string;
variation_id: string;
product_customizations?: ProductCustomizationPayload[];
}
// source: 'MEMBERSHIPS' — a subscription plan
interface AddMembershipRequestDto extends AddItemRequestDto {
source: 'MEMBERSHIPS';
plan_id: string;
quantity: 1;
}
// source: 'BOOKINGS' — a booking slot
interface AddBookingRequestDto extends AddItemRequestDto {
source: 'BOOKINGS';
quantity: 1;
event_name: string;
event_type_id: number;
slot_start: string;
slot_duration?: number;
time_zone: string;
booking_fields_responses?: Record<string, unknown>;
}Example (prefill a cart with one catalog product):
const [cart, error] = await window.dmAPI.ecomm.createCart({
items: [
{
source: 'STORE_CATALOG',
product_id: 'prod_tshirt_001',
variation_id: 'var_tshirt_blue_m',
quantity: 2,
},
],
});CreateCartPayload
CreateCartPayloadThe options accepted by createCart, and reused as cart_options by the "buy now" methods (buyProduct, buyMembership, buyBooking) to prefill the cart that is auto-created for the purchase. Every field is optional.
interface CreateCartPayload {
email?: string;
shipping_address?: PersonalAddressRequestDto;
billing_address?: PersonalAddressRequestDto;
metadata?: object;
items?: AddItemRequestDto[];
timezone?: string; // defaults to the browser's timezone
}See the Prefilling the cart with cart_options example (under buyProduct).
CurrencyCustomFormat
CurrencyCustomFormatCustom money-formatting options passed to formatMoney via override.customFormat. When omitted, the store's configured format is used.
interface CurrencyCustomFormat {
groupSeparator: string; // thousands separator, e.g. ','
decimalSeparator: string; // decimal separator, e.g. '.'
keepTrailingZeroesDecimals: boolean; // keep trailing zeroes, e.g. '10.00'
symbol: string; // currency symbol, e.g. '$'
symbolPlacement: 'BEFORE_PRICE' | 'AFTER_PRICE';
}Example: formats 1999 as "$19.99".
window.dmAPI.ecomm.formatMoney({
amount: 1999,
override: {
customFormat: {
groupSeparator: ',',
decimalSeparator: '.',
keepTrailingZeroesDecimals: true,
symbol: '$',
symbolPlacement: 'BEFORE_PRICE',
},
},
});CheckoutCustomFieldZone
CheckoutCustomFieldZoneThe checkout zone a custom field belongs to, used by setCustomFields. It is a string union:
type CheckoutCustomFieldZone =
| 'CONTACT_INFO'
| 'SHIPPING_ADDRESS'
| 'SHIPPING_METHODS'
| 'BILLING_ADDRESS'
| 'PAYMENT_METHODS';TypeScript
The public API is typed. Use the exported type IDmAPIEcomm for the interface and type window.dmAPI.ecomm accordingly (e.g. via global augmentation or a local declaration).
Error handling
Most async methods do not throw. Each tuple-returning method one resolves to a [result, error] tuple, so you handle failures by inspecting the second element instead of using try/catch:
- Success:
[result, undefined]—resultis the response,errorisundefined. - Failure:
[undefined, error]—resultisundefined,erroris anEcomSDKError.
Always destructure the tuple and check error before using the result:
const [cart, error] = await window.dmAPI.ecomm.addProduct({
product_id: 'prod_tshirt_001',
variation_id: 'var_tshirt_001_blue_m',
quantity: 1,
});
if (error) {
// show a toast, fallback UI, or report to monitoring
console.error(error.code, error.message);
return;
}
// safe to use the result
console.log(cart);If you only care about whether the call failed, you can skip the result with a hole:
const [, error] = await window.dmAPI.ecomm.addDiscount({ code: 'SAVE20' });
if (error) {
// the code may be invalid or expired
}The error object
error is always an instance of EcomSDKError with:
code— a stable, localizable error code (e.g.sdk_errors.common.network_error).message— a human-readable description.
Common subclasses you may receive:
| Error | When it occurs |
|---|---|
NetworkSDKError | The request could not reach the server (connectivity, timeout). |
BadRequestSDKError | The server rejected the request (validation, invalid input). |
InvalidPayloadSDKError | The payload failed client-side validation before any request was sent (missing or malformed fields); code sdk_errors.common.invalid_payload_error. |
PaymentSDKError | A payment could not be processed. |
UnknownSDKError | Any other/unexpected error; the original cause is on .details. |
You can branch on the subclass or the code when you need specific handling:
import { NetworkSDKError } from '...'; // exported by the SDK package
const [orders, error] = await window.dmAPI.ecomm.getOrders({ limit: 5 });
if (error) {
if (error instanceof NetworkSDKError) {
// offer a retry
} else {
// generic failure handling
}
}This tuple-based handling applies to nearly every async method in the SDK (addProduct, buyProduct, getCart, getOrders, etc.). The synchronous helpers — openCart, closeCart, clearCart, formatMoney, and login — return their value directly, never produce a tuple, and do not throw.
Updated about 1 hour ago