Skip to main content

Script Setup

<script
  src="https://hemsy.ai/hemsy-embed.js"
  data-hemsy-subdomain="your-store-subdomain"
  data-hemsy-selector=".hemsy-launcher"
  defer
></script>
Use a shared class selector like .hemsy-launcher when multiple elements should open Hemsy. Avoid reusing the same id on multiple elements. Any element matching data-hemsy-selector becomes a Hemsy launcher. The launch behavior is decided per element from attributes on that exact clicked element.

Required Attributes

  • data-hemsy-subdomain: Your Hemsy store subdomain. For mystore.hemsy.ai, use "mystore".
  • data-hemsy-selector: CSS selector for elements that should launch Hemsy.

Script Attributes Reference

AttributeRequiredExampleDescription
data-hemsy-subdomaintrue"mystore"Your Hemsy store subdomain.
data-hemsy-selectortrue".hemsy-launcher"CSS selector for elements that launch Hemsy.
data-hemsy-anonymous-idfalse"visitor-42"Anonymous visitor ID passed through to Hemsy for analytics and order metadata.
data-hemsy-modefalse"instant"Launch mode. Omit for the default interactive experience; set to "instant" for instant generation with a pre-selected model.
data-hemsy-actionfalse"cart"Controls the final CTA behavior: "checkout" (default) opens Shopify checkout; "cart" sends variant data back to your site. Applies to all modes.
Note: For event callbacks (onItemAdded, onItemRemoved, onProductVariantData), use window.HemsyEmbedConfig instead of data attributes. See the Event Callbacks section below.

Launcher Attributes

Attach these to any element matching data-hemsy-selector.
AttributeExampleDescription
data-hemsy-share"<shareId>"Opens a previously shared look by its short ID. Highest priority.
data-hemsy-auto-product-context"true"Auto-builds a closet payload from the current PDP variant. Falls back to scratch if no variant is detected.
data-hemsy-closet"eyJwcm9k..."Explicit URL-safe base64 closet payload. Use this for bundles or curated looks.
data-hemsy-flow"<flowId>"Adds a guided flow to the launch. Must be on the clicked launcher element.
data-hemsy-model"<modelId>"Model UUID for instant mode. Skips model selection and uses this model for generation. Copy the ID from the dashboard Models page.

Launch Resolution

Evaluated per clicked element, in this order:
  1. Instant (data-hemsy-mode="instant" on the script + data-hemsy-model on the element): instant generation with the specified model and closet payload.
  2. data-hemsy-share="<shortId>": share mode with that shared look.
  3. data-hemsy-auto-product-context="true": closet mode with a synthesized product payload, or scratch if no product variant is detected.
  4. data-hemsy-closet="<base64>": closet mode with that exact payload.
  5. Nothing else: scratch mode (open the full interactive experience).
data-hemsy-flow composes with any of the above. A flow-only launcher opens scratch mode with the flow applied. data-hemsy-action is orthogonal to launch resolution and controls only what the final CTA does (checkout or cart handoff).
Instant mode and share are not compatible on the same script tag. When data-hemsy-mode="instant" is set on the script, share launchers do not work — the share param is not forwarded to the iframe URL. If an element has both data-hemsy-model and data-hemsy-share, instant wins and share is ignored. Use default script mode for share buttons, or load separate script tags if you need both instant and share launchers on the same page.

Final Action

data-hemsy-action is set on the script tag and controls what happens when the customer taps the final result CTA. It applies to all modes (default and instant).
  • checkout (default): the CTA says “Go to Checkout” and Hemsy creates or opens a Shopify checkout/cart URL.
  • cart: the CTA says “Go to Cart”, Hemsy posts PRODUCT_VARIANT_DATA to the parent page, calls window.HemsyEmbedConfig.onProductVariantData(payload), and closes the modal.
Use cart when your storefront owns cart state and should add the returned variants itself.
<script
  src="https://hemsy.ai/hemsy-embed.js"
  data-hemsy-subdomain="your-store-subdomain"
  data-hemsy-selector=".hemsy-launcher"
  data-hemsy-action="cart"
  defer
