Developer Documentation

PrimeStyle VTO API

Add AI-powered virtual try-on to any e-commerce experience. One API call turns a product photo into a try-on result.

Introduction

PrimeStyle gives shoppers photorealistic virtual try-on and AI-powered size recommendations on any product page. Try-on results are generated in 15-20 seconds from a single customer photo plus a garment image. Sizing uses a small user profile (height, weight, and a few body-shape answers) to recommend a size for your product.

There are three ways to integrate, in order of how much work you want to do:

Drop-in React component

<PrimeStyleTryon /> renders a launch button and a modal that handles photo upload, sizing, and try-on end-to-end. Fastest path to ship.

Headless sizing hook

usePrimeStyleSize() and recommendForProduct() return a recommended size for your own UI. No modal.

Raw HTTP API

Call POST /api/v1/tryon and /api/v1/sizing/* from any server or client. Full control over UI and flow.

Profiles live in localStorage on the user's browser. PrimeStyle does not store user profiles or measurements on our servers, and there is no backend account system for end users. You do not need a database to use any of this.

End-to-end flow

1User fills out a short profile or uploads a body photo
2SDK picks a size and (optionally) generates a try-on image
3Result is cached in localStorage for that profile + product

Quick Start

Get a working virtual try-on button on your product page in under five minutes.

1. Install the SDK

The package ships both the React component and the plain-JS web component.

npm install @primestyleai/tryon

2. Get an API key

Create a key in the developer dashboard. Every authenticated request needs Authorization: Bearer <apiKey>. The React component reads the key from the API URL it calls; keep the raw key out of your client bundle and proxy through your own server when possible.

3. Drop the component on a product page

Pass at minimum a productImage. Everything else is optional.

ProductPage.tsx
import { PrimeStyleTryon } from "@primestyleai/tryon/react";
export default function ProductPage() {
return (
<PrimeStyleTryon
productImage="https://cdn.example.com/products/shirt-front.jpg"
productTitle="Linen Oxford Shirt"
productId="sku-12345"
apiUrl="https://myaifitting.com"
onComplete={(result) => {
console.log("Try-on ready:", result.imageUrl);
}}
onError={(err) => {
console.error("Try-on failed:", err.message);
}}
/>
);
}

4. What the user sees

  1. A "Try it on" button renders next to your product.
  2. Clicking opens a modal that walks the user through profile setup or photo upload.
  3. The SDK requests a size recommendation and a try-on image from the API.
  4. Within ~20 seconds the modal shows the try-on photo and recommended size.

Authentication

Every authenticated endpoint requires a PrimeStyle API key. Keys are issued and revoked from the developer dashboard; they carry your try-on token balance and your rate limit.

API Keys

Create a key from your dashboard. Treat keys like any other secret — do not commit them and rotate them periodically.

Passing the key

  • HTTP API: send the header Authorization: Bearer <apiKey> on every request.
  • SSE stream: you may pass the key either in the Authorization header or as ?key= on the URL (EventSource does not support custom headers in the browser).
  • SDK: the React component calls an apiUrl you control. If you do not pass one, the SDK reads NEXT_PUBLIC_PRIMESTYLE_API_URL at build time. Proxy the real key through your backend so it never ships to the browser.
Authenticated request
curl -X POST https://myaifitting.com/api/v1/tryon \
-H "Authorization: Bearer psk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"modelImage": "https://example.com/user.jpg",
"garmentImage": "https://example.com/shirt.jpg"
}'
Authenticated SSE
const es = new EventSource(
"https://myaifitting.com/api/v1/tryon/stream?key=psk_live_your_key_here"
);
es.addEventListener("completed", (e) => {
const payload = JSON.parse(e.data);
console.log("image ready:", payload.imageUrl);
});

Try-On Balance & Rate Limits

Each successful try-on deducts one token from your account. Sizing endpoints are currently free and metered only by rate limits. Every POST /api/v1/tryon response includes tryOnsUsed and newBalance so your dashboard can show the running total.

402 Insufficient tokens

If your balance is 0 we return HTTP 402 with a body of { error, currentBalance, required }. Top up from the billing page or fall back to showing the size recommendation without the visual try-on.

401 Unauthorized

Returned when the key is missing, malformed, expired, or revoked. Rotate and try again.

API Reference

The PrimeStyle HTTP API lives at https://myaifitting.com. All authenticated endpoints accept JSON and require a bearer token.

There is no HTTP surface for managing user profiles — profiles are kept in localStorage by the SDK. Your backend never needs to store them.

Create a Try-On Job

POST/api/v1/tryonAuth

Submit a new try-on job. Returns immediately with a galleryId you can stream or poll. One try-on token is deducted per successful job (failed jobs are refunded).

Request Body

modelImagestring (URL or data URI)required

Image of the person. Public HTTPS URL, signed URL, or base64 data URI. JPEG, PNG or WebP up to 50MB.

garmentImagestring (URL or data URI)required

Image of the product. A clean product shot works best.

fitInfoFitAreaInfo[]

Optional per-area fit hints. Each entry has { area, fit, userValue?, garmentRange? } where fit is one of good, tight, loose, a-bit-tight, a-bit-loose, too-tight, too-loose. When any fit is imperfect the backend does a two-pass render to adjust that area. Not stored server-side.

Example Request

curl -X POST https://myaifitting.com/api/v1/tryon \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"modelImage": "https://cdn.example.com/user.jpg",
"garmentImage": "https://cdn.example.com/shirt.jpg",
"fitInfo": [
{ "area": "Chest", "fit": "a-bit-tight", "userValue": 102, "garmentRange": "96-100" }
]
}'

Response 202

json
{
"galleryId": "65c8d1a4e2b4f5d3c8a91234",
"status": "processing",
"message": "VTO job started. Poll /api/v1/tryon/status/:galleryId to check progress",
"tokensDeducted": 1,
"newBalance": 958
}

Poll Try-On Status

GET/api/v1/tryon/status/:galleryIdAuth

Returns the current state of a try-on job. Poll every ~3 seconds as a fallback to the SSE stream. Note: the URL param is the galleryId returned from POST /api/v1/tryon — some SDK code still calls this value 'jobId' for backwards compatibility.

Path Parameters

galleryIdstring (24-char MongoDB ObjectId)required

The galleryId returned from POST /api/v1/tryon. Example: 65c8d1a4e2b4f5d3c8a91234.

Example Request

curl https://myaifitting.com/api/v1/tryon/status/65c8d1a4e2b4f5d3c8a91234 \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY"

Response 200

json
{
"galleryId": "65c8d1a4e2b4f5d3c8a91234",
"status": "completed",
"imageUrl": "https://res.cloudinary.com/.../result.jpg",
"message": "Try-on ready"
}

status is one of processing, completed, or failed. The imageUrl is populated only when the job completes successfully.

Server-Sent Events Stream

GET/api/v1/tryon/streamAuth

Subscribe to real-time job events. You receive 'completed' and 'failed' events for any job submitted by your API key while the connection is open. Use this instead of polling when you can keep a connection open.

Query Parameters

keystring

API key, passed on the URL because browser EventSource does not support custom headers. You may use the Authorization header from server-side clients instead.

Example Request

curl -N "https://myaifitting.com/api/v1/tryon/stream?key=$PRIMESTYLE_API_KEY"

Uploading Images

The try-on endpoint does not require a separate upload step. Pass images either as a public URL that PrimeStyle can fetch, or as a data:image/jpeg;base64,… URI. The SDK helpers compressImage and isValidImageFile can prepare a user file before posting.

  • Accepted formats: JPEG, PNG, WebP.
  • Max payload: 50MB per image.
  • Input images (your photo + the garment) are forwarded to the rendering backend in memory for the duration of the request.
  • The generated result is delivered back to your application; we do not maintain a long-term copy on your behalf.

Recommend a Size

POST/api/v1/sizing/recommendAuth

Given a user profile and a product (optionally with its size guide), returns a recommended size, a confidence level, and human-readable reasoning.

Request Body

method"exact" | "quick" | "photo"required

How the user's measurements were obtained. 'exact' means the user entered them directly, 'quick' means they came from height + weight + body-shape answers, 'photo' means they were derived from BlazePose landmarks on a body photo.

product{ title, description?, variants? }required

Product context used to match the right size chart. Title is required; description and variants improve accuracy.

measurementsobject

Keyed by body part. Common keys: gender, heightCm, chest, bust, waist, hips, shoulderWidth, sleeveLength, inseam, neckCircumference. For footwear: shoeEU, shoeUS, shoeUK. Required when method is 'exact' or 'photo'. Extra keys are accepted and forwarded.

quickEstimateobject

Height/weight/gender/age/body-shape inputs used when method is 'quick'.

sizeGuideobject

A normalized size guide, typically the response from /api/v1/sizing/sizeguide.

bodyImagestring

Body photo (URL or data URI) used when method is 'photo'.

bodyLandmarksobject

MediaPipe BlazePose landmarks extracted in the browser.

sizingUnit"cm" | "in"

Preferred unit in the response. Defaults to cm.

localestring

BCP-47 locale used to generate the reasoning text.

Example Request

curl -X POST https://myaifitting.com/api/v1/sizing/recommend \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"method": "exact",
"sizingUnit": "cm",
"product": {
"title": "Linen Oxford Shirt",
"description": "Regular fit, 100% linen."
},
"measurements": {
"chest": 102,
"waist": 86,
"height": 180,
"weight": 78
}
}'

Response 200

json
{
"recommendedSize": "M",
"tightSize": "S",
"looseSize": "L",
"confidence": "high",
"reasoning": "Chest 102cm falls in the middle of the M band (100-104cm).",
"internationalSizes": { "US": "M", "EU": "50", "UK": "M" },
"unit": "cm"
}

Response Fields

recommendedSizestringrequired

The size we recommend for this user + product.

tightSizestring

The next size down, if a tighter fit is possible.

looseSizestring

The next size up.

recommendedLengthstring

For multi-length products (e.g. Short / Regular / Long inseam).

confidence"high" | "medium" | "low"required

How certain the model is in the recommendation.

reasoningstringrequired

Short explanation suitable to show to the end user.

internationalSizesRecord<string, string>

Localized size labels (US, EU, UK, JP, etc.).

sectionsobject[]

Per-section recommendations for products with multiple sizing sections (e.g. top + bottom).

unit"cm" | "in"

Unit echoed from the request.

Estimate Body Measurements

POST/api/v1/sizing/estimateAuth

Estimate body measurements from either a short text profile (height, weight, gender, body-shape answers) or a body photo with MediaPipe BlazePose landmarks.

Request Body

snapModeboolean

Switches between two request shapes: normal (text profile) or snap (photo-only). When true, only bodyImage and requiredFields are used; gender/demographics are inferred from the photo.

bodyImagestring

Body photo as URL or data URI. Required when snapMode is true; optional in normal mode to enhance accuracy.

bodyLandmarksobject

MediaPipe BlazePose landmarks extracted in the browser. Improves accuracy when bodyImage is used.

requiredFieldsstring[]required

1–20 measurement names you want back (e.g. ['chest', 'waist', 'hips']). Required in normal mode; optional in snap mode.

heightnumber

Height in heightUnit. 20–300. Used in normal mode.

weightnumber

Weight in weightUnit. 20–700. Used in normal mode.

heightCmnumber

Alternative to height+heightUnit — explicit centimeters, 36–250.

weightKgnumber

Alternative to weight+weightUnit — explicit kilograms, 30–660.

heightUnit"cm" | "in" | "ft"

Unit of the supplied height. Default 'cm'. 'ft' is a legacy alias for 'in'.

weightUnit"kg" | "lbs"

Unit of the supplied weight. Default 'kg'.

gender"male" | "female" | "unisex"required

Required in normal mode. Inferred from the photo in snap mode.

agenumber

13–100. Improves accuracy slightly.

bodyType"slim" | "athletic" | "average" | "stocky" | "plus"

Self-assessed overall build.

chestProfile"narrow" | "average" | "broad"

Shape hint for the chest/bust.

midsectionProfile"flat" | "average" | "round"

Shape hint for the midsection.

hipProfile"narrow" | "average" | "full"

Shape hint for the hips.

unit"cm" | "in"

Preferred unit for the returned estimates.

Example Request

# Normal mode — estimate from height/weight/gender + optional shape answers
curl -X POST https://myaifitting.com/api/v1/sizing/estimate \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"height": 180,
"weight": 78,
"heightUnit": "cm",
"weightUnit": "kg",
"gender": "male",
"age": 32,
"bodyType": "average",
"chestProfile": "average",
"midsectionProfile": "flat",
"hipProfile": "average",
"requiredFields": ["chest", "waist", "hips", "shoulderWidth"]
}'
# Snap mode — estimate from a body photo alone
curl -X POST https://myaifitting.com/api/v1/sizing/estimate \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"snapMode": true,
"bodyImage": "https://cdn.example.com/body.jpg",
"requiredFields": ["chest", "waist", "hips"]
}'

Response 200

json
{
"estimates": {
"chest": 102,
"waist": 86,
"hips": 98,
"shoulderWidth": 46
},
"method": "llm",
"unit": "cm",
"detectedGender": "male"
}

Parse a Size Guide

POST/api/v1/sizing/sizeguideAuth

Normalize a raw size guide (HTML, JSON, or chart images) into a structured table of sizes that /api/v1/sizing/recommend can consume. Useful once per product page load; cache the result alongside your product.

Request Body

product{ title?, description?, category?, subcategory?, gender? }

Product context used to disambiguate the chart. Category/subcategory/gender help pick the right chart when the merchant serves multiple.

sizeGuideDataunknown

A pre-structured guide (headers + rows, or your own shape). Skips parsing on the backend.

sizeGuideRawstring

Raw HTML or plain text (for instance the innerHTML of a size-guide panel). The backend will normalize it with an LLM.

sizeChartImagesstring[]

Array of image URLs of size charts. Used when the merchant only has screenshots or PDFs — the backend uses vision to extract the table.

Example Request

# 1. Raw HTML — backend parses it with an LLM
curl -X POST https://myaifitting.com/api/v1/sizing/sizeguide \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product": { "title": "Linen Oxford Shirt" },
"sizeGuideRaw": "<table>...</table>"
}'
# 2. Pre-structured JSON — no parsing needed
curl -X POST https://myaifitting.com/api/v1/sizing/sizeguide \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product": { "title": "Linen Oxford Shirt" },
"sizeGuideData": {
"headers": ["Size", "Chest (cm)", "Waist (cm)"],
"rows": [["S","92-96","76-80"], ["M","100-104","84-88"]]
}
}'
# 3. Image URLs only — backend uses vision to extract the table
curl -X POST https://myaifitting.com/api/v1/sizing/sizeguide \
-H "Authorization: Bearer $PRIMESTYLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product": { "title": "Linen Oxford Shirt" },
"sizeChartImages": [
"https://cdn.example.com/size-chart-men.jpg"
]
}'

Response 200

json
{
"found": true,
"title": "Men's Shirt Size Guide",
"headers": ["Size", "Chest (cm)", "Waist (cm)"],
"rows": [
["S", "92-96", "76-80"],
["M", "100-104","84-88"],
["L", "108-112","92-96"]
],
"sections": []
}

Error Codes

HTTPMeaning
400Malformed JSON, missing required fields, or an image the renderer could not decode.
401Missing, malformed, expired, or revoked API key.
402Try-on balance is 0. Body includes currentBalance and required.
404Gallery id not found (for /status/:galleryId).
422Input passed validation but cannot be sized (e.g. no pose detected in the photo).
429Rate limit exceeded. Retry with exponential backoff.
500Unexpected server error. Failed jobs are automatically refunded.

Guest Try-On (no auth)

For marketing demos and landing pages, a public endpoint accepts one free try-on per IP address without an API key. It is otherwise identical to the authenticated version. There is a matching GET /api/public/tryon/status/:jobId.

POST/api/public/tryon

Submit one free guest try-on. Rate-limited per IP. Not intended for production integrations.

Example Request

curl -X POST https://myaifitting.com/api/public/tryon \
-H "Content-Type: application/json" \
-d '{
"modelImage": "https://cdn.example.com/user.jpg",
"garmentImage": "https://cdn.example.com/shirt.jpg"
}'

SDK

The @primestyleai/tryon package exposes four surfaces: a React component, a set of headless functions and a React hook for sizing, a browser web component, and a handful of localStorage helpers for managing user profiles client-side.

Installation

npm install @primestyleai/tryon

The package has no peer dependencies beyond React 18+ (React 19 supported). The web component is registered automatically by importing the top-level package (import "@primestyleai/tryon").

React Component: <PrimeStyleTryon />

The React component renders a launch button and, when clicked, a full-screen modal that walks the user through profile creation, sizing, and try-on. A single prop — productImage — is enough to ship.

typescript
import { PrimeStyleTryon } from "@primestyleai/tryon/react";

Props

productImagestringrequired

Public URL of the product image to try on.

productTitlestring

Shown in the modal header and used to improve sizing accuracy.

productIdstring

Your SKU. Used as the cache key for size recommendations and try-on results per profile.

buttonTextstring

Button label. Defaults to the localized 'Try it on'.

apiUrlstring

PrimeStyle API base URL. Defaults to NEXT_PUBLIC_PRIMESTYLE_API_URL or https://myaifitting.com.

showPoweredByboolean

Show the 'Powered by PrimeStyle' badge in the modal footer.

showIconboolean

Show the sparkle icon on the button.

buttonIconReact.ReactNode

Custom icon replacing the default sparkle.

localestring

BCP-47 locale code (e.g. 'en', 'fr', 'es-MX'). See the i18n section.

buttonStylesButtonStyles

Override colors, padding, border, radius, font, and hover state.

modalStylesModalStyles

Override modal colors, shadow, and accent.

classNamesPrimeStyleClassNames

Map of className overrides for internal modal slots.

classNamestring

Class applied to the root launch button.

styleCSSProperties

Inline styles on the launch button.

sizeGuideDataunknown

Pre-parsed size guide to skip the /sizing/sizeguide call. See 'Using your own size guide' in Guides.

onOpen() => void

Fires when the modal opens.

onClose() => void

Fires when the modal closes.

onUpload(file: File) => void

Fires when the user selects a photo.

onProcessing(jobId: string) => void

Fires once we have a jobId from the API.

onComplete(r: { jobId, imageUrl }) => void

Fires when the try-on image is ready.

onError(e: { message, code? }) => void

Fires on any error surfaced to the user.

Full example
import { PrimeStyleTryon } from "@primestyleai/tryon/react";
export function ProductTryOnButton({ product }) {
return (
<PrimeStyleTryon
productImage={product.image}
productTitle={product.title}
productId={product.sku}
buttonText="See it on you"
apiUrl={process.env.NEXT_PUBLIC_PRIMESTYLE_API_URL}
locale="en"
showPoweredBy
onOpen={() => track("tryon_open", { sku: product.sku })}
onUpload={(file) => track("tryon_upload", { size: file.size })}
onProcessing={(jobId) => track("tryon_processing", { jobId })}
onComplete={({ jobId, imageUrl }) => {
track("tryon_complete", { jobId });
analytics.setResult(imageUrl);
}}
onError={(err) => track("tryon_error", err)}
/>
);
}

Customization

Three layers, in order of precedence (top wins):

  • classNames — map of Tailwind/CSS classes for specific slots (button, modal, footer, etc.).
  • buttonStyles and modalStyles — typed objects for the common tweaks (colors, border, radius, hover).
  • className + style — last-resort overrides on the launch button.
typescript
<PrimeStyleTryon
productImage={product.image}
buttonStyles={{
background: "#111",
color: "#fff",
borderRadius: 0,
hoverBackground: "#2154EF",
}}
modalStyles={{ accent: "#2154EF" }}
classNames={{ launchButton: "font-serif uppercase tracking-widest" }}
/>

Callbacks

All callbacks are optional and fire in this order during a successful flow:

Naming note: the SDK exposes the try-on identifier as jobId. The raw HTTP API calls the same value galleryId. They are interchangeable; the SDK translates at the boundary.
  1. onOpen() — user clicks the launch button.
  2. onUpload(file) — user selects a body photo.
  3. onProcessing(jobId) — the API has accepted the job.
  4. onComplete({ jobId, imageUrl }) — the try-on image is ready.
  5. onClose() — user dismisses the modal.

onError({ message, code? }) fires whenever a recoverable error surfaces to the user (bad image, safety filter block, 402 insufficient balance, etc.).

Headless Sizing

If you don't want the bundled modal, three primitives let you build your own UI on top of the same sizing engine. All of them are pure client-side calls against the PrimeStyle API and use whatever profile is currently active in localStorage.

recommendForProduct()

One-shot async function. Returns RecommendForProductResult | null.

estimateFullMeasurements()

One-shot async function. Returns estimates + unit + detected gender.

usePrimeStyleSize()

React hook wrapper around recommendForProduct. Returns { data, loading, error }.

recommendForProduct
import { recommendForProduct } from "@primestyleai/tryon";
const rec = await recommendForProduct({
apiUrl: "https://myaifitting.com",
product: {
title: "Linen Oxford Shirt",
description: "Regular fit, 100% linen.",
},
sizingUnit: "cm",
});
if (rec) {
console.log(rec.recommendedSize, rec.confidence, rec.reasoning);
}
estimateFullMeasurements
import { estimateFullMeasurements } from "@primestyleai/tryon";
const result = await estimateFullMeasurements({
apiUrl: "https://myaifitting.com",
height: 180,
weight: 78,
heightUnit: "cm",
weightUnit: "kg",
gender: "male",
requiredFields: ["chest", "waist", "hips"],
});
console.log(result?.estimates, result?.unit);
usePrimeStyleSize
import { usePrimeStyleSize } from "@primestyleai/tryon/react";
function SizeRecommendation({ product }) {
const { data, loading, error } = usePrimeStyleSize({
product: { title: product.title, description: product.description },
sizingUnit: "cm",
});
if (loading) return <p>Measuring&hellip;</p>;
if (error) return <p>Couldn&apos;t compute a size.</p>;
if (!data) return <p>Set up your profile to see a size.</p>;
return (
<div>
<strong>Recommended: {data.recommendedSize}</strong>
<p>{data.reasoning}</p>
</div>
);
}

Web Component (Coming Soon)

Drop-in <primestyle-tryon> tag and CDN script coming soon

A framework-agnostic custom element you can drop into any HTML page with a single script tag is in development. For now, use the React component or the raw HTTP API directly.

Profile Storage (localStorage)

These helpers read and write localStorage directly. There is no network I/O and no server-side profile store. Safe to call client-side on any render.

Exports

getProfiles()() => Profile[]

All stored profiles on this browser.

saveProfiles(profiles)(profiles: Profile[]) => void

Replace the entire profile list.

getActiveProfile()() => Profile | null

The currently active profile, or null.

getActiveProfileId()() => string | null

Id of the currently active profile.

setActiveProfileId(id)(id: string) => void

Switch the active profile.

updateProfile(id, patch)(id, patch) => void

Merge-update a profile by id.

updateProfileMeasurements(id, m, unit?)function

Update only the measurements object for a profile.

addSizeToHistory(profileId, entry)function

Remember a recommended size for later lookups.

getCachedSize(profile, productId)(profile, productId) => string | null

Return a previously cached recommendation for (profile, product), or null.

Profile helpers
import {
getProfiles,
getActiveProfile,
getActiveProfileId,
setActiveProfileId,
saveProfiles,
updateProfile,
updateProfileMeasurements,
addSizeToHistory,
getCachedSize,
} from "@primestyleai/tryon";
// Read the current active profile (or null).
const profile = getActiveProfile();
// Switch profiles.
setActiveProfileId("profile-2");
// Update measurements after the user edits them.
updateProfileMeasurements(profile.id, {
chest: 104,
waist: 88,
}, "cm");
// Look up a cached size recommendation for a product.
const cached = getCachedSize(profile, "sku-12345");

Environment Variables

Build-time variables

NEXT_PUBLIC_PRIMESTYLE_API_URLstring

Base URL the SDK calls when no apiUrl prop is passed. Defaults to https://myaifitting.com. Exposed to the browser at build time by Next.js.

The SDK itself does not read the API key from any env var; it hits whatever URL you point it at. Proxy the key through your backend so end users never see it.

Internationalization (i18n)

Pass a locale via the locale prop (or locale attribute on the web component). The SDK ships with a set of built-in translations and will fall back to English for missing keys.

i18n utilities

SUPPORTED_LOCALESreadonly string[]

Locale codes that ship out of the box.

TRANSLATION_KEYSreadonly string[]

All keys used inside the modal. Useful for validation in custom translations.

registerLocale(locale, dict)function

Register or override a locale at runtime.

createT(locale)(locale: string) => (key: string) => string

Build a translation function for a specific locale. Useful for your own UI built around the headless hook.

detectLanguage()() => string

Detect the user's language from the browser (navigator.language).

Guides

Short, copy-and-paste guides for the integrations our partners ask about the most.

Shopify (Coming Soon)

Official Shopify app coming soon

A first-class Shopify integration with product detection, size-guide parsing, per-variant recommendations, and zero-code install is on the roadmap. Until then, you can integrate via the React component or the raw HTTP API.

Using Your Own Size Guide

If you already have structured size data, pass it directly to sizeGuideData. The SDK will skip the call to /api/v1/sizing/sizeguide and feed your table straight into the recommender.

Inline size guide
import { PrimeStyleTryon } from "@primestyleai/tryon/react";
export function ProductButton({ product }) {
return (
<PrimeStyleTryon
productImage={product.image}
productTitle={product.title}
productId={product.sku}
sizeGuideData={{
headers: ["Size", "Chest (cm)", "Waist (cm)"],
rows: [
["S", "92-96", "76-80"],
["M", "100-104", "84-88"],
["L", "108-112", "92-96"],
],
}}
/>
);
}

If all you have is raw HTML (for example, the innerHTML of a merchant's existing size-guide panel), call the parser endpoint once at build time and cache the normalized structure with your product:

Parsed once, cached forever
// 1. Normalize raw HTML once, cache the result on your product.
const guide = await fetch(
"https://myaifitting.com/api/v1/sizing/sizeguide",
{
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({
product: { title: product.title },
sizeGuideRaw: product.sizeGuideHtml,
}),
},
).then((r) => r.json());
// 2. Pass the normalized guide back to PrimeStyleTryon:
<PrimeStyleTryon
productImage={product.image}
productTitle={product.title}
sizeGuideData={guide}
/>

Listening to Try-On Events

Wire the component's callbacks into your analytics to understand the funnel (opens → uploads → completes) and to react to the final image (cache it, show a "looks great" prompt, etc.).

Analytics wiring
<PrimeStyleTryon
productImage={product.image}
productTitle={product.title}
productId={product.sku}
onOpen={() => analytics.track("tryon_open")}
onUpload={(file) =>
analytics.track("tryon_upload", { size: file.size, type: file.type })
}
onProcessing={(jobId) => analytics.track("tryon_processing", { jobId })}
onComplete={({ jobId, imageUrl }) => {
analytics.track("tryon_complete", { jobId });
setResultImage(imageUrl);
}}
onError={({ message, code }) =>
analytics.track("tryon_error", { message, code })
}
/>

Image Best Practices

  • Product images: clean, well-lit, front-facing, preferably on a plain background.
  • Body photos: one person, full torso visible, arms slightly away from the body.
  • Max 50MB per image; compress to ~1600px on the longest side before posting.
  • Use JPEG for photos and PNG only when you need alpha.
Image inputs
// Accepted image sources for modelImage / garmentImage:
// - Public HTTPS URL:
"https://cdn.example.com/products/shirt.jpg"
// - Data URI (base64):
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
// - Helpers for browser-side files:
import { compressImage, isValidImageFile } from "@primestyleai/tryon";
if (!isValidImageFile(file)) throw new Error("Unsupported image");
const compressed = await compressImage(file, { maxDimension: 1600 });

Loading States

Try-ons typically complete in 15-20 seconds. To keep the UI responsive:

  • Show an optimistic state as soon as onProcessing(jobId) fires.
  • Prefer /api/v1/tryon/stream (SSE) over polling; it typically shaves 1-3 seconds off the perceived latency.
  • Fall back to polling /status/:jobId every 3 seconds if SSE is blocked (some corporate proxies strip it).
  • On failed, show a short retry affordance — failed jobs are refunded automatically.

Pricing

Simple pricing based on try-on generations. Subscribe for volume discounts. You are only charged for successful try-ons — failed jobs are refunded automatically. Sizing endpoints are currently free and metered only by rate limits.

PlanPriceTry-Ons
Tier I$299/mo600
Tier IIPopular$999/mo2,500
Tier III$2,999/mo10,000

~15s

Average processing time

$0

Failed jobs are free

Auto

Refund on failed jobs

Support

We're here to help you integrate. Reach out through any of these channels:

Frequently Asked

What image formats are supported?

JPEG, PNG, and WebP. Both URLs and base64 data URIs. Max 50MB per file.

Where are user profiles stored?

Profiles live in the user's browser localStorage. PrimeStyle does not offer a server-side profile store and no endpoint for profile CRUD. If you need profiles to follow a user across devices, you are free to mirror them into your own backend.

Do you store customer photos or try-on results?

Input photos are forwarded in memory to the rendering backend and dropped after the request completes. Generated results are returned to your application. See the Privacy Policy for retention details.

What happens if a try-on fails?

Failed try-ons are not counted against your balance. Common causes: safety filter blocks or invalid images. Listen for failed on the SSE stream or check /status/:jobId.

How long does processing take?

Typically 15-20 seconds. Use the SSE stream to receive the result the instant it's ready, or poll the status endpoint every 3 seconds as a fallback.

Data Processing Addendum

This Data Processing Addendum (“DPA”) forms part of the agreement between PrimeStyleAI (“Processor”) and the Customer (“Controller”) for the provision of the Services.

If there is a conflict between the DPA and the main agreement, the DPA controls for data protection matters.

1. Roles & Scope

1.1 Controller and Processor. Customer is the Controller of personal data processed via the Services. PrimeStyleAI acts as the Processor.

1.2 Purpose. Processor will process personal data only to provide the Services (render Outputs, secure the platform, and operate the developer portal).

1.3 Instructions. Customer instructs Processor to process personal data as necessary to provide the Services and as documented in the API/SDK documentation and this DPA.

2. Details of Processing

Annex 1 describes the subject matter, duration, nature and purpose of processing, types of personal data, and categories of data subjects.

Annex 1

Subject matter
AI-powered image rendering for virtual try-on outputs.
Duration
For the term of the agreement and as needed for transient processing.
Nature of processing
Receiving, transforming, and generating image outputs; logging for security and performance.
Purpose
Provide the Services; prevent abuse; ensure reliability.
Data subjects
Customer’s end users and Customer’s authorized users.
Types of data
User-uploaded images; product images; technical identifiers; account emails; logs.

3. Processor Obligations

  • Process personal data only on documented instructions from Customer, unless required by law.
  • Ensure persons authorized to process personal data are bound by confidentiality obligations.
  • Implement appropriate technical and organizational measures to protect personal data.
  • Assist Customer with responding to data subject requests, taking into account the nature of processing.
  • Assist Customer with DPIAs and consultations with regulators to the extent required and reasonably available.

4. Security Measures

Processor maintains reasonable measures appropriate to the risk. Annex 2 lists baseline controls.

Annex 2 — Baseline Security Controls

  • Encryption in transit (HTTPS/TLS).
  • Access controls and least-privilege administrative access.
  • Credential and key management practices (rotation recommended).
  • Monitoring, logging, and abuse prevention controls.
  • Separation of environments where feasible (e.g., dev/prod).

5. Subprocessors

Customer authorizes Processor to engage subprocessors to provide the Services (e.g., cloud hosting and AI infrastructure providers). Processor will impose data protection obligations on subprocessors consistent with this DPA and remains responsible for subprocessors' performance.

Processor will provide an up-to-date list of subprocessors upon request or via an attached schedule. Customer may object to a new subprocessor on reasonable data protection grounds by providing written notice within ten (10) days of notice.

6. International Transfers

Where personal data originating in the EEA/UK/Switzerland is transferred to a country without an adequacy decision, the Parties will implement appropriate safeguards, such as the EU Standard Contractual Clauses (SCCs) and/or the UK Addendum, typically incorporated by reference through this DPA or an exhibit.

7. Personal Data Breach

Processor will notify Customer without undue delay after becoming aware of a confirmed personal data breach affecting the Services. Processor will provide information reasonably necessary for Customer to meet any breach notification obligations.

8. Deletion & Return

Upon termination of the Services, Processor will delete or return personal data as requested by Customer, subject to applicable law and reasonable backups/archival practices. Transient user images are not intended to be stored persistently by default.

9. Audits

Upon reasonable prior notice and no more than once annually, Customer may audit Processor's compliance with this DPA to the extent necessary and proportionate, subject to confidentiality and security constraints. Processor may satisfy audit requests by providing summaries of controls, third-party reports (if available), or other reasonable evidence of compliance.

10. Liability

Each Party's liability under this DPA is subject to the limitations of liability in the main agreement, unless prohibited by applicable law.

Contact

admin@PrimeStyleAI.com28171 Westfield Drive, Laguna Niguel, CA 92677, USA