Skip to content

SDK API

The SDK exposes a single THGVTO class plus a few static DOM helpers. Everything else is a product handle — you create one per product and call generate() or getImage() on it.

The script tag reads data-api-url and data-api-key as defaults, so you can configure once and not repeat it in JavaScript:

<script
src="http://static.thcdn.com/vto-sdk.js"
data-api-url="https://api-nonprod.thg.dev/agentic-commerce/v1/vto"
data-api-key="your-api-key"
></script>

You can also import the bundle as an ES module and pass apiUrl / apiKey directly to the constructor.

At minimum:

const vto = new THGVTO();

With a theme:

const vto = new THGVTO({
theme: { primaryColor: '#003942', primaryTextColor: '#fff' },
});

All options are optional — defaults match the “invisible on any storefront” aesthetic. Common ones:

| Option | Default | What it does | |--------|---------|--------------| | theme | — | Modal CSS tokens (primaryColor, buttonRadius, fontFamily, …) | | content | — | Overrides for modal copy (modalTitle, uploadHint, disclaimerText, …) | | watermark | off | Brand watermark on downloaded/shared images | | consent | both true | GDPR consent for photo processing and storage | | showAddToCart / showWishlist | false | Actions on the result view | | modelsEnabled | false | Offer predefined models alongside user upload | | qrEnabled | false | QR code for mobile upload from desktop |

See the Configuration Reference for the full list and TypeScript types.

vto.getProduct() returns a THGVTOProduct handle. Handles are cached by product image URL, so calling getProduct() repeatedly with the same image returns the same instance.

const product = vto.getProduct({
image: 'https://cdn.example.com/products/shirt.jpg', // required
url: window.location.href, // required
title: 'Blue Oxford Shirt',
price: '£35.00',
id: 'SKU-12345',
});

Opens the modal, walks the user through upload/generation, resolves with the result URL — or null if the user closed the modal.

const imageUrl = await product.generate();
if (imageUrl) {
// inject imageUrl into your gallery
}

Pass { mode: 'direct' } to skip the modal when the user already has a photo uploaded. Falls back to the modal if no photo exists or direct generation fails.

await product.generate({ mode: 'direct' });

Returns a cached try-on URL for this product (keyed on the uploaded person photo), or null if nothing is cached. Use this on page load to show a result before the user even clicks.

const cached = await product.getImage();
if (cached) injectResult(cached);

The SDK ships four static helpers on THGVTO that make it easy to inject results into any storefront’s gallery without touching the SDK’s internals.

THGVTO.clone(templateEl, imageUrl, badgeLabel?, productImage?)

Section titled “THGVTO.clone(templateEl, imageUrl, badgeLabel?, productImage?)”

Clones a gallery slide (the first child of your carousel, say), swaps every <img> src to the try-on URL, strips buttons, and stamps a badge. Returns the clone — you decide where to insert it.

const carousel = document.getElementById('product-gallery');
const slide = THGVTO.clone(
carousel.firstElementChild,
imageUrl,
'TRY-ON',
product.image,
);
carousel.insertBefore(slide, carousel.firstElementChild);

The cloned element gets data-vto-result (and data-vto-product-image when you pass productImage) so update() and clear() can find it later.

Updates the src of every <img> inside existing result elements. Pass productImage to scope to a single product. Returns true if any elements were found.

if (!THGVTO.update(newImageUrl, product.image)) {
// no existing result — inject a new one with clone()
}

Removes result elements from the DOM. Pass productImage to scope to a single product.

// User changed variant — drop the old try-on
window.addEventListener('variant-change', () => {
THGVTO.clear();
});

Debounced MutationObserver — useful when your gallery re-renders on variant changes and you need to re-inject the result after the DOM settles.

const galleryRoot = document.getElementById('pdp-gallery-container');
const stop = THGVTO.watch(galleryRoot, () => {
if (document.querySelector('[data-vto-result]')) return;
updateTryOnImage();
});
// stop() to disconnect

Update consent state at runtime (e.g. when a cookie banner is accepted):

vto.updateConsent({ photoProcessing: true, photoStorage: true });

See the GDPR Consent guide for details.

const vto = new THGVTO({
theme: { primaryColor: '#003942', primaryTextColor: '#FDFCFB' },
});
function getCurrentProduct() {
const v = window.__CURRENTVARIANT__;
return vto.getProduct({
title: v.title,
image: v.images[0].original,
url: window.location.href,
id: v.sku,
});
}
function injectResult(imageUrl) {
const productImage = getCurrentProduct().image;
if (THGVTO.update(imageUrl, productImage)) return;
const carousel = document.getElementById('gallery-carousel');
const slide = THGVTO.clone(carousel.firstElementChild, imageUrl, 'TRY-ON', productImage);
carousel.insertBefore(slide, carousel.firstElementChild);
}
let product = getCurrentProduct();
// Show cached result on load
product.getImage().then(img => { if (img) injectResult(img); });
// Variant changes — drop old result, re-bind product
window.addEventListener('variant-change', () => {
THGVTO.clear();
product = getCurrentProduct();
});
// Re-inject after gallery re-renders
const galleryRoot = document.getElementById('pdp-gallery-container');
THGVTO.watch(galleryRoot, () => {
if (document.querySelector('[data-vto-result]')) return;
product = getCurrentProduct();
product.getImage().then(img => { if (img) injectResult(img); });
});
// Trigger button
document.getElementById('vto-trigger').addEventListener('click', async () => {
const result = await product.generate();
if (result) injectResult(result);
});