></script>
data-hemsy-mode="cart" and data-hemsy-mode="checkout" are deprecated but still supported for backward compatibility. Prefer data-hemsy-action instead.

Instant Mode

Instant mode bypasses model selection and immediately triggers image generation with a specific model and product closet. The customer sees the generation loading state and then the result.

Setup

<script
  src="https://hemsy.ai/hemsy-embed.js"
  data-hemsy-subdomain="your-store-subdomain"
  data-hemsy-selector=".hemsy-launcher"
  data-hemsy-mode="instant"
  defer
></script>

<a
  href="#"
  class="hemsy-launcher"
  data-hemsy-model="YOUR_MODEL_ID"
  data-hemsy-closet="YOUR_URLSAFE_BASE64_CLOSET_PAYLOAD"
>
  See it on a model
</a>

How It Works

  1. Set data-hemsy-mode="instant" on the script tag.
  2. Optionally set data-hemsy-action="cart" to use cart mode instead of the default checkout.
  3. On each launcher element, set data-hemsy-model to the model UUID (copied from the dashboard Models page) and data-hemsy-closet to the product payload.
  4. When the customer clicks the launcher, Hemsy opens the modal, loads the model image and products, and immediately starts generating the try-on image.
  5. After generation, the customer sees the result and can proceed to checkout or cart.

Getting Model IDs

Model IDs are UUIDs assigned to each model image in your store’s model library. To find them:
  1. Go to your store’s Models page in the Hemsy dashboard.
  2. Hover over any model image.
  3. Click the copy icon in the bottom-left corner to copy the model ID to your clipboard.

Instant Mode with Cart Action

To combine instant mode with cart handoff (where your storefront manages cart state):
<script
  src="https://hemsy.ai/hemsy-embed.js"
  data-hemsy-subdomain="your-store-subdomain"
  data-hemsy-selector=".hemsy-launcher"
  data-hemsy-mode="instant"
  data-hemsy-action="cart"
  defer
></script>

Launcher Examples

Scratch Launcher

<a class="hemsy-launcher" href="#">Design a look</a>

PDP Launcher

<button class="hemsy-launcher" data-hemsy-auto-product-context="true">
  Try with this product
</button>

Flow Launcher

<button class="hemsy-launcher" data-hemsy-flow="YOUR_FLOW_ID">
  Build a bed
</button>

Closet Launcher

<button
  class="hemsy-launcher"
  data-hemsy-closet="YOUR_URLSAFE_BASE64_CLOSET_PAYLOAD"
>
  Try on Bed Bundle
</button>

Share Launcher

Open a previously shared look directly:
<button class="hemsy-launcher" data-hemsy-share="FreyjHz4">
  View Shared Look
</button>
This is useful for marketing campaigns, email links, or displaying shared looks on your storefront without requiring a page reload.
Do not use share launchers on a page where the embed script has data-hemsy-mode="instant". Share requires default script mode. See Launch Resolution for details.

Instant Launcher

Skip model selection and immediately generate with a specific model:
<a
  href="#"
  class="hemsy-launcher"
  data-hemsy-model="YOUR_MODEL_ID"
  data-hemsy-closet="YOUR_URLSAFE_BASE64_CLOSET_PAYLOAD"
>
  See it on a model
</a>

URL Parameter Auto-Open

The embed script automatically detects ?share= and ?closet= URL parameters and opens the modal on page load:
https://your-store.com/?share=FreyjHz4
https://your-store.com/?closet=eyJwcm9kdWN0cy...
This enables:
  • Share links: Customers can share their try-on results and recipients see the look immediately when they visit the link.
  • Marketing campaigns: Pre-load products via URL parameters in ads or emails.
The URL parameter is automatically removed after opening to prevent re-triggering on page refresh.
For SPAs using client-side routing, the embed script also intercepts history.pushState to detect URL changes after initial page load.

Closet Payload

