> ## Documentation Index
> Fetch the complete documentation index at: https://docs.komerza.com/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript

> Type definitions for the Komerza client library

The script loads from a CDN, so its types come as a separate, types-only package:

```bash theme={null}
npm install --save-dev @komerza/types
```

Reference it once, from any `.d.ts` file in your project - `src/komerza.d.ts`, say:

```typescript theme={null}
/// <reference types="@komerza/types" />
```

If your `tsconfig.json` already has a `compilerOptions.types` list, add `"@komerza/types"` to it
instead.

`komerza` is then typed everywhere, `window.komerza` and `globalThis.komerza` included. Every type
lives in the `Komerza` namespace, so none of them collide with your own `Product` or `Order`:

```typescript theme={null}
const response = await komerza.getStore();
const products: Komerza.Product[] = response.data?.products ?? [];
```

The package is generated from the SDK's own source every time the SDK changes, so it always matches
the script the CDN serves.

## Without npm

Save this as `komerza.d.ts` anywhere your `tsconfig.json` includes. It's the package's file, unchanged:

```typescript komerza.d.ts theme={null}
// Types for the Komerza Storefront SDK - https://cdn.komerza.com/komerza.min.js
// Docs: https://docs.komerza.com/storefront/typescript
//
// Generated from the SDK's source by commerce-script/tools/build-types.ts.
// Don't edit by hand - change src/models.ts or src/main.ts and regenerate.
export {};

declare global {
  /** The Komerza Storefront SDK. */
  var komerza: Komerza.Client;

  namespace Komerza {
    /**
     * A single item in the shopping basket.
     */
    interface BasketItem {
      /**
       * The unique identifier of the product.
       */
      productId: string;

      /**
       * The unique identifier of the product variant. Every item added through the public API has one;
       * checkout refuses a line without it.
       */
      variantId: string;

      /**
       * How many of this product are in the basket.
       */
      quantity: number;
    }

    /**
     * Details about a field that failed validation.
     */
    interface InvalidField {
      /**
       * The name of the invalid field.
       */
      fieldName: string;

      /**
       * A human-readable reason why this field is considered invalid.
       */
      reason: string;
    }

    /**
     * Standard wrapper for API responses.
     *
     * @template T  The type of the 'data' payload.
     */
    interface ApiResponse<T> {
      /**
       * Indicates whether the request completed successfully.
       */
      success: boolean;

      /**
       * An optional human-readable message from the server.
       */
      message?: string;

      /**
       * A list of fields that failed validation, if any.
       */
      invalidFields?: InvalidField[];

      /**
       * The payload of the response, of generic type 'T'.
       */
      data?: T;

      /**
       * A machine-readable error code from the server when a request fails (e.g. "ValidationError",
       * "ProductUnavailable", "NotFound"). Absent on success and on errors raised inside the SDK.
       */
      code?: string;
    }

    /**
     * Standard wrapper for paginated API responses.
     */
    interface PaginatedApiResponse<T> extends ApiResponse<T[]> {
      /** The total number of pages available. */
      pages: number;
    }

    /**
     * Base entity with an ID and creation timestamp.
     */
    interface Entity {
      /**
       * Unique identifier for the entity.
       */
      id: string;

      /**
       * ISO 8601 timestamp when the entity was created.
       */
      dateCreated: string;
    }

    /**
     * An adaptive-currency quote: the shopper's local currency and the rate to reach it from the store's.
     *
     * Display-only. The store's currency stays the authoritative one for money movement — the rate that
     * actually charges a card is snapshotted onto the order when it is created, not taken from here.
     */
    interface PresentmentQuote {
      /** The shopper's currency code (lowercase, e.g. "eur"). */
      currencyCode: string;

      /** Multiply a store-currency amount by this to get the shopper's. Includes the merchant's FX margin. */
      rate: number;
    }

    /**
     * A store owned by a merchant that can be used as a front for selling products to customers.
     */
    interface Store extends Entity {
      /** The name of the store. */
      name: string;

      /** The description of the store, shown on their about page and on the home of their store page. */
      description: string;

      /**
       * The subdomain for the store, ONLY the subdomain, not the fully qualified domain.
       * @example "store"
       */
      url: string;

      /** The custom domain set for the store, or null if not set. */
      customDomain?: string;

      /** The store’s preferred currency code (ISO 4217). */
      currencyCode: string;

      /**
       * The shopper's adaptive-currency quote, when one applies to this request.
       *
       * Absent means every price renders in `currencyCode` — which is equally what happens when the store
       * has the feature off, when the shopper's browser already prefers the store's own currency, and when
       * no rate was available. All three are the same instruction as far as display goes.
       */
      presentment?: PresentmentQuote;

      /**
       * Whether the store has adaptive currency switched on at all.
       *
       * Distinct from `presentment` being set, and the distinction is load-bearing: this payload can arrive
       * server-rendered from a cache shared by every visitor, which therefore has to stay currency-agnostic.
       * An absent quote cannot tell the runtime whether no quote applied or whether it simply was not in the
       * shared copy — this can, so the runtime only spends a request resolving one when it would count.
       *
       * Three states, not two. `false` is the server saying the feature is off, and costs nothing. `undefined`
       * is an older payload that predates this field — which every already-rendered storefront is — and has to
       * be treated as unknown rather than off, or the feature never reaches those pages at all.
       */
      isAdaptiveCurrencyEnabled?: boolean;

      /**
       * The store's products: public ones and unlisted ones (`visibility` 1), plus any on hold (3). Private
       * products are never included. Filter to `visibility === 0` (public) before listing them.
       */
      products: ProductReference[];

      /** The list of categories configured for this store. */
      categories?: Category[];

      /** The domain the store is associated with. */
      domain: string;

      /** The reason the store is in maintenance; null if not in maintenance mode. */
      maintenanceReason?: string;

      /**
       * The average (median) review rating for the product based on past customer reviews.
       */
      rating: number;

      /**
       * The store branding options
       */
      branding?: Branding;

      /**
       * The list of discount dialogs configured for this store.
       */
      discountDialogs?: DiscountDialog[];

      /** Affiliate program options for the store. */
      affiliateOptions?: AffiliateOptions;

      /** Whether the ticketing (support) system is enabled. */
      isTicketingEnabled?: boolean;

      /** Whether the product name is prepended to the receipt. */
      prependProductNameToReceipt?: boolean;

      /** Whether automatic review collection is enabled. */
      isAutomaticReviewsEnabled?: boolean;

      /** Whether public store statistics are shown. */
      showPublicStats?: boolean;

      /** Whether recent order toast notifications are shown to visitors. */
      showRecentOrderToasts?: boolean;

      /** The merchant's terms of service URL, if they set one. */
      termsOfServiceUrl?: string;

      /** The merchant's refund policy URL, if they set one. */
      refundPolicyUrl?: string;

      /** The merchant's privacy policy URL, if they set one. */
      privacyPolicyUrl?: string;

      /** Where the merchant handles support, when it isn't Komerza's ticketing. */
      externalSupportUrl?: string;

      /** Text to show alongside `externalSupportUrl`. */
      externalSupportDescription?: string;

      /** Whether the customer download center is enabled. */
      isDownloadCenterEnabled?: boolean;

      /** Whether the store is accepting reseller applications. */
      isAcceptingResellerApplications?: boolean;

      /** The Google Search Console verification token, if the merchant set one. */
      googleSiteVerification?: string;
    }

    interface Branding {
      /**
       * The banner logo file name
       */
      bannerFileName?: string;
      /**
       * The icon file name
       */
      iconFileName?: string;
      /**
       * The accent color for the store (hex color code).
       * Used for buttons, links, and other interactive elements.
       * @example "#6366f1"
       */
      accentColor?: string;
    }

    /**
     * Affiliate program configuration for a store.
     */
    interface AffiliateOptions {
      /** Whether the affiliate program is enabled. */
      isEnabled: boolean;

      /** The default return percentage for affiliates. */
      defaultReturnPercentage: number;

      /** The default percentage discount given via affiliate links. */
      defaultPercentageOff: number;

      /** Whether affiliates can convert their balance. */
      canConvertAffiliateBalance: boolean;

      /** Whether public affiliate registration is enabled. */
      isPublicRegistrationEnabled: boolean;

      /** Whether affiliates can edit their links. */
      isLinkEditingEnabled: boolean;
    }

    /**
     * The trigger type for a discount dialog.
     * 0 Entrance, 1 ExitIntent.
     */
    type DiscountDialogTriggerType = 0 | 1;

    /**
     * A discount dialog that can be shown to users.
     */
    interface DiscountDialog extends Entity {
      /** The type of trigger that shows this dialog. */
      triggerType: DiscountDialogTriggerType;

      /** The title of the dialog. */
      title: string;

      /** The message displayed in the dialog. */
      message: string;

      /** The text displayed on the action button. */
      buttonText: string;

      /** Whether this dialog is currently enabled. */
      isEnabled: boolean;

      /** Delay in seconds before showing the dialog. */
      displayDelaySeconds: number;

      /** Whether to show the dialog only once per session. */
      showOncePerSession: boolean;

      /** Whether to show the dialog only once ever (persisted). */
      showOnceEver: boolean;

      /** The coupon code to display/copy. */
      couponCode: string;

      /** The ID of the associated coupon. */
      couponId: string;
    }

    /**
     * A category used to group products within a store.
     */
    interface Category extends Entity {
      /** The display name of the category. */
      name: string;

      /** The visibility level of the category. */
      visibility: Privacy;

      /** URL-friendly slug for the category, if one was set. */
      slug?: string;

      /** The image file name for the category. */
      imageName?: string;

      /** The display order of the category (ascending). */
      order: number;

      /** The ID of the store this category belongs to. */
      storeId: string;

      /** The number of products in this category. */
      productCount: number;

      /** The list of product IDs that belong to this category. */
      products: string[];
    }

    /**
     * A reference to a product, with limited details.
     */
    interface ProductReference extends Entity {
      /** The name of the product. */
      name: string;

      /** The description of the product; supports markdown. */
      description: string;

      /** The list of image names that will be displayed for the product. */
      imageNames: string[];

      /** The average (median) review rating for the product. */
      rating: number;

      /** The order number of the product group (ascending). */
      order: number;

      /**
       * The visibility level of the product. Sent for the products in `getStore()`; `getProduct()` does
       * not include it.
       */
      visibility?: Privacy;

      /** The store ID that owns this product. */
      storeId: string;

      /** URL-friendly slug for the product. */
      slug?: string;

      /** The list of variants associated with this product. */
      variants: Variant[];

      /** The merchant's display badges for this product, e.g. "New" or "Sale". */
      badges?: ProductBadge[];

      /** Whether this product is marked as a best seller. */
      isBestSeller?: boolean;

      /** When true the merchant has hidden stock levels; every variant then reports `stock: 0`. */
      hideStock?: boolean;

      /** Custom metadata the merchant attached to the product, if any. */
      metadata?: string;

      /** The affiliate discount mode for this product: 0 = store default, 1 = none, 2 = custom. */
      affiliateDiscountMode?: number;

      /** The custom affiliate discount percentage, when `affiliateDiscountMode` is 2. */
      customAffiliateDiscountPercentage?: number;
    }

    /**
     * A display badge on a product.
     */
    interface ProductBadge {
      /** The badge's unique identifier. */
      id: string;

      /** The product the badge belongs to. */
      productId: string;

      /** The badge text. */
      label: string;

      /** The badge color as a hex code, if the merchant picked one. */
      color?: string;

      /** The display order (ascending). */
      order: number;
    }

    /**
     * Represents a variant tied to a product.
     */
    interface Variant extends Entity {
      /** The name of the variant. */
      name: string;

      /** The ID of the product this variant belongs to. */
      productId: string;

      /** The ID of the store this variant belongs to. */
      storeId: string;

      /** The cost of the product variant in the store’s currency. */
      cost: number;

      /** The minimum quantity allowed for purchase (defaults to 1). */
      minimumQuantity: number;

      /**
       * The maximum quantity allowed for purchase (-1 equals infinity).
       */
      maximumQuantity: number;

      /** The ordering number of the product variant (defaults to 1). */
      order: number;

      /**
       * Whether purchasing this variant requires Discord authorization.
       */
      requireDiscordAuthorization: boolean;

      /** An optional description of this specific variant. */
      description?: string;

      /** The type of product being sold. */
      type: ProductType;

      /** How often a subscription variant bills. Absent on one-time variants. */
      billingInterval?: BillingInterval;

      /** The billing period in days, when `billingInterval` is `BillingInterval.Custom`. */
      customIntervalDays?: number;

      /**
       * The list of image names for this variant; inherits from product if empty.
       */
      imageNames: string[];

      /**
       * The available stock count for this variant. Always 0 when `hideStock` is true, so never read
       * 0 as "sold out" on its own — use `isOutOfStock`.
       */
      stock: number;

      /** The mode used to calculate stock levels. */
      stockMode: StockCalculationMode;

      /** Whether the merchant has hidden stock levels (inherited from the product). */
      hideStock: boolean;

      /**
       * Whether this variant is sold out. Computed by the server: never true when stock is hidden or
       * not tracked. This is the flag to disable "Add to cart" on.
       */
      isOutOfStock: boolean;

      /** Quantity-based price tiers. The highest tier the line's quantity reaches applies to every unit. */
      volumeDiscounts: VolumeDiscount[];

      /** When true, volume discounts stop applying once a coupon is used. */
      disableVolumeDiscountOnCoupon: boolean;

      /** Questions the hosted checkout asks the buyer for this variant. */
      customFields: CustomField[];

      /** How the variant is delivered: "license_keys", "dynamic", "instructions", "files", "custom_fields". */
      deliveryTypes: string[];

      /** Whether license keys are handed out in random order rather than first-in, first-out. */
      randomizeKeyDelivery: boolean;
    }

    /**
     * A quantity-based price tier on a variant.
     */
    interface VolumeDiscount {
      /** The tier's unique identifier. */
      id: string;

      /** The variant this tier belongs to. */
      variantId: string;

      /** The minimum quantity that unlocks this tier. */
      quantity: number;

      /** How `discountValue` is applied. */
      discountType: VolumeDiscountType;

      /**
       * Percentage: 10 means 10% off. FixedAmount: an amount off each unit, in the store's currency.
       * FixedPrice: the new unit price, in the store's currency.
       */
      discountValue: number;
    }

    /**
     * A question the hosted checkout asks the buyer.
     */
    interface CustomField {
      id: string;
      variantId: string;
      label: string;
      identifier: string;
      placeholder?: string;
      type: string;
      isRequired: boolean;
      hint?: string;
      options: CustomFieldOption[];
    }

    /**
     * One option of a select-style custom field.
     */
    interface CustomFieldOption {
      id: string;
      customFieldId: string;
      label: string;
      value: string;
      order: number;
    }

    /**
     * The stock calculation modes that are used.
     * 0 Calculated, 1 Ignored, 2 Fixed.
     */
    type StockCalculationMode = 0 | 1 | 2;

    /**
     * The type of product that is being sold.
     * 0 OneTime, 1 Subscription.
     */
    type ProductType = 0 | 1;

    /**
     * How often a subscription variant bills.
     * 0 Weekly, 1 Monthly, 2 Quarterly, 3 Yearly, 4 Custom.
     */
    type BillingInterval = 0 | 1 | 2 | 3 | 4;

    /**
     * How a volume discount tier's `discountValue` is applied.
     * 0 Percentage, 1 FixedAmount, 2 FixedPrice.
     */
    type VolumeDiscountType = 0 | 1 | 2;

    /**
     * List of product visibility (privacy) levels.
     * 0 Public, 1 Unlisted, 2 Private, 3 OnHold.
     */
    type Privacy = 0 | 1 | 2 | 3;

    /**
     * Details about a single line item within an order.
     */
    interface LineItem extends Entity {
      productName: string;
      productId: string;
      variantId: string;
      variantName: string;
      /** The unit price, in the order's currency. */
      amount: number;
      /** `amount` × `quantity`. */
      lineTotal: number;
      quantity: number;
      /** Set on subscription line items. */
      billingInterval?: BillingInterval;
      customIntervalDays?: number;
    }

    /** Order details associated with an order. */
    interface OrderDetails extends Entity {
      countryCode: string;
      emailAddress: string;
      customerId: string;
    }

    /** Gateway-specific metadata for the order. */
    interface GatewayMetadata extends Entity {
      transactionId?: string;
      clientSecret?: string;
      note?: string;
      cryptocurrencyAddress?: string;
      cryptocurrencyAmount?: number;
      cryptocurrencyExpiration?: string;
    }

    /** Coupon usage information on an order. */
    interface OrderCoupon extends Entity {
      percentageDecrease?: number;
      netDecrease: number;
      code: string;
      couponId: string;
    }

    /**
     * Public order returned by the API.
     */
    interface Order extends Entity {
      items: LineItem[];
      currencyCode: string;
      /** The shopper's currency, when adaptive currency priced the order in it. */
      presentmentCurrencyCode?: string;
      /** The rate from `currencyCode` to `presentmentCurrencyCode`, snapshotted when the order was created. */
      presentmentFxRate?: number;
      gateway: string;
      customer: OrderDetails;
      gatewayMetadata?: GatewayMetadata;
      status: string;
      amountPaid: number;
      amount: number;
      coupon?: OrderCoupon;
    }

    /**
     * What a `komerza.on("basketchange", …)` listener receives.
     */
    interface BasketChangeEvent {
      /** The basket as it is now. */
      items: BasketItem[];

      /** The total quantity across every item, the same number `getBasketItemCount()` returns. */
      itemCount: number;
    }

    /** Represents a single product review. */
    interface Review extends Entity {
      rating: number;
      reason: string;
      productId?: string;
      reply?: string;
    }

    /**
     * Anonymized public statistics for a store.
     * All counts are bucketed into broad ranges to protect exact figures.
     */
    interface PublicStoreStats {
      /** Approximate total completed orders (e.g. "100+", "10-50"). */
      totalOrders: string;

      /** Approximate total unique customers. */
      totalCustomers: string;

      /** Approximate number of listed products. */
      totalProducts: string;

      /** Average review rating out of 5, or null if no reviews. */
      averageRating: number | null;

      /** Approximate number of reviews. */
      totalReviews: string;

      /** How long the store has been active (e.g. "6 months", "2 years"). */
      storeAge: string;
    }

    /** What `getStore()` lists in `products` and what `getProduct()` returns. */
    type Product = ProductReference;

    /**
     * The `komerza` global: the Komerza Storefront SDK.
     */
    interface Client {
      /**
       * Initialize the commerce library for a specific store.
       * Must be called before any other methods that rely on a 'storeId'.
       * @param storeId Unique identifier of your store.
       */
      init(storeId: string): void;

      /**
       * Gets the URL to the stores logo
       */
      getStoreLogoUrl(): Promise<string>;

      /**
       * Gets the URL to the stores banner
       */
      getStoreBannerUrl(): Promise<string>;

      /**
       * Creates a number formatter for the current store's currency.
       */
      createFormatter(): Promise<Intl.NumberFormat>;

      /**
       * Retrieve the items currently in the basket.
       * @returns Array of BasketItem.
       */
      getBasket(): BasketItem[];

      /**
       * Get the current total item count in the basket.
       * @returns Number of items.
       */
      getBasketItemCount(): number;

      /**
       * Add a product to the basket.
       * @param productId Unique identifier of the product.
       * @param variantId Unique identifier of the variant
       * @param quantity Quantity to add (defaults to 1).
       */
      addToBasket(productId: string, variantId: string, quantity?: number): void;

      /**
       * Clear the basket and add a product to the basket.
       * @param productId Unique identifier of the product.
       * @param variantId Unique identifier of the variant
       * @param quantity Quantity to add (defaults to 1).
       */
      buyNow(productId: string, variantId: string, quantity?: number): void;

      /**
       * Remove a product from the basket.
       * @param productId Unique identifier of the product.
       * @param variantId Unique identifier of the variant
       */
      removeFromBasket(productId: string, variantId: string): void;

      /**
       * Set the quantity of an item already in the basket. A quantity of 0 or less removes it; an item
       * that isn't in the basket is left alone (use addToBasket for that).
       * @param productId Unique identifier of the product.
       * @param variantId Unique identifier of the variant
       * @param quantity The new quantity.
       */
      setBasketItemQuantity(
        productId: string,
        variantId: string,
        quantity: number,
      ): void;

      /**
       * Listen for basket changes, whether this page or another tab made them.
       * @param event The event to listen for. "basketchange" is the only one.
       * @param handler Called with the basket as it is after the change.
       * @returns A function that removes the listener.
       */
      on(
        event: "basketchange",
        handler: (event: BasketChangeEvent) => void,
      ): () => void;

      /**
       * Remove all items from the basket.
       */
      clearBasket(): void;

      /**
       * Get details of the currently initialized store (must be initialized).
       * @returns ApiResponse containing the Store.
       */
      getStore(): Promise<ApiResponse<Store>>;

      /**
       * Retrieve a product by ID or slug for the current store (must be initialized).
       * @param idOrSlug The product's unique identifier or slug.
       * @returns ApiResponse containing the product.
       */
      getProduct(idOrSlug: string): Promise<ApiResponse<ProductReference>>;

      /**
       * Retrieve reviews for a product.
       * @param productId The product's unique identifier (not its slug).
       * @param page The page number, starting at 1 (the default).
       * @returns ApiResponse containing an array of reviews.
       */
      getProductReviews(
        productId: string,
        page?: number,
      ): Promise<PaginatedApiResponse<Review>>;

      /**
       * Create an order from the current basket and redirect to the hosted checkout page.
       * @param emailAddress Email address for the order.
       * @param couponCode Optional coupon code to apply.
       * @returns ApiResponse containing the created order.
       */
      checkout(
        emailAddress: string,
        couponCode?: string,
      ): Promise<ApiResponse<Order>>;

      /**
       * Create a backup of the current basket state.
       * @returns True if backup was successful, false otherwise.
       */
      createBasketBackup(): boolean;

      /**
       * Restore basket from backup.
       * @returns True if restore was successful, false otherwise.
       */
      restoreBasketFromBackup(): boolean;

      /**
       * Validate the integrity of the basket data.
       * @returns True if basket integrity is valid, false otherwise.
       */
      validateBasketIntegrity(): boolean;

      /**
       * Merge items from another basket into the current basket and save the result. Where both
       * baskets hold the same item, the higher quantity wins.
       * @param otherBasket Array of BasketItem to merge.
       * @returns The basket after the merge.
       */
      mergeBaskets(otherBasket: BasketItem[]): BasketItem[];

      /**
       * Move an item from one product/variant to another.
       * @param fromProductId Product ID of the source item.
       * @param fromVariantId Variant ID of the source item.
       * @param toProductId Product ID of the destination.
       * @param toVariantId Variant ID of the destination.
       * @returns True if move was successful, false otherwise.
       */
      moveBasketItem(
        fromProductId: string,
        fromVariantId: string,
        toProductId: string,
        toVariantId: string,
      ): boolean;

      /**
       * Get comprehensive statistics about the basket.
       * @returns Detailed basket statistics.
       */
      getBasketStats(): {
        itemCount: number;
        totalQuantity: number;
        lastModified: number;
        version: number;
        hasBackup: boolean;
        integrityValid: boolean;
      };

      /**
       * Retrieve public store statistics (order counts, customer counts, etc.).
       * Returns empty data gracefully if stats are disabled.
       * @returns ApiResponse containing PublicStoreStats.
       */
      getPublicStoreStats(): Promise<ApiResponse<PublicStoreStats>>;
    }
  }
}
```

Numeric fields such as `visibility` and `stockMode` are unions of their values rather than TypeScript
enums: an enum needs an object at runtime, and the script doesn't provide one.

## Machine-readable schema

The same API is published as JSON Schema at
[docs.komerza.com/storefront/script.json](https://docs.komerza.com/storefront/script.json) -
every function with its parameters and return type, and every model. It's generated from the SDK's
source too, which makes it the thing to point code generators and AI tools at.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.