FieldRequiredTypeNotes
productstrueArray<object>Must contain at least 1 product.
products[].variantIdfalsenumber | stringPreferred identifier and most reliable.
products[].productIdfalsenumber | stringFallback identifier when variantId is unavailable.
products[].attributesfalseArray<{ key: string; value: string }>Optional line-item attributes passed through to checkout/cart create.
Each product must include at least one identifier: variantId or productId.

Minimal Closet Payload Example

{
  "products": [
    {
      "variantId": 1234567890,
      "attributes": [{ "key": "_bundleTitle", "value": "Starter Bundle" }]
    }
  ]
}

Resolution Rules

  • Best: provide variantId for each line item.
  • If variantId is missing, Hemsy falls back to productId.
  • attributes are preserved and passed to checkout line items or cart-mode callbacks.

URL-Safe Base64 Encoding

Hemsy expects URL-safe base64:
  • Replace + with -
  • Replace / with _
  • Remove trailing =
function toUrlSafeBase64(obj) {
  return btoa(JSON.stringify(obj))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}
Always wrap data-hemsy-closet values in quotes. URL-safe base64 strings can contain = characters that will break unquoted HTML parsing.

Programmatic API

If you’d rather open Hemsy from JavaScript, use window.HemsyEmbed:
// Scratch
HemsyEmbed.open("scratch");

// Product context, auto-built from current page
HemsyEmbed.open({ payload: { autoProductContext: true } });

// Explicit closet payload
HemsyEmbed.open("closet", { closet: "YOUR_URLSAFE_BASE64_CLOSET_PAYLOAD" });

// Shared look by short ID
HemsyEmbed.open("share", { share: "FreyjHz4" });

// Into a flow
HemsyEmbed.open("scratch", { flow: "YOUR_FLOW_ID" });

// Instant mode with model and closet
HemsyEmbed.open("instant", {
  model: "YOUR_MODEL_ID",
  closet: "YOUR_URLSAFE_BASE64_CLOSET_PAYLOAD",
});

// Close
HemsyEmbed.close();
Set mode on the script tag or window.HemsyEmbedConfig; it applies to every launch from that embed script.

Syncing Cart State

Listen for events from the Hemsy embed to sync with your cart state:
window.HemsyEmbedConfig = {
  subdomain: "your-store-subdomain",
  selector: ".hemsy-launcher",
  action: "cart",
  onItemAdded: function (item) {
    // item.variantId - Shopify variant ID (string)
    // item.quantity - always 1
    console.log("Added to bag:", item.variantId);
  },
  onItemRemoved: function (item) {
    // item.variantId - Shopify variant ID (string)
    console.log("Removed from bag:", item.variantId);
  },
  onProductVariantData: function (payload) {
    // Fired in cart action when the final "Go to Cart" button is clicked.
    // payload.items - Array<{ variantId: string, quantity: number, attributes?: Array<{ key: string, value: string }> }>
    console.log("Send these variants to your cart:", payload.items);
  },
};

Available Event Callbacks

CallbackPayloadDescription
onItemAdded{ variantId: string, quantity: number }Fired when item added to bag
onItemRemoved{ variantId: string }Fired when item removed from bag
onProductVariantData{ items: Array<{ variantId: string, quantity: number, attributes?: Array<{ key: string, value: string }> }> }Fired when action: "cart" and final CTA is clicked

Hydrogen Example

import { useCart } from "@shopify/hydrogen";

function HemsyEmbedSetup() {
  const { linesAdd, linesRemove, lines, cartOpen } = useCart();

  useEffect(() => {
    window.HemsyEmbedConfig = {
      subdomain: "your-store",
      selector: ".hemsy-launcher",
      action: "cart",
      onItemAdded: ({ variantId, quantity }) => {
        linesAdd([
          {
            merchandiseId: `gid://shopify/ProductVariant/${variantId}`,
            quantity,
          },
        ]);
      },
      onItemRemoved: ({ variantId }) => {
        // Find and remove the line item
        const lineToRemove = lines?.find(
          (line) =>
            line.merchandise.id === `gid://shopify/ProductVariant/${variantId}`,
        );
        if (lineToRemove) {
          linesRemove([lineToRemove.id]);
        }
      },
      onProductVariantData: () => {
        cartOpen();
      },
    };
  }, [linesAdd, linesRemove, lines, cartOpen]);

  return null;
}
Event callbacks fire immediately when items are added or removed from the bag inside the Hemsy embed. The variantId is the numeric Shopify variant ID as a string, which you’ll need to convert to a GID format for Hydrogen’s cart operations. With action: "cart", onProductVariantData fires when the final “Go to Cart” button is clicked.

End-to-End Example

<script>
  window.HemsyEmbedConfig = {
    subdomain: "your-store-subdomain",
    selector: ".hemsy-launcher",
    action: "cart",
    onProductVariantData: function ({ items }) {
      console.log("Add these items to your storefront cart:", items);
    },
  };
</script>

<script
  src="https://hemsy.ai/hemsy-embed.js"
  data-hemsy-subdomain="your-store-subdomain"
  data-hemsy-selector=".hemsy-launcher"
  data-hemsy-anonymous-id="visitor-42"
  data-hemsy-action="cart"
  defer
></script>

<!-- Nav: design anything from scratch -->
<a class="hemsy-launcher" href="#"> Design a look </a>

<!-- PDP: try with the product currently on screen -->
<button class="hemsy-launcher" data-hemsy-auto-product-context="true">
  Try with this product
</button>

<!-- Bundle: pre-loaded closet -->
<button
  class="hemsy-launcher"
  data-hemsy-closet="eyJwcm9kdWN0cyI6W3sidmFyaWFudElkIjoxMjM0NTY3ODkwLCJhdHRyaWJ1dGVzIjpbeyJrZXkiOiJfYnVuZGxlVGl0bGUiLCJ2YWx1ZSI6Ik15IEJ1bmRsZSJ9XX1dfQ"
>
  Try on Bundle
</button>

<!-- Flow: guided build -->
<button class="hemsy-launcher" data-hemsy-flow="YOUR_FLOW_ID">
  Build a bed
</button>

<!-- Share: open a previously shared look -->
<button class="hemsy-launcher" data-hemsy-share="FreyjHz4">
  View Shared Look
</button>

End-to-End Example (Instant Mode)

<script>
  window.HemsyEmbedConfig = {
    subdomain: "your-store-subdomain",
    selector: ".hemsy-launcher",
    mode: "instant",
    action: "cart",
    onProductVariantData: function ({ items }) {
      console.log("Add these items to your storefront cart:", items);
    },
  };
</script>

<script
  src="https://hemsy.ai/hemsy-embed.js"
  data-hemsy-subdomain="your-store-subdomain"
  data-hemsy-selector=".hemsy-launcher"
  data-hemsy-mode="instant"
  data-hemsy-action="cart"
  defer
></script>

<!-- Instant: skip model selection, generate immediately -->
<a
  href="#"
  class="hemsy-launcher"
  data-hemsy-model="YOUR_MODEL_ID"
  data-hemsy-closet="YOUR_URLSAFE_BASE64_CLOSET_PAYLOAD"
>
  See it on a model
</a>

Common Mistake

Do not use data-hemsy-trigger:
<a href="https://your-store.hemsy.ai/" data-hemsy-trigger="closet">
  Design Your Bed
</a>
data-hemsy-trigger is not a supported launcher configuration. Specify data-hemsy-selector on the script tag, then make the clickable element match that selector. Use this instead for a flow:
<script
  src="https://hemsy.ai/hemsy-embed.js"
  data-hemsy-subdomain="your-store-subdomain"
  data-hemsy-selector=".hemsy-launcher"
  defer
></script>

<a href="#" class="hemsy-launcher" data-hemsy-flow="YOUR_FLOW_ID">
  Design Your Bed
</a>
Or this for an explicit closet:
<a
  href="#"
  class="hemsy-launcher"
  data-hemsy-closet="YOUR_URLSAFE_BASE64_CLOSET_PAYLOAD"
>
  Design Your Bed
</a>
Or this for a shared look:
<a href="#" class="hemsy-launcher" data-hemsy-share="FreyjHz4">
  View Shared Look
</a>