# TryYourSize Docs (full) > All of docs.tryyoursize.com as Markdown. Each page starts with its title and its source URL. --- # TryYourSize developer docs > Add size recommendations to your store. Quickstarts for Shopify, the web, React, iOS, Android, React Native, Flutter and your server; SDK and HTTP API reference. Source: https://docs.tryyoursize.com/ ## Add size recommendations to your store Pick your stack. Each quickstart takes you from a test key to a size on your product page, and opens with a **For your developer** note a brand team can forward. - [Shopify](https://docs.tryyoursize.com/quickstarts/shopify.md): No code: install the app, add the block - [Web SDK](https://docs.tryyoursize.com/quickstarts/web.md): One script tag, any storefront - [React](https://docs.tryyoursize.com/quickstarts/react.md): Provider and components, SSR-safe - [iOS](https://docs.tryyoursize.com/quickstarts/ios.md): SwiftUI and UIKit button - [Android](https://docs.tryyoursize.com/quickstarts/android.md): Compose and Views button - [React Native](https://docs.tryyoursize.com/quickstarts/react-native.md): The native button, from JS - [Flutter](https://docs.tryyoursize.com/quickstarts/flutter.md): A native platform view - [Server](https://docs.tryyoursize.com/quickstarts/server.md): Node.js and Python SDKs ## Products - [Size guide](https://docs.tryyoursize.com/products/size-guide.md): “Your size: M” next to your size picker, in your own size labels. (Web · iOS · Android · Shopify) - [Fit view](https://docs.tryyoursize.com/products/fit-view.md): The garment on the shopper's 3D body: ease in cm, pressure in kPa, per zone. (Window · all SDKs) - [Recommendations API](https://docs.tryyoursize.com/products/recommendations-api.md): Sizes for up to 25 products from your server or a headless storefront. (POST /v1/recommendations) - [Catalog API](https://docs.tryyoursize.com/products/catalog-api.md): Products, size maps, garment versions and fit groups, synced from your PIM. (/v1/products · /v1/garments) - [Webhooks](https://docs.tryyoursize.com/products/webhooks.md): Signed events when a size is recommended or a garment is published. (TYS-Signature · HMAC-SHA256) - [A/B and outcomes](https://docs.tryyoursize.com/products/ab-outcomes.md): A holdout group, orders and returns: size returns with and without TryYourSize. (POST /v1/orders · /v1/returns) - [Studio](https://docs.tryyoursize.com/products/studio.md): Where your team describes garments, maps sizes and gets keys. (studio.tryyoursize.com) ## Reference - **[SDK reference](https://docs.tryyoursize.com/reference.md)**: one page per method, component and event for the [Web SDK](https://docs.tryyoursize.com/reference/web.md), [React](https://docs.tryyoursize.com/reference/react.md), [Node.js](https://docs.tryyoursize.com/reference/node.md), [Python](https://docs.tryyoursize.com/reference/python.md), [iOS](https://docs.tryyoursize.com/reference/ios.md), [Android](https://docs.tryyoursize.com/reference/android.md), [React Native](https://docs.tryyoursize.com/reference/react-native.md) and [Flutter](https://docs.tryyoursize.com/reference/flutter.md), generated from their source. - **[HTTP API reference](https://docs.tryyoursize.com/api/reference.md)**: every public endpoint, generated from our [OpenAPI spec](https://docs.tryyoursize.com/openapi.json). - **[Troubleshooting](https://docs.tryyoursize.com/guides/troubleshooting.md)**, **[API errors](https://docs.tryyoursize.com/api/errors.md)** and the **[changelog](https://docs.tryyoursize.com/api/changelog.md)**. ## For brand teams You do not need to read code to get started. Each quickstart opens with a short **For your developer** note you can forward: what the integration does, how long it takes, and which keys to send. Keys are in **Studio → Developers → API keys**. --- # Going live > Verify your domain, complete the live activation checklist, and understand billing before you switch to live keys. Source: https://docs.tryyoursize.com/get-started/going-live/ Build and test with **test keys**. When you are ready for real shoppers, activate live mode for your store in **Studio → Activate live mode**, then swap `pk_test_…` / `sk_test_…` for live keys. ## The live activation checklist Live keys can be created, and live origins served, only when every item is done. Studio shows the checklist with a link for each item. | # | Item | What it means | |---|---|---| | 1 | **Verified domain** | At least one verified domain on your organization, covering every live origin | | 2 | **Terms** | The owner accepted the current Terms of Service and Data Processing Agreement | | 3 | **Billing details** | Legal name, country and billing email (and a VAT ID if you are an EU business). Done automatically when you are billed through Shopify or have a contract with us | | 4 | **Payment method** | Needed only on a paid plan. The free Starter plan does not need a card: it has a hard cap of 2,500 fit checks per month | | 5 | **Organization active** | Your organization is not suspended | Then press **Activate live mode** (owners and admins with the `live.activate` permission; you are asked to sign in again). It is recorded in your audit log. ## Verify your domain In **Studio → Settings → Domains**, add your domain and prove you control it with one of: | Method | How | |---|---| | **DNS TXT record** (default) | Add `_tryyoursize.` with the value `tys-verify=` | | **Meta tag** | Put `` on your home page (`https:///`). For hosted shops without DNS access | | **Shopify** | Automatic for your `*.myshopify.com` host and primary domain when you install the [Shopify app](https://docs.tryyoursize.com/quickstarts/shopify.md) | - A verified apex domain (`brand.com`) covers `https://brand.com`, `https://www.brand.com` and `https://*.brand.com`. - A hostname can be verified by only one organization. - Domains are checked again every week. After 3 failed checks in a row a domain lapses; its live origins keep working for 14 more days, then stop. Keep the TXT record or meta tag in place. ## Billing - A **fit check** is one shopper seeing a size for one product, in live mode. The same shopper and product count once per 30 days. A changed body counts again. - **Test mode is never metered.** Shoppers in an [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md) are never metered either: they see no element. - A page view never starts a simulation. Server-side recommendations count like browser ones: a delivered size is a fit check. - **Starter** is free with a hard cap of 2,500 fit checks per month and 100 published styles. Above the cap, the size guide hides and size answers are `unavailable` until the next period; shoppers never see an error. Paid plans can set a budget cap. - Plans and prices: [tryyoursize.com/#pricing](https://tryyoursize.com/#pricing). Shopify stores can be billed through Shopify. Usage is in **Studio → Settings → Usage**; plan and invoices in **Settings → Billing**. ## Go-live checklist for developers - [ ] Replace test keys with live keys (publishable on the page, secret on the server). Keep test keys for your staging site. - [ ] Register your production origins for **live** mode. - [ ] Publish every garment you want live shoppers to see (live answers published versions only). - [ ] Map every shop size in each product's size map (publishing needs it). - [ ] Add a live [webhook endpoint](https://docs.tryyoursize.com/guides/webhooks.md) if you use webhooks; endpoints are per mode. - [ ] Check your Content Security Policy allows `https://js.tryyoursize.com` (scripts) and `https://embed.tryyoursize.com` (frames). --- # Keys and modes > Publishable, secret and restricted keys; test and live mode; key scopes, origins, rolling keys and IP allow-lists. Source: https://docs.tryyoursize.com/get-started/keys-and-modes/ Every key belongs to one **store** and one **mode** (test or live). Create and manage them in **Studio → Developers → API keys**. The full key is shown once; we store only a hash. ## Three kinds of key | Kind | Prefix | Where it goes | What it can do | |---|---|---|---| | **Publishable** | `pk_test_…` / `pk_live_…` | Your web pages and apps | Show the size guide, open the window, read the shopper's recommended size. Works only from the **origins** you register | | **Secret** | `sk_test_…` / `sk_live_…` | Your server only | Every server scope of its store | | **Restricted** | `rk_test_…` / `rk_live_…` | A partner's server (returns platform, PLM, BI) | Only the scopes you choose | Never put a secret or restricted key in a page or an app. The web SDK throws `secret_key_in_browser` and the mobile SDKs throw `secret_key_in_app` if you try. ## Test and live mode The mode belongs to the key: a request made with a test key is a test request. | | Test | Live | |---|---|---| | Origins | The origins you add for test mode, plus `localhost` | Only origins covered by a [verified domain](https://docs.tryyoursize.com/get-started/going-live.md#verify-your-domain) | | Garments answered | Drafts and published versions (designers can preview before publishing) | Published versions only | | Shoppers | Real shopper accounts can sign in; the window shows a "TEST MODE" ribbon | Real shoppers | | Billing | Never metered | Fit checks are metered | | Limits | 1,000 fit checks per day per store | By plan | | Analytics, A/B, webhooks | Kept separate from live | Live | The **catalog is shared** by both modes: you do not keep two catalogs. Test mode answers drafts, live mode answers published versions, so a draft never reaches live shoppers. ## Key scopes A secret key has every scope. A restricted key has only the scopes it was created with; a call outside them answers `403 forbidden`. Each operation in the [API reference](https://docs.tryyoursize.com/api/reference.md) lists its **Key scopes**. | Scope | Allows | |---|---| | `products:read` | List and read products, size maps, garments and fit groups | | `products:write` | Create and update products, size maps and fit groups | | `garments:write` | Create and edit garment drafts | | `garments:publish` | Publish a garment version (shoppers are fitted against it) | | `orders:write` | `POST /v1/orders` | | `returns:write` | `POST /v1/returns` | | `fit_checks:read` | `POST /v1/recommendations` (server-side recommendations) | | `audit:read` | `GET /v1/audit_events` | | `analytics:read`, `webhooks:manage` | Reserved for server endpoints that are coming soon | ## Origins A publishable key works only on the sites you register for it, per store and mode, in **Studio → Settings → Stores → Allowed sites**. - `https://` origins only, no path. `http://` only for `localhost`, in test mode. - `https://*.brand.com` matches subdomains (not the apex `brand.com`). - Live origins must be covered by a verified domain. A verified apex domain covers its subdomains. - Up to 50 origins per mode. - Apps register an app origin: `ios-app://` or `android-app://` (lower case). App origins are accepted in **test mode** only today; live mode for apps needs app verification, which is coming soon. ## Rolling and revoking keys - **Roll** a key to replace it without downtime. The new key has the same kind, mode, name and scopes. Choose how long the old key keeps working: `now`, `1h`, `24h` or `7d`. After that it answers `401`. - **Revoke** stops a key at once. - Creating, rolling or revoking a **live** secret or restricted key asks you to sign in again. - Up to 20 working keys per store and mode. - Every key shows when it was last used (updated at most once a minute). Every change is in your audit log. ## IP allow-list Secret and restricted keys can be limited to a list of IP addresses; a call from any other address is refused. The Studio screen for it is coming soon: until then, ask [support](mailto:support@tryyoursize.com) to set one. ## Rate limits See [Rate limits](https://docs.tryyoursize.com/guides/rate-limits.md). --- # How TryYourSize works > From a tape measure to a size in your store's labels. What happens, who sees what, and what your store receives. Source: https://docs.tryyoursize.com/get-started/overview/ TryYourSize recommends a size by simulating your garments on the shopper's own body. The shopper measures once, and the same body works in every store that uses TryYourSize. ## The flow 1. **Tape measurements.** The shopper enters a few measurements taken with a tape measure (height, weight and body measurements such as chest, waist and hip). They do this once, in our window, and can update them at any time. 2. **A body from the measurements.** Our engine builds a 3D body from those numbers alone, not from photos. 3. **Cloth simulation.** Your garment, as you described it in TryYourSize Studio (sizes, points of measure per size, grading and fabric), is simulated on that body in each size. For every size and zone the engine reports objective numbers: **ease** (room between body and garment, in cm), **contact pressure** (in kPa) and **fabric stretch** (%), and whether the size is **wearable**. 4. **A size.** The recommended size is the wearable size that best matches the fit your designer intended. It is returned in **your** size labels, through the product's size map ("M" → "Medium"). Many garments answer in under a second once they are published. The 3D view the shopper opens with "See it on you" then confirms the result on their own body. ## What your store receives **Sizes only.** Your page, your app and your server receive the recommended size, the sizes that are wearable on this body, and whether the size has been confirmed by the full simulation. They never receive the shopper's measurements, their body, their email or their account. See [Privacy](https://docs.tryyoursize.com/guides/privacy.md). ## The pieces | Piece | Where it runs | What it does | |---|---|---| | **TryYourSize.js** (web SDK) and the native SDKs | Your product page or app | Shows the size guide element, gives you the recommended size, opens our window | | **The measuring window** | Our origin (`embed.tryyoursize.com`), framed in your page or shown in a native sheet | Sign-in, tape measurements, 3D try-on | | **Studio** | `studio.tryyoursize.com` | Your catalog, garments, size maps, keys, origins, webhooks, results | | **The server API** | `api.tryyoursize.com` | Catalog sync, orders and returns, server-side recommendations | ## Costs at a glance A **fit check** is one shopper seeing a size for one product in live mode. The same shopper and product count once per 30 days. Test mode is never counted. A page view never starts a simulation: if a size is not available yet, the status is `unavailable` until the shopper asks for it. See [Going live](https://docs.tryyoursize.com/get-started/going-live.md) for billing. ## Next - [Keys and modes](https://docs.tryyoursize.com/get-started/keys-and-modes.md): publishable, secret and restricted keys; test and live. - Pick a quickstart: [Shopify](https://docs.tryyoursize.com/quickstarts/shopify.md), [Web](https://docs.tryyoursize.com/quickstarts/web.md), [React](https://docs.tryyoursize.com/quickstarts/react.md), [iOS](https://docs.tryyoursize.com/quickstarts/ios.md), [Android](https://docs.tryyoursize.com/quickstarts/android.md), [Server](https://docs.tryyoursize.com/quickstarts/server.md). --- # Android quickstart > The native Size guide button (Compose and Views), the shopper's recommended size, and the try-on window. Source: https://docs.tryyoursize.com/quickstarts/android/ > **For your developer** > > **What it does:** Adds a native 'Size guide' button next to the size picker in our Android app. Connected shoppers see 'Your size: M'; a tap opens the TryYourSize window. The app receives sizes only. > > **Time needed:** About a day, including registering the app. > > **What to send them:** The test publishable key (pk_test_…) and our app's package name to register as android-app://. minSdk 24. Jetpack Compose and Views. > **Caution: Test mode only for apps, for now** > > App origins (`android-app://…`) are accepted in test mode. Live mode for apps needs app verification (asset links), which is coming soon. Publication to our Maven repository is also coming soon: during the pilot we share the library with you. ## 1. Install ```kotlin dependencies { implementation("com.tryyoursize:tryyoursize-android:0.1.0") } ``` ## 2. Register your app In Studio, add `android-app://` (lower case, for example `android-app://com.brand.shop`) to the allowed sites of your store for **test mode**. ## 3. Use it The window needs an androidx `ComponentActivity` (for example `AppCompatActivity`). ```kotlin val tys = TryYourSize(context, "pk_test_…") // a secret key throws: keep sk_ on your server // Compose, where your "Size guide" link was: Row(verticalAlignment = Alignment.CenterVertically) { Text("Size"); Spacer(Modifier.weight(1f)) SizeGuide(tys, "SKU-123", textStyle = MaterialTheme.typography.bodyMedium) { rec -> // On show and on every change. Preselect, but never over the shopper's own choice. if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } } // Views val guide = SizeGuideView(context).apply { client = tys; product = "SKU-123"; variant = SizeGuideVariant.BUTTON } // Without a button val rec = tys.recommendation("SKU-123") // suspend; or getRecommendation("SKU-123") { result -> } tys.present(activity, "SKU-123") tys.on { e -> if (e is TryYourSize.Event.Size) select(e.selection.size) } ``` Call the SDK from the main thread. **Button states:** "Size guide" → "Calculating your size…" → "Your size: **M**" (with the connected dot), or "Size guide · connected". Labels follow `locale` (en, es, fr, de, it). **`Recommendation`:** `status` (`READY`, `COMPUTING`, `NO_SIZE`, `SIGNED_OUT`, `UNAVAILABLE`), `size` in your labels, `studioSize`, `wearableSizes`. It never starts a simulation. ## 4. Appearance By default (`INHERIT`) the button and the window take your theme's text and background colours; the button takes the `textStyle` you pass. ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(colorPrimary = 0xFF3F51B5.toInt(), fontFamily = "Inter, Roboto", borderRadius = 12f, fonts = listOf("https://fonts.googleapis.com/css2?family=Inter:wght@400;600")))) tys.appearance = tys.appearance.copy(colorPrimary = Color.BLACK) // live: an open window restyles at once ``` ## 5. Events and errors - **Events:** `Ready`, `Open`, `Close`, `Size(SizeSelection)`, `LoadError(TryYourSizeException)`. - **Error codes** (`TryYourSizeException.code`): `SECRET_KEY_IN_APP`, `INVALID_KEY`, `INVALID_APP_IDENTIFIER`, `INVALID_OPTION`, `WINDOW_UNREACHABLE`, `APP_NOT_ALLOWED`, `SIGN_IN_CANCELLED`, `SIGN_IN_UNAVAILABLE`. - **Back** asks the window first: with unsaved measurements, the shopper confirms. ## Sign-in Shoppers sign in inside the window (email and a 6-digit code), once per app. Sign-in through Custom Tabs with passkeys is coming soon. --- # Flutter quickstart > The tryyoursize plugin over the native SDKs. The Size guide button and the shopper's recommended size. Source: https://docs.tryyoursize.com/quickstarts/flutter/ > **For your developer** > > **What it does:** Adds the native TryYourSize 'Size guide' button and the shopper's recommended size to our Flutter app. The app receives sizes only. > > **Time needed:** About a day. > > **What to send them:** The test publishable key (pk_test_…) and our iOS bundle ID and Android package to register. A thin plugin over the native [iOS](https://docs.tryyoursize.com/quickstarts/ios.md) and [Android](https://docs.tryyoursize.com/quickstarts/android.md) SDKs. The measuring and 3D try-on window is our web window, opened by the native SDK (a sheet on iOS, full screen on Android). > **Caution: Preview** > > The Flutter plugin is in preview: it has not been through our build and device tests yet. The native SDKs it wraps are tested. App origins work in test mode only for now; pub.dev publication is coming soon. ## Install ```yaml # pubspec.yaml dependencies: tryyoursize: ^0.1.0 # from the source we share during the pilot ``` - **iOS** (16+): the plugin depends on the `TryYourSize` pod (CocoaPods; `platform :ios, '16.0'`). - **Android** (minSdk 24): the plugin depends on `com.tryyoursize:tryyoursize-android`. The window needs an androidx `ComponentActivity`, so `MainActivity` must extend **`FlutterFragmentActivity`**: ```kotlin class MainActivity : io.flutter.embedding.android.FlutterFragmentActivity() ``` ## Register your app In Studio, for test mode, add `ios-app://` and `android-app://` (lower case). ## Use ```dart final tys = await TryYourSize.create( 'pk_test_…', appearance: const Appearance(borderRadius: 10), // default theme: inherit your app's colours ); // The button, where your "Size guide" link was. A tap opens the window. const SizeGuide(product: 'SKU-123'); // or variant: SizeGuideVariant.button // Preselect the recommended size, but never override the shopper's own choice. tys.onRecommendation('SKU-123').listen((rec) { if (rec.status == RecommendationStatus.ready && !touched) select(rec.size!); }); // The shopper picked a size in the window. tys.events.listen((e) { if (e is SizeSelected) select(e.selection.size); }); // Without the button: final rec = await tys.recommendation('SKU-123'); await tys.present('SKU-123'); ``` | `Recommendation` | | |---|---| | `status` | `ready`, `computing`, `noSize` (simulated, no size is wearable), `signedOut`, `unavailable` (no size for this product yet) | | `size` | The recommended size in **your** label, or null | | `studioSize` | The same size as entered in Studio | | `wearableSizes` | Sizes this body can wear, in your labels | Errors are `TryYourSizeException` with a stable `code`: `secret_key_in_app`, `invalid_key`, `invalid_app_identifier`, `app_not_allowed`, `window_unreachable`. **Appearance:** `theme` (`inherit` or `tryyoursize`, our light beige brand), `colorPrimary`, `colorBackground`, `colorText`, `fontFamily`, `borderRadius` (0–32) and `fonts` (Google Fonts stylesheet URLs). `tys.setAppearance(...)` restyles live, for example when your app switches to dark mode. --- # iOS quickstart > The native Size guide button (SwiftUI and UIKit), the shopper's recommended size, and the try-on window in a sheet. Source: https://docs.tryyoursize.com/quickstarts/ios/ > **For your developer** > > **What it does:** Adds a native 'Size guide' button next to the size picker in our iOS app. Connected shoppers see 'Your size: M'; a tap opens the TryYourSize window in a sheet. The app receives sizes only. > > **Time needed:** About a day, including registering the app. > > **What to send them:** The test publishable key (pk_test_…) and our app's bundle ID to register as ios-app://. iOS 16+, Swift 5.9+, SwiftUI and UIKit. > **Caution: Test mode only for apps, for now** > > App origins (`ios-app://…`) are accepted in test mode. Live mode for apps needs app verification (associated domains), which is coming soon. Public distribution of the Swift package and pod is also coming soon: during the pilot we share the package with you. ## 1. Install - **Swift Package Manager:** File → Add Package Dependencies → the `TryYourSize` package, product `TryYourSize`. - **CocoaPods:** `pod 'TryYourSize'` (with the `:path` or source we share). ## 2. Register your app In Studio, add `ios-app://` (lower case, for example `ios-app://com.brand.shop`) to the allowed sites of your store for **test mode**. Until it is registered, the button reports `app_not_allowed`. ## 3. Use it ```swift let tys = try TryYourSize(publishableKey: "pk_test_…") // a secret key throws: keep sk_ on your server // SwiftUI, where your "Size guide" link was: HStack { Text("Size") Spacer() SizeGuide(client: tys, product: "SKU-123") { rec in // On show and on every change. Preselect, but never over the shopper's own choice. if rec.status == .ready, let size = rec.size, !touched { selectedSize = size } } } // UIKit let guide = SizeGuideButton(client: tys, product: "SKU-123", variant: .button) guide.onRecommendation = { rec in /* … */ } // Without a button let rec = try await tys.recommendation(for: "SKU-123") tys.present(product: "SKU-123") // open the window yourself tys.on { event in if case let .size(s) = event { select(s.size) } } // "Use this size" in the window ``` **Button states:** "Size guide" (signed out) → "Calculating your size…" → "Your size: **M**" (with the connected dot), or "Size guide · connected". Labels follow `locale` (en, es, fr, de, it); `label:` sets your own words. **`Recommendation`:** `status` (`.ready`, `.computing`, `.noSize`, `.signedOut`, `.unavailable`), `size` in your labels, `studioSize`, `wearableSizes`. The web SDK's `"none"` is `.noSize` in Swift. A recommendation never starts a simulation. ## 4. Appearance By default (`.inherit`) the button takes your SwiftUI font and text colour, and the window takes your app's text and background colours, dark mode included. ```swift let config = TryYourSize.Configuration(appearance: Appearance( colorPrimary: TYSColor(UIColor.systemIndigo), fontFamily: "Inter, -apple-system", borderRadius: 12, fonts: [URL(string: "https://fonts.googleapis.com/css2?family=Inter:wght@400;600")!])) let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: config) tys.appearance.colorPrimary = TYSColor(hex: "#111111") // live: an open window restyles at once ``` ## 5. Events and errors - **Events** (`tys.on`): `.ready`, `.open`, `.close`, `.size(SizeSelection)`, `.loadError(TryYourSizeError)`. - **Error codes** (`TryYourSizeError.code`): `secret_key_in_app`, `invalid_key`, `invalid_app_identifier`, `invalid_option`, `window_unreachable`, `app_not_allowed`, `sign_in_cancelled`, `sign_in_unavailable`. - Swiping the sheet down asks the window first: with unsaved measurements, the shopper confirms. ## Sign-in Shoppers sign in inside the window (email and a 6-digit code), once per app. Sign-in through the system browser with passkeys, sharing the shopper's tryyoursize.com session, is coming soon. --- # React Native quickstart > @tryyoursize/react-native: the native Size guide button and the recommended size, over the iOS and Android SDKs. Source: https://docs.tryyoursize.com/quickstarts/react-native/ > **For your developer** > > **What it does:** Adds the native TryYourSize 'Size guide' button and the shopper's recommended size to our React Native app. The app receives sizes only. > > **Time needed:** About a day. > > **What to send them:** The test publishable key (pk_test_…) and our iOS bundle ID and Android package to register. A thin wrapper over the native [iOS](https://docs.tryyoursize.com/quickstarts/ios.md) and [Android](https://docs.tryyoursize.com/quickstarts/android.md) SDKs. It needs a development build (bare React Native or Expo prebuild); Expo Go is not supported. React Native 0.73+, iOS 16+, Android minSdk 24. > **Caution: Preview** > > The JavaScript layer and the Android module are tested; the iOS module is in preview. App origins work in test mode only for now. The npm package is published for pilot customers on request. ## Install ```sh npm install @tryyoursize/react-native ``` The package depends on the native SDKs: add the `TryYourSize` pod to your `Podfile` (then `pod install`) and the `com.tryyoursize:tryyoursize-android` library to your Android build. `MainActivity` (a `ReactActivity`) already is a `ComponentActivity`. **Register your app** in Studio for test mode: `ios-app://` and `android-app://`. ## Use ```tsx await TryYourSize.configure("pk_test_…", { appearance: { variables: { colorPrimary: "#111111", borderRadius: 10 } } }); Size r.status === "ready" && !touched && setSize(r.size)} /> ; const rec = await TryYourSize.getRecommendation("SKU-123"); // { product, status, size, studioSize, wearableSizes } const sub = TryYourSize.addRecommendationListener("SKU-123", (r) => {}); // sub.remove() TryYourSize.addEventListener((e) => e.type === "size" && setSize(e.selection.size)); TryYourSize.present("SKU-123"); ``` `status` takes the web SDK's values: `ready`, `computing`, `none`, `signed_out`, `unavailable`. Appearance colours are `#rrggbb` or `#rrggbbaa`. The default `inherit` theme takes your app's native text and background colours. --- # React quickstart > @tryyoursize/react: a provider near the root and element components where you need them. SSR-safe. Source: https://docs.tryyoursize.com/quickstarts/react/ > **For your developer** > > **What it does:** Adds TryYourSize components to our React storefront: a size guide next to the size picker and the recommended size in our own labels. We receive sizes only. > > **Time needed:** An afternoon. > > **What to send them:** The publishable key (pk_test_…) and our site origins to register in Studio. The components work like `@stripe/react-stripe-js`: one provider near the root of your app, and element components where you want them. ```bash npm install @tryyoursize/react @tryyoursize/sdk ``` ## Quick start ```tsx // Once, outside render, so you don't create a new client on every render. const tysPromise = loadTryYourSize("pk_test_…"); return ( ); } function ProductPage() { const [size, setSize] = useState(null); return ( <> setSize(size)} /> ); } ``` If you bundle the SDK from npm instead of loading it from our CDN, pass the key: ``. ## `` | Prop | | |---|---| | `publishableKey` | Your publishable key. The provider creates the client in the browser. | | `client` | Instead of `publishableKey`, a client or a promise of one (for example `loadTryYourSize()`). `null` means "not yet": elements wait. | | `options.appearance` | See [Appearance](https://docs.tryyoursize.com/guides/appearance.md). Changes apply live; an inline object does not cause updates on every render. | | `options.locale` | `"auto"` or a language. Read once, when the client is created. | The client cannot change after it is set; if it changes, the provider logs a warning and keeps the first client. ## Components - **``**: the link-sized size guide, next to your size picker. `variant="link"` or `"button"`. - **``**: the inline element (recommended). **``**: the compact "Find my size" button. | Prop | | |---|---| | `product` (required) | Your product ID or SKU. Changing it calls `update({ product })`, not a new element. | | `onSize` | `({ product, size, verified }) => void`: the shopper chose a size in our window. | | `onRecommendation` | `({ product, status, size, studioSize, wearableSizes }) => void`. See [Recommended size](https://docs.tryyoursize.com/guides/recommendation-event.md). | | `onReady`, `onOpen`, `onClose`, `onLoadError` | The SDK events with the same names. | | `id`, `className`, `style` | Applied to the mount `
`. | ```tsx status === "ready" && !touched && setSize(size!)} /> ``` A `ref` gives you `{ open(), close(), getElement() }`: ```tsx const fit = useRef(null); ``` ## Hooks - `useTryYourSize()` returns the client, or `null` until it has loaded. Use it for the client methods, for example `await tys.recommendationHandle()` to send a sealed handle to your server ([Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md)). - `useElements()` returns the provider's elements group, or `null` until loaded: use it for `elements.update({ appearance })`. ## Guarantees - **Mount on commit, destroy on unmount.** Under `` only one iframe is ever live. - **SSR-safe.** Importing and server rendering never touch `window`; on the server you get an empty mount `
`. - **Stable handlers.** Changing an event handler never reloads the iframe. --- # Server API quickstart > Call the TryYourSize server API from Node or Python. Catalog sync, orders and returns, server-side recommendations. Source: https://docs.tryyoursize.com/quickstarts/server/ > **For your developer** > > **What it does:** Connects our backend to TryYourSize: sync products and garment data, send orders and returns for the size-return report, and read shoppers' sizes server-side. No measurements are ever sent or received. > > **Time needed:** A few hours for a first call; a day or two for a full catalog sync. > > **What to send them:** A test secret key (sk_test_…) from Studio → Developers → API keys, sent through a password manager, never by email or chat. For a partner, a restricted key (rk_test_…) with only the scopes it needs. ## Conventions | | | |---|---| | Base URL | `https://api.tryyoursize.com` | | Authentication | `Authorization: Bearer sk_test_…` (or a [restricted key](https://docs.tryyoursize.com/get-started/keys-and-modes.md#key-scopes) `rk_…`) | | Version | `TYS-Version: 2026-10-25` (see [Versioning](https://docs.tryyoursize.com/guides/versioning.md)) | | Errors | `{ error, status, code, doc_url, request_id }` plus the `X-Request-ID` header (see [Errors](https://docs.tryyoursize.com/api/errors.md)) | | Retries | `Idempotency-Key` on POST (see [Idempotency](https://docs.tryyoursize.com/guides/idempotency.md)) | | Lists | `limit` + `cursor` → `{ data, has_more, next_cursor }` (see [Pagination](https://docs.tryyoursize.com/guides/pagination.md)) | Catalog paths take a TryYourSize product ID **or your SKU**. The catalog is shared by test and live mode: test answers drafts, live answers published versions. ## Install **Node.js** ```bash npm install @tryyoursize/node ``` ```ts const tys = new TryYourSize(process.env.TYS_SECRET_KEY!); ``` **Python** ```bash pip install tryyoursize ``` ```python tys = tryyoursize.TryYourSize(os.environ["TYS_SECRET_KEY"]) ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/products?limit=5 \ -H "Authorization: Bearer $TYS_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` > **Note** > > The Node (`@tryyoursize/node`) and Python (`tryyoursize`) libraries are our official server SDKs. They pin `TYS-Version` for you, retry safely with idempotency keys, and follow the API reference one to one; when in doubt, the [API reference](https://docs.tryyoursize.com/api/reference.md) is the source of truth. ## Sync a product **Node.js** ```ts const product = await tys.products.create( { external_id: "CREW-TSHIRT", name: "Crew T-shirt", category: "tshirt" }, { idempotencyKey: crypto.randomUUID() }, ); // Your shop's labels for each Studio size: recommendations come back in these labels. await tys.products.sizeMap.save("CREW-TSHIRT", { entries: [ { studio_size: "S", shop_value: "Small" }, { studio_size: "M", shop_value: "Medium" }, { studio_size: "L", shop_value: "Large" }, ], }); for await (const p of tys.products.list({ limit: 50 })) { console.log(p.id, p.external_id); } ``` **Python** ```python product = tys.products.create( external_id="CREW-TSHIRT", name="Crew T-shirt", category="tshirt", idempotency_key=str(uuid.uuid4()), ) tys.products.size_map.save("CREW-TSHIRT", entries=[ {"studio_size": "S", "shop_value": "Small"}, {"studio_size": "M", "shop_value": "Medium"}, {"studio_size": "L", "shop_value": "Large"}, ]) for p in tys.products.list(limit=50).auto_paging_iter(): print(p["id"], p["external_id"]) ``` Field names and bodies are in the [API reference](https://docs.tryyoursize.com/api/reference.md). ## Resources | Node | Python | Endpoint | |---|---|---| | `tys.recommendations.create({ handle, products })` | `tys.recommendations.create(...)` | `POST /v1/recommendations` | | `tys.products.list / create / retrieve / update` | same | `/v1/products`, `/v1/products/{product}` | | `tys.products.sizeMap.retrieve / save` | `tys.products.size_map.retrieve / save` | `/v1/products/{product}/size_map` | | `tys.products.garments.list / createDraft` | `tys.products.garments.list / create_draft` | `/v1/products/{product}/garments` | | `tys.garments.retrieve / updateDraft / publish` | `tys.garments.retrieve / update_draft / publish` | `/v1/garments/{garment_id}` | | `tys.fitGroups.list / create / retrieve / linkProducts` | `tys.fit_groups.list / create / retrieve / link_products` | `/v1/fit_groups` | | `tys.orders.create` | `tys.orders.create` | `POST /v1/orders` | | `tys.returns.create` | `tys.returns.create` | `POST /v1/returns` | | `tys.auditEvents.list` | `tys.audit_events.list` | `GET /v1/audit_events` | | `tys.webhooks.constructEvent(rawBody, header, secret)` | `tryyoursize.Webhook.construct_event(payload, sig_header, secret)` | [Webhooks](https://docs.tryyoursize.com/guides/webhooks.md) | ## Errors **Node.js** ```ts try { await tys.products.retrieve("NO-SUCH-SKU"); } catch (err) { if (err instanceof TryYourSizeError) { console.error(err.code, err.docUrl, err.requestId); // "not_found", https://docs…/api/errors/#not_found, … } } ``` **Python** ```python try: tys.products.retrieve("NO-SUCH-SKU") except tryyoursize.APIError as e: print(e.code, e.doc_url, e.request_id) ``` ## Next - [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md): the shopper's size on your server. - [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md): measure size-related returns. - [Webhooks](https://docs.tryyoursize.com/guides/webhooks.md): `fit.recommended`, `garment.published` and more. --- # Shopify app (no code) > Install the TryYourSize app, sync your catalog, and add the Size guide block in the theme editor. No code. Source: https://docs.tryyoursize.com/quickstarts/shopify/ > **For your developer** > > **What it does:** Installs the TryYourSize Shopify app, which syncs our clothing products and adds a 'Size guide' block to the product page in the theme editor. No code. Shoppers' sizes (never measurements) are shown and can preselect the size option. > > **Time needed:** About 30 minutes in the Shopify admin, plus garment data in TryYourSize Studio. > > **What to send them:** No keys: the app creates and installs them. A staff account on the Shopify store with permission to install apps and edit the theme. > **Note** > > The app is in a private pilot (unlisted). Ask [sales@tryyoursize.com](mailto:sales@tryyoursize.com) for the install link. 1. **Install the app** from the link we send you, and open it in your Shopify admin. 2. **Set up, one click.** - New to TryYourSize: **Create a TryYourSize account**. You become the owner with your Shopify email (sign in to Studio with it). - Already a customer: in Studio → Integrations, **Connect Shopify** and enter your shop; then in the app, **Link to this store**. Setup verifies your `*.myshopify.com` host and primary domain (this counts as [domain verification](https://docs.tryyoursize.com/get-started/going-live.md#verify-your-domain)), registers them as allowed sites, and installs a publishable key in your theme. The store starts in **test mode**. 3. **Your catalog syncs.** Clothing products become TryYourSize products (the product ID is the Shopify **handle**), with their size option, a proposed **size map** ("Medium" → M), the category from Shopify's product taxonomy, a proposed fabric composition from the description, and proposed **fit groups** for colourways. Product changes in Shopify are synced automatically. 4. **Add garment data in Studio.** For each style, a designer enters or imports the garment: sizes, points of measure per size and fabric, then publishes it. Until a garment is published, live shoppers get no size for that product. 5. **Turn on the app embed.** Online Store → Themes → **Customize** → **App embeds** → turn on **TryYourSize** → Save. (The app's page has a direct link.) 6. **Add the Size guide block.** In the product template, **Product information** section → **Add block** → **Apps** → **Size guide**. Drag it next to the variant picker. Settings: **Style** (link or outlined button), **Alignment**, **Label**, and **Preselect the recommended size** (off by default). Save. 7. **Go live** in Studio when you are ready ([Going live](https://docs.tryyoursize.com/get-started/going-live.md)). The next time you open the app, it installs a **live** key in your theme, with no theme change. ## Preselecting the size With **Preselect the recommended size** on, the block selects the shopper's size in your size picker (Dawn-style pills and dropdowns are supported). It never overrides the shopper: not after they change the picker, not when the URL has `?variant=`, not twice per page, and never to a sold-out or disabled value. Otherwise the size is only shown. ## Orders and returns The recommended size goes to the cart line as the hidden line property `_tys_size` (size only, no shopper data), and the A/B visitor ID as `_tys_vid`. With your permission (optional `read_orders` / `read_returns` scopes), TryYourSize reads orders and returns to report size-related returns, and can import the last 60 days. See [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md). ## Languages The block's storefront text and editor labels are in English, Spanish, French, German and Italian. ## Uninstalling Uninstalling disconnects the store, removes the theme key and the domains verified through Shopify. Products and garments you authored in Studio stay in your TryYourSize account. --- # Size guide button > Replace your "Size guide" link with the TryYourSize size guide element, which shows "Your size: M" to connected shoppers. Source: https://docs.tryyoursize.com/quickstarts/size-guide/ > **For your developer** > > **What it does:** Puts a TryYourSize 'Size guide' link where our current size guide link is. Connected shoppers see 'Your size: M' on page load, and we can preselect that size. We receive sizes only. > > **Time needed:** About an hour once the web SDK is on the page. > > **What to send them:** The publishable key (pk_test_… first, pk_live_… to go live) and our site origins to register. The `sizeGuide` element is link-sized (32–34 px tall) and sits next to your size picker, where a "Size guide" link usually is. **JavaScript** ```html
Size
``` **HTML (no code)** ```html ``` **React** ```tsx ``` ## States "Size guide" (signed out) → "Calculating your size…" → "Your size: **M**" (connected, with a small dot on the mark), or "Size guide · connected" (connected, no size for this product yet). A click opens the full window. ## Label and language - The label follows `locale` (`"auto"`: your page's ``, then the browser): "Size guide", "Guía de tallas", "Guide des tailles", "Größentabelle", "Guida alle taglie". - `label` sets your own words. ## Look It renders in your page (Shadow DOM, no visible iframe), so it takes your font and text colour where you mount it. Override with `appearance.variables`: `colorText`, `fontFamily`, `colorPrimary` (the "connected" dot) and `borderRadius` (for `variant: "button"`). See [Appearance and theming](https://docs.tryyoursize.com/guides/appearance.md). ## Why it is safe in your page The TryYourSize session lives in our origin's storage, which your page cannot read. One invisible frame on our origin per page (a few KB, shared by every element) reads it and tells your page **sizes only**, and only when your page's origin is registered for the key. ## Preselect the size The size guide always reports the `recommendation` event, so you can preselect your size picker on page load. See [Recommended size and preselection](https://docs.tryyoursize.com/guides/recommendation-event.md). --- # Web SDK quickstart > Add the TryYourSize element to any product page with a script tag or npm. Publishable keys, events, appearance. Source: https://docs.tryyoursize.com/quickstarts/web/ > **For your developer** > > **What it does:** Adds a TryYourSize element to our product pages. Shoppers who have measured see their size in our own size labels; we can preselect it in the size picker. We receive sizes only, never measurements. > > **Time needed:** An afternoon for a first product page; less with the script tag. > > **What to send them:** The test publishable key (pk_test_…) from Studio → Developers → API keys, and the list of our site origins to register (Studio → Settings → Stores → Allowed sites). On Shopify there is nothing to code: see the [Shopify quickstart](https://docs.tryyoursize.com/quickstarts/shopify.md). 1. **Get your key.** In Studio → Developers → API keys, copy the **publishable** key (`pk_test_…` for now). Register the origin of your site (for example `https://staging.brand.com`) for test mode. `http://localhost` works in test mode without registering. 2. **Load TryYourSize.js.** **Script tag** ```html ``` The `/v1/` path always serves the latest compatible version 1. **npm** ```bash npm install @tryyoursize/sdk ``` ```ts import { TryYourSize } from "@tryyoursize/sdk"; // bundled into your app // or load the CDN build lazily, once (like loadStripe): import { loadTryYourSize } from "@tryyoursize/sdk/loader"; const tys = await loadTryYourSize("pk_test_…"); // null during SSR ``` 3. **Add the element** where shoppers choose a size. `product` is your product ID or SKU, as synced to TryYourSize. **HTML (no code)** ```html ``` **JavaScript** ```html
``` **React** ```tsx import { loadTryYourSize } from "@tryyoursize/sdk/loader"; import { TryYourSizeProvider, FitElement } from "@tryyoursize/react"; const tysPromise = loadTryYourSize("pk_test_…"); export function ProductPage() { return ( selectVariant(size)} /> ); } ``` See the [React quickstart](https://docs.tryyoursize.com/quickstarts/react.md). 4. **Allow our domains in your Content Security Policy** (if you have one): `script-src https://js.tryyoursize.com` (CDN build) and `frame-src https://embed.tryyoursize.com`. 5. **Try it.** Open your product page, sign in with an email code (or measure without an account), and pick a size. When everything works, follow [Going live](https://docs.tryyoursize.com/get-started/going-live.md). ## Elements | Element | What it is | |---|---| | `"fit"` | The inline element: sign-in and "your body is ready" right in your page. The measuring window opens only to measure. **Recommended.** | | `"button"` | A compact "Find my size" button that opens the measuring window. | | `"sizeGuide"` | A link-sized "Size guide" with the TryYourSize mark, for the spot next to your size picker. Shows "Your size: M" when the shopper is connected. See [Size guide button](https://docs.tryyoursize.com/quickstarts/size-guide.md). | No-code web components: `` and ``, which emit DOM events (`tys:size`). ## Element API | Method | | |---|---| | `mount(selectorOrElement)` | Shows the element. Throws `mount_not_found` or `already_mounted`. | | `on(event, handler)` / `once` / `off` | Events below. A handler that throws never breaks the SDK. | | `update({ product })` | Switch product (for example on a variant page) without recreating the element. | | `open()` / `close()` | Open or close the measuring window from your own UI. | | `getRecommendation()` | Promise of the shopper's recommended size for this product. | | `unmount()` / `destroy()` | Remove from the page; `destroy` also drops handlers. | | `elements.update({ appearance })` | Live restyle (for example your dark mode), without reloading anything. | | Event | Payload | |---|---| | `ready` | `{ elementType }`: shown and connected. | | `size` | `{ product, size, verified }`: the shopper chose a size in our window; preselect the variant. `verified` is true when the full simulation confirmed it. | | `open` / `close` | The measuring window opened or closed. Closing with unsaved changes asks the shopper first. | | `loaderror` | `{ error }`: our window could not be reached (network, blocked by CSP). | | `recommendation` | `{ product, status, size, studioSize, wearableSizes? }`: the shopper's recommended size in your labels. See [Recommended size and preselection](https://docs.tryyoursize.com/guides/recommendation-event.md). | ## Client methods | Method | | |---|---| | `tys.elements(options?)` | An elements group (`appearance`, `locale`). | | `tys.recommendation(product)` | Promise of the shopper's recommended size for a product, without mounting anything. See [Recommended size and preselection](https://docs.tryyoursize.com/guides/recommendation-event.md). | | `tys.recommendationHandle()` | Promise of `{ handle, expiresAt }` or `null`: a sealed handle your server sends to `POST /v1/recommendations`. See [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md). | | `tys.experiment()` | Promise of `{ experimentId, arm, visitorId }` or `null` when the store runs no [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md). | ## Errors All SDK errors are `TryYourSizeError` with `type` (`integration_error` or `load_error`), a stable `code`, `message` and `docUrl`. | Code | When | |---|---| | `secret_key_in_browser`, `invalid_key` | Wrong key passed to `TryYourSize()`. | | `mount_not_found`, `already_mounted`, `element_destroyed` | Element lifecycle misuse. | | `unknown_element_type`, `invalid_option` | Bad `create()` arguments. | | `window_unreachable` | Emitted with `loaderror` when our window does not connect. | | `script_load_failed` | `loadTryYourSize()` could not load the CDN script. | ## Security - Use **publishable keys only** in the browser. Each works only on the origins you register. - Our windows are iframes on `embed.tryyoursize.com`. Messages between your page and our window go through an exact origin allow-list on both sides, and every argument is validated. - The shopper's measurements never reach your page or your servers: only the size does. ## Demo mode (test keys only) For landing pages and sales demos, the `fit` element can show the try-on inline on a named **sample body**, with no sign-in. Sample body tape values are synthetic size-chart values, never a real person's. ```ts const tys = TryYourSize("pk_test_…"); // a live key throws const demo = tys.elements().create("fit", { product: "CREW-TSHIRT", demo: { body: "sample-female-165", height: 640 }, }); demo.mount("#demo"); demo.update({ product: "SLIM-STRETCH-JEANS" }); // another garment demo.update({ demo: { body: "sample-male-178" } }); // another body ``` Sample bodies: `sample-female-158`, `sample-female-165`, `sample-female-172`, `sample-male-172`, `sample-male-178`, `sample-male-186`. Demo mode shows precomputed results only and never starts a simulation on a page view; a body and product without a result says "Not simulated on this body yet". > **Note: Versioning** > > The CDN path pins the major version (`/v1/`): improvements reach you automatically, and breaking changes ship as `/v2/`. The npm package follows semantic versioning. --- # A/B and outcomes > Measure size-related returns with and without TryYourSize, with a holdout group and the orders and returns you send. Source: https://docs.tryyoursize.com/products/ab-outcomes/ TryYourSize's job is fewer size-related returns. To show it with your own numbers: 1. **A/B holdout.** In Studio, start an experiment: a share of visitors (10–50 %) sees no TryYourSize element at all. The SDK assigns each visitor with a random first-party ID; [`tys.experiment()`](https://docs.tryyoursize.com/reference/web/client-experiment.md) tells you the arm. 2. **Orders.** Your server sends each order with [`POST /v1/orders`](https://docs.tryyoursize.com/reference/node/orders-create.md), with the visitor ID saved with the cart. No customer data is accepted. 3. **Returns.** Send returns with [`POST /v1/returns`](https://docs.tryyoursize.com/reference/node/returns-create.md) and their reason (`size_too_small`, `size_too_large`, …). 4. **Results.** Studio → Results compares size-related return rates between the arms. Aggregates only. Shopify stores get orders and returns automatically from the app. - [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md): Set it up and keep the holdout clean. - [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md): What to send, from your server or as CSV. --- # Catalog API > Products, size maps, garment versions and fit groups from your PIM or ERP, with a secret or restricted key. Source: https://docs.tryyoursize.com/products/catalog-api/ Your team can describe garments in Studio, or you can sync them from your PIM, PLM or ERP with the Catalog API. The catalog is shared by test and live mode: test answers drafts, live answers published versions. | Resource | What it is | |---|---| | **Product** | Your SKU or style ID, name and category. Read by TryYourSize ID or by SKU. | | **Size map** | Studio size → your size label (`M` → `Medium`), so every answer uses your labels. | | **Garment version** | Sizes, points of measure per size (mm), grading and fabric. Drafts are edited; published versions never change. | | **Fit group** | One garment shared by several products, for example colourways and prints. | ```ts const draft = await tys.products.garments.createDraft("TEE-001", garment); await tys.garments.publish(draft.id); // 422 lists what is missing ``` - [Server quickstart](https://docs.tryyoursize.com/quickstarts/server.md): Install the Node.js or Python SDK and sync a product. - [HTTP API: Catalog](https://docs.tryyoursize.com/api/reference/operations/tags/catalog-api.md): Every catalog endpoint. --- # Fit view > The garment on the shopper's own 3D body, with ease in cm and contact pressure in kPa per zone, in our window. Source: https://docs.tryyoursize.com/products/fit-view/ **Fit view** is what the shopper sees after "See it on you": the garment on their own 3D body, built from their tape measurements (never from photos), in every size you make. For each size and zone it shows objective numbers rather than verdicts: | Number | Unit | What it says | |---|---|---| | Ease | cm | Room between body and garment, per zone (chest, waist, hip, thigh…) | | Contact pressure | kPa | How much the garment presses where it touches | | Fabric stretch | % | How far the fabric is stretched | | Wearable | yes / no | Whether the size can be put on and worn | The view lives in **our window** (`embed.tryyoursize.com`): framed in your page by the web SDK, or shown in a native sheet by the mobile SDKs. It inherits your store's appearance. Your page and your app receive the outcome only: the size the shopper chose (`size` event) and the recommended size. ## Open it from your code | SDK | Call | |---|---| | Web | [`element.open()`](https://docs.tryyoursize.com/reference/web/element-open.md) | | iOS / Android | [`present(product:)`](https://docs.tryyoursize.com/reference/ios/present.md) / [`present()`](https://docs.tryyoursize.com/reference/android/present.md) | | React Native / Flutter | [`present()`](https://docs.tryyoursize.com/reference/react-native/present.md) / [`present()`](https://docs.tryyoursize.com/reference/flutter/present.md) | - [Appearance and theming](https://docs.tryyoursize.com/guides/appearance.md): Make the window look like your store. - [How TryYourSize works](https://docs.tryyoursize.com/get-started/overview.md): From a tape measure to a size in your labels. --- # Recommendations API > The shopper's size for up to 25 products from your server or a headless storefront, from a sealed handle the browser SDK issues. Source: https://docs.tryyoursize.com/products/recommendations-api/ For server-rendered and headless storefronts, search results and carts: your server asks for the shopper's size for up to 25 products at once, in your size labels. 1. In the browser, [`tys.recommendationHandle()`](https://docs.tryyoursize.com/reference/web/client-recommendation-handle.md) returns an opaque, encrypted **handle** (valid for an hour). Your page cannot read anything in it. 2. Your page sends the handle to your server with its request. 3. Your server calls [`POST /v1/recommendations`](https://docs.tryyoursize.com/api/reference/operations/createrecommendations.md) with a secret or restricted key (`fit_checks:read`). ```ts const { data } = await tys.recommendations.create({ handle, products: ["SKU-123", "SKU-456"] }); ``` Each answer is `ready` (with `size` and `wearable_sizes`), `computing`, `none` or `unavailable`. The call never starts a simulation and never returns measurements. - [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md): The full guide, with caching and errors. - [Node.js reference](https://docs.tryyoursize.com/reference/node/recommendations-create.md): tys.recommendations.create() - [Python reference](https://docs.tryyoursize.com/reference/python/recommendations-create.md): tys.recommendations.create() --- # Size guide > A "Size guide" link next to your size picker that becomes "Your size: M" for connected shoppers, on the web, in apps and on Shopify. Source: https://docs.tryyoursize.com/products/size-guide/ The **size guide** is the smallest way to add TryYourSize: a link-sized "Size guide" with the TryYourSize mark, placed where your store already has its size chart link. It renders in your page (no iframe to paint) and takes your store's own font and colour. | Shopper | What they see | |---|---| | Not connected | "Size guide". A tap opens our window: sign in with an email code, or measure with a tape measure. | | Connected, size known | "Your size: **M**", in your size labels. | | Connected, try-on running | "Calculating your size…" | | Connected, no size can be worn | "No size fits you" | Your code gets the same information as a `recommendation` event, so you can **preselect** your size picker. You receive sizes only, never measurements. ## Where it runs | Surface | How | |---|---| | Any website | `elements.create("sizeGuide", …)` or `` ([Web SDK](https://docs.tryyoursize.com/reference/web/elements-create.md)) | | React | [``](https://docs.tryyoursize.com/reference/react/size-guide.md) | | iOS | [`SizeGuide`](https://docs.tryyoursize.com/reference/ios/size-guide.md) (SwiftUI) and [`SizeGuideButton`](https://docs.tryyoursize.com/reference/ios/size-guide-button.md) (UIKit) | | Android | [`SizeGuide`](https://docs.tryyoursize.com/reference/android/size-guide.md) (Compose) and [`SizeGuideView`](https://docs.tryyoursize.com/reference/android/size-guide-view.md) | | React Native, Flutter | [``](https://docs.tryyoursize.com/reference/react-native/size-guide.md), [`SizeGuide`](https://docs.tryyoursize.com/reference/flutter/size-guide.md) | | Shopify | The "Size guide" theme block ([no code](https://docs.tryyoursize.com/quickstarts/shopify.md)) | - [Size guide quickstart](https://docs.tryyoursize.com/quickstarts/size-guide.md): Add it next to your size picker in ten minutes. - [Preselect the recommended size](https://docs.tryyoursize.com/guides/recommendation-event.md): Use the recommendation without overriding the shopper's choice. --- # Studio > TryYourSize Studio, where your team describes garments, maps sizes, manages keys, origins and webhooks, and reads results. Source: https://docs.tryyoursize.com/products/studio/ **Studio** (`studio.tryyoursize.com`) is where your brand team works. Developers mostly need four places: | Where | What you do there | |---|---| | **Developers → API keys** | Publishable, secret and restricted keys for test and live mode; roll a key. | | **Settings → Stores → Allowed sites** | The origins (and app identities, `ios-app://…`, `android-app://…`) each key works on. | | **Developers → Webhooks** | Endpoints, signing secrets, test events and delivery logs. | | **Developers → Logs** | Your API requests, by request ID (`X-Request-ID`). | Your team describes each garment there (sizes, points of measure per size, grading, fabric), checks it, and maps Studio sizes to your size labels. Everything Studio does to the catalog, the [Catalog API](https://docs.tryyoursize.com/products/catalog-api.md) can do too. - [Keys and modes](https://docs.tryyoursize.com/get-started/keys-and-modes.md): Which key goes where. - [Going live](https://docs.tryyoursize.com/get-started/going-live.md): The checklist before live mode. --- # Webhooks > Signed events to your server when a size is recommended, a garment is published or a shopper disconnects. Source: https://docs.tryyoursize.com/products/webhooks/ Register an HTTPS endpoint in Studio → Developers → Webhooks and receive signed events. Payloads never carry shopper identity or measurements. | Event | When | |---|---| | `fit.recommended` | A shopper got a size for one of your products | | `garment.published` | A garment version was published (Studio or API) | | `fit_check.completed` | A fit check finished | | `profile.disconnected` | A shopper disconnected your store | | `usage.threshold_reached` | Coming soon: your usage reached a threshold of your plan | | `webhook.test` | You sent a test event from Studio | Every delivery has a `TYS-Signature` header (`t=,v1=`). Verify it against the **raw** body: ```ts const event = tys.webhooks.constructEvent(rawBody, req.headers["tys-signature"], process.env.TYS_WEBHOOK_SECRET!); ``` - [Webhooks guide](https://docs.tryyoursize.com/guides/webhooks.md): Envelope, verification in any language, retries, rolling the secret. - [constructEvent() (Node.js)](https://docs.tryyoursize.com/reference/node/webhooks-construct-event.md) - [Webhook.construct_event() (Python)](https://docs.tryyoursize.com/reference/python/webhook-construct-event.md) --- # A/B holdout > Measure TryYourSize's effect on size-related returns. A share of visitors sees no element; the holdout is never metered. Source: https://docs.tryyoursize.com/guides/ab-holdout/ An A/B holdout shows you, with your own orders, how size-related returns compare **with and without** TryYourSize. A share of your visitors (the **holdout**) sees no TryYourSize element at all; everyone else (the **treatment**) sees it as usual. ## Start an experiment In **Studio → Results**, start an experiment and choose the holdout share: **10–50 %** of visitors. One experiment runs per store at a time. Stopping it shows TryYourSize to everyone again; the results stay. Nothing changes in your code: TryYourSize.js reads the running experiment and applies it. ## What the SDK does - **Assignment:** a random anonymous visitor ID in your site's own `localStorage` (`tys_vid`). It is first-party to your shop, not shared across shops, not a cookie on our domain, and never linked to a shopper account. The arm is a deterministic hash of the experiment's salt and that ID, so a visitor stays in the same arm. - **Holdout:** nothing is rendered: no element, no status frame, no window. `recommendation()` and `getRecommendation()` answer `unavailable` with no size, and `recommendationHandle()` answers `null`, so your own preselection and your server do not leak TryYourSize into the holdout. `ready` never fires. - **No experiment, or our API unreachable:** everyone sees TryYourSize (such visitors are not counted in either arm). - Demo mode is never part of an experiment. ## Send the visitor ID with orders To attribute an order to its arm, save the visitor ID with the cart and send it with the order: ```ts const assignment = await tys.experiment(); // { experimentId, arm, visitorId } | null if (assignment) saveToCart("tys_vid", assignment.visitorId); ``` Then send it as `visitor_id` in [`POST /v1/orders`](https://docs.tryyoursize.com/guides/outcomes.md). TryYourSize recomputes the arm from the ID; it stores only a hash of it. If you cannot send the ID, send `experiment_arm` (`treatment` or `holdout`). On Shopify, the theme block writes the ID as the hidden line property `_tys_vid` for you. ## Billing The holdout renders nothing, so it never creates a fit check, a try-on or a window session: **holdout traffic is never metered**. ## Reading the results Studio → Results shows the size-related return rate per arm with 95 % intervals, the difference and whether it is significant, the sample still needed when there is not enough data yet, and a warning when the observed split does not match the configured one. "Followed the recommendation vs not" is shown too, labelled as observational. The methods are written on the page and in the CSV export. --- # Use TryYourSize with AI tools > Point Cursor, Claude Code, ChatGPT or Claude at our docs with llms.txt and Markdown pages, use ready-made integration prompts, and drop a rules file into your repo. Source: https://docs.tryyoursize.com/guides/ai-tools/ Every page of these docs is also available as clean Markdown, so an AI assistant can read exactly what you read, including the full SDK and HTTP API reference. | URL | What it is | |---|---| | [`/llms.txt`](https://docs.tryyoursize.com/llms.txt) | Index in the [llms.txt](https://llmstxt.org) format: every page, as a link to its Markdown | | [`/llms-full.txt`](https://docs.tryyoursize.com/llms-full.txt) | All the docs in one Markdown file: guides, quickstarts, SDK reference and HTTP API reference | | `/llms/.txt` | One SDK's whole reference: [`web`](https://docs.tryyoursize.com/llms/web.txt), [`react`](https://docs.tryyoursize.com/llms/react.txt), [`node`](https://docs.tryyoursize.com/llms/node.txt), [`python`](https://docs.tryyoursize.com/llms/python.txt), [`ios`](https://docs.tryyoursize.com/llms/ios.txt), [`android`](https://docs.tryyoursize.com/llms/android.txt), [`react-native`](https://docs.tryyoursize.com/llms/react-native.txt), [`flutter`](https://docs.tryyoursize.com/llms/flutter.txt) | | `.md` | Any page as Markdown: add `.md` to its URL, for example [`/quickstarts/web.md`](https://docs.tryyoursize.com/quickstarts/web.md) | | [`/openapi.json`](https://docs.tryyoursize.com/openapi.json) | The public OpenAPI spec | On every page, **Copy page** copies its Markdown, and the menu next to it opens the page in ChatGPT or Claude with a prompt that names only the page's public URL. ## Cursor Add the docs once as a source: **Settings → Indexing & Docs → Add doc**, with `https://docs.tryyoursize.com/llms-full.txt`. Then mention `@TryYourSize` in chat. For a project rule, save the [rules file](#rules-file-for-your-repo) as `.cursor/rules/tryyoursize.mdc`. ## Claude Code Add the [rules file](#rules-file-for-your-repo) to your repo's `CLAUDE.md`, or ask directly: ```text Read https://docs.tryyoursize.com/llms.txt, then the pages you need, and add the TryYourSize size guide to our product page. ``` Claude Code fetches the Markdown pages it needs from the index. ## ChatGPT and Claude Use **Open in ChatGPT** or **Open in Claude** in the page menu, or paste one of the prompts below. For long tasks, attach `llms-full.txt`. ## Ready-made prompts Replace the values in angle brackets. Each prompt asks the assistant to read our docs first, so it uses only real APIs. ### Shopify (no code) ```text Read https://docs.tryyoursize.com/quickstarts/shopify.md. Walk me through installing the TryYourSize app on my Shopify store, adding the "Size guide" block next to the size picker on the product template of my theme , and checking that orders reach TryYourSize. I don't write code; give me clicks, not code. ``` ### React (Next.js, Remix, Vite) ```text Read https://docs.tryyoursize.com/quickstarts/react.md and https://docs.tryyoursize.com/llms/react.txt. In this repo, add the TryYourSize size guide next to the size picker of the product page (): - load the client once with loadTryYourSize("") from @tryyoursize/sdk/loader and wrap the page in ; - render where the "Size guide" link is today; - preselect the size from onRecommendation when status is "ready", but never override a size the shopper picked; - keep it SSR-safe; use only APIs that exist in the docs. ``` ### iOS (SwiftUI) ```text Read https://docs.tryyoursize.com/quickstarts/ios.md and https://docs.tryyoursize.com/llms/ios.txt. In this app, create one TryYourSize client with try TryYourSize(publishableKey: ""), and put SizeGuide(client:product:) next to the size picker in . Preselect the size from the onRecommendation closure when rec.status == .ready, without overriding the shopper's own choice. Tell me which app origin (ios-app://…) to register in Studio. Use only APIs that exist in the docs. ``` ### Server (Node.js or Python) ```text Read https://docs.tryyoursize.com/quickstarts/server.md and https://docs.tryyoursize.com/guides/outcomes.md. Add a job to that sends every order and return to TryYourSize with @tryyoursize/node (or the tryyoursize Python package), with the secret key from the environment variable TRYYOURSIZE_SECRET_KEY, idempotent by order ID, including visitor_id from the cart. Send no customer data. ``` ## Rules file for your repo Save it as `.cursor/rules/tryyoursize.mdc` (Cursor), add it to `CLAUDE.md` (Claude Code), `AGENTS.md`, or `.github/copilot-instructions.md` (GitHub Copilot). ```markdown # TryYourSize integration rules - Docs: https://docs.tryyoursize.com/llms.txt (index) and https://docs.tryyoursize.com/llms-full.txt (everything). Read the relevant page before writing TryYourSize code. Use only methods, props and endpoints that exist there. - Keys: publishable keys (pk_test_… / pk_live_…) in browsers and apps only. Secret (sk_…) and restricted (rk_…) keys on the server only, from environment variables. Never commit keys. - The store receives sizes only (in its own size labels), never measurements or body data. Do not try to read, store or log shopper measurements. - Preselect the recommended size only when status === "ready", and never override a size the shopper chose. - Web: allow script-src https://js.tryyoursize.com and frame-src https://embed.tryyoursize.com in the CSP. - Apps: register ios-app:// / android-app:// for the key (test mode). - Server: send orders and returns without customer data; make POSTs idempotent (the SDKs do it); verify webhook signatures on the raw body. - Describe fit with numbers (cm of ease, kPa of pressure), not with verdict words. ``` > **Note** > > The docs describe what TryYourSize does and how to integrate it. Assistants do not get, and should not guess, anything about how sizes are computed. --- # Appearance and theming > TryYourSize elements inherit your page's font and colours by default. Override them with appearance variables, live. Source: https://docs.tryyoursize.com/guides/appearance/ By default every element **inherits your page**: the font, text colour and background where it is mounted, with buttons in your text colour (like most shops' main button). You usually need no styling at all. ## Appearance API Like Stripe's Appearance API, pass `appearance` when you create the elements group: ```ts const elements = tys.elements({ appearance: { theme: "inherit", // default; or "tryyoursize" (our light beige brand) variables: { colorPrimary: "#111", colorBackground: "#fff", colorText: "#111", fontFamily: "Inter, sans-serif", borderRadius: 8, }, fonts: [{ cssSrc: "https://fonts.googleapis.com/css2?family=Inter:wght@400;600" }], // Google Fonts only }, locale: "auto", }); ``` | Variable | Used for | |---|---| | `colorPrimary` | Buttons and accents; the "connected" dot on the size guide | | `colorBackground` | Element and window background | | `colorText` | Text | | `fontFamily` | Text in the element and the window | | `borderRadius` | Corners (px); the size guide uses it for `variant: "button"` | The TryYourSize mark always stays on the element, so shoppers recognise that the same body works in every store. ## Live updates Restyle without reloading anything, for example when your site switches to dark mode: ```ts const dark = matchMedia("(prefers-color-scheme: dark)"); const apply = () => elements.update({ appearance: { variables: dark.matches ? { colorBackground: "#111", colorText: "#eee" } : {} }, }); dark.addEventListener("change", apply); ``` In React, change `options.appearance` on ``: the provider calls `elements.update()` for you. ## Mobile The native SDKs use the same variables: | Variable | Web | iOS | Android | |---|---|---|---| | `theme` | `inherit` / `tryyoursize` | `.inherit` / `.tryyoursize` | `INHERIT` / `TRYYOURSIZE` | | inherited text / background | computed style where mounted | `UIColor.label` / `.systemBackground` for the presenter (dark mode included) | theme `textColorPrimary` / `colorBackground` | | `colorPrimary` | CSS colour | `TYSColor` (hex or `UIColor`) | ARGB `Int` | | `fontFamily` | page font | CSS family for the window (default `-apple-system`); the button uses the SwiftUI environment font | CSS family (default `Roboto`); the button takes a `TextStyle` | | `borderRadius` | px | points; also the sheet's corner radius | dp | | `fonts` | Google Fonts `cssSrc` | `[URL]`, Google Fonts only | `List`, Google Fonts only | Live: `tys.appearance = …` (iOS, Android) or `setAppearance(...)` (Flutter) restyles an open window at once. React Native takes `#rrggbb` or `#rrggbbaa` colours in `configure()`. --- # Languages (i18n) > The element, the measuring window and the mobile buttons in English, Spanish, French, German and Italian. Choose with the locale option or let your page's html lang decide. Source: https://docs.tryyoursize.com/guides/i18n/ ## Languages | Language | Element and window | Size guide label | |---|---|---| | English (`en`) | Complete (the source) | "Size guide" | | Spanish (`es`) | Complete (Spain, neutral enough for Latin America) | "Guía de tallas" | | French (`fr`) | Complete | "Guide des tailles" | | German (`de`) | Complete | "Größentabelle" | | Italian (`it`) | Complete | "Guida alle taglie" | The inline element, the measuring window (sign-in, measuring, the 3D try-on view), the size guide label and the native buttons are available in all five languages. A language we do not have falls back to English. ## Choosing the language Web SDK: the `locale` option, on the client or on `elements()`. ```ts const tys = TryYourSize("pk_live_…", { locale: "es" }); // or: tys.elements({ locale: "auto" }) ``` The window uses, in this order: 1. the `locale` option, when you set a language; 2. with `"auto"` (the default), your page's ``; 3. the shopper's browser languages; 4. English. So a shop whose pages already declare `` needs no option at all. A region is kept for number and date formatting (`es-MX`, `fr-CA`). - **React:** `options.locale` on `` (read once, when the client is created). - **iOS, Android, React Native, Flutter:** the `locale` setting of the client; without it, the device language. - **Shopify app:** the block follows the storefront's current language (Shopify's `request.locale`). To check a language, open the window with `?locale=fr` (for example in our Body app at `my.tryyoursize.com/?locale=fr`). ## What is translated - Everything the shopper reads in the element and the window: sign-in, measuring instructions, the try-on view, errors. - Units and numbers are formatted per language: ease in cm, pressure in kPa, stretch in % (`2,1 cm`, `18 %` in Spanish, French, German and Italian). - **Your** product names and size labels are shown as you wrote them. - Fit is described with objective numbers in every language (ease, pressure, stretch), never with subjective judgements. - Shoppers are addressed as is usual for fashion retail in each language: *tú* in Spanish, *vous* in French, *du* in German, *tu* in Italian. --- # Idempotency and retries > Retry POST requests safely with an Idempotency-Key. Stored 24 hours per store and mode. Source: https://docs.tryyoursize.com/guides/idempotency/ Networks fail. To retry a `POST` without doing it twice, send an `Idempotency-Key` header with a unique value per logical request (a UUID v4 is ideal; 1–255 printable ASCII characters). ```bash curl https://api.tryyoursize.com/v1/products \ -H "Authorization: Bearer $TYS_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Idempotency-Key: 6f1c1f0e-7a8b-4c1e-9d6f-0b3a2e5c4d11" \ -H "Content-Type: application/json" \ -d '{"external_id":"CREW-TSHIRT","name":"Crew T-shirt","category":"tshirt"}' ``` The SDKs take it as an option: `{ idempotencyKey }` (Node) or `idempotency_key=` (Python). ## How it behaves | Situation | Answer | |---|---| | First request | Runs normally. The response is stored for **24 hours**, per store and mode (secret and restricted keys). | | Retry with the same key and the same body | The stored response, with the header `Idempotent-Replayed: true`. | | Retry while the first request is still running | `409` [`idempotency_key_in_use`](https://docs.tryyoursize.com/api/errors.md#idempotency_key_in_use): wait and retry. | | Same key, different body | `422` [`idempotency_key_reused`](https://docs.tryyoursize.com/api/errors.md#idempotency_key_reused): use a new key for a new request. | | Malformed key | `400` [`invalid_idempotency_key`](https://docs.tryyoursize.com/api/errors.md#invalid_idempotency_key). | | The first response was a 5xx | Not stored: a retry runs again. | ## Naturally idempotent endpoints `POST /v1/orders` is idempotent by your order ID and `POST /v1/returns` by (line, return ID): sending the same order again updates it. An `Idempotency-Key` still protects against duplicate work during a retry. ## When to retry - **Retry** on network errors, timeouts, `409 idempotency_key_in_use`, `429` (after `Retry-After`) and `5xx`, with exponential backoff and the **same** key. - **Do not retry** other `4xx` answers unchanged: fix the request first. --- # Orders and returns (outcomes) > Send orders and returns so TryYourSize can report size-related returns per A/B arm. No customer data. Source: https://docs.tryyoursize.com/guides/outcomes/ Orders and returns power the size-return report in **Studio → Results** and the [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md). Send them from your server, upload a CSV, or let the Shopify app read them. > **Note: No customer data** > > There is no field for a customer ID, name, email, phone or address, and unknown fields are refused (`400`), so a stray `email` is rejected rather than stored. Free-text return notes are never accepted. ## Orders: `POST /v1/orders` Secret key, or a restricted key with `orders:write`. Idempotent by your order ID: sending the same `id` again updates the order (`200`); a new order is `201`. **Node.js** ```ts await tys.orders.create({ id: "1001", ordered_at: "2026-10-25T10:12:00Z", currency: "EUR", visitor_id: cart.tysVisitorId, // from tys.experiment(), for the A/B arm lines: [ { id: "1001-1", product: "STRAIGHT-JEANS", // the ID you pass to the SDK size: "32", // size bought, in your labels quantity: 1, unit_price_minor: 8900, // optional, for the savings estimate tys_shown: true, recommended_size: "32", // from the recommendation event or _tys_size attribution: "sdk", }, ], }); ``` **Python** ```python tys.orders.create( id="1001", ordered_at="2026-10-25T10:12:00Z", currency="EUR", visitor_id=cart["tys_visitor_id"], lines=[{ "id": "1001-1", "product": "STRAIGHT-JEANS", "size": "32", "quantity": 1, "tys_shown": True, "recommended_size": "32", "attribution": "sdk", }], ) ``` | Line field | | |---|---| | `id`, `product`, `size`, `quantity` | Required. `size` is the size bought, in your labels. | | `variant`, `unit_price_minor` | Optional. Price in minor units (`5995` = 59.95). | | `tys_shown` | A TryYourSize element was shown for this purchase. | | `recommended_size` | The size TryYourSize recommended (the SDK's `recommendation` or `size` event, the server API, or the `_tys_size` cart line property). Whether the shopper followed it ("Medium" = "M") is computed for you. | | `attribution` | Where `recommended_size` came from: `cart_attribute`, `sdk` or `api`. | Order-level: `id`, `ordered_at`, `lines` (required), `currency`, and `visitor_id` or `experiment_arm` for the A/B arm. The `tryon_id` from [server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md) identifies the try-on behind a size; keep it with your order line for your own records. ## Returns: `POST /v1/returns` Secret key, or a restricted key with `returns:write` (for example a returns platform). Idempotent by (line, return ID). The order must have been sent first (`404` otherwise). ```json { "id": "RMA-5521", "order_id": "1001", "line_id": "1001-1", "quantity": 1, "reason": "size_too_small", "exchange_size": "33", "returned_at": "2026-11-02T09:00:00Z" } ``` ### Return-reason taxonomy | Code | Size-related | |---|---| | `size_too_small` | yes | | `size_too_large` | yes | | `fit_other` (a zone, the length, "doesn't fit") | yes | | `style` | no | | `quality` | no | | `damaged_or_wrong` | no | | `changed_mind` | no | | `other` | no | | empty: `unspecified` | no | Shopify's return reasons (`SIZE_TOO_SMALL`, `UNWANTED`, `DEFECTIVE`…) and common returns-platform words are mapped for you. `exchange: true` (or an `exchange_size`) marks an exchange instead of a refund. ## CSV import No API integration? Upload CSV files in **Studio → Results → Import**. - **Orders**, one row per line: `order_id, ordered_at, line_id, product, size, quantity` (optional: `currency, unit_price, variant, recommended_size, tys_shown, experiment_arm, visitor_id`). - **Returns**: `order_id, line_id, return_id, quantity, returned_at` (optional: `reason, exchange, exchange_size`). - Up to 10 MB or 50,000 rows. Rows with errors are skipped and listed by row number. - **A file with a personal-data column** (`email`, `name`, `phone`, `address`, `customer…`, `ip`, `birth`…) **is refused as a whole.** ## Shopify With the [Shopify app](https://docs.tryyoursize.com/quickstarts/shopify.md), grant the optional `read_orders` and `read_returns` permissions and TryYourSize reads orders, refunds and returns as they happen, and can import the last 1–60 days (Studio → Results → Import from Shopify). Only the fields the report needs are read: line items, the `_tys_size` and `_tys_vid` line properties, refunds and returns with their reasons. Never customer, address, email or note fields. Test orders, and orders of a store that is not live yet, are recorded in test mode. ## Attribution summary | Source | Field | |---|---| | SDK `recommendation` / `size` event, or server-side recommendation | `recommended_size` with `attribution: "sdk"` or `"api"` | | Shopify cart line property `_tys_size` | `recommended_size` with `attribution: "cart_attribute"` (automatic) | | A/B arm | `visitor_id` (from `tys.experiment()`, or `_tys_vid` on Shopify), else `experiment_arm` | --- # Pagination > Cursor pagination for lists. limit and cursor in, data, has_more and next_cursor out. Source: https://docs.tryyoursize.com/guides/pagination/ List endpoints (`GET /v1/products`, `GET /v1/fit_groups`, `GET /v1/audit_events`…) return one page at a time, newest first. | Parameter | | |---|---| | `limit` | Page size: default **20**, at most **100**. | | `cursor` | The `next_cursor` of the previous page. Omit it for the first page. | ```json { "data": [ { "id": "…", "external_id": "CREW-TSHIRT" } ], "has_more": true, "next_cursor": "eyJ…" } ``` Request the next page while `has_more` is `true`. Cursors are opaque: do not build or parse them. ## With the SDKs ```ts // Node: auto-pagination for await (const p of tys.products.list({ limit: 50 })) { console.log(p.external_id); } ``` ```python # Python for p in tys.products.list(limit=50).auto_paging_iter(): print(p["external_id"]) ``` --- # Privacy: what your store receives > Your store receives sizes only. Never measurements, body data, email or a shopper account. Source: https://docs.tryyoursize.com/guides/privacy/ TryYourSize is built so that a store **never needs the shopper's body data** to sell them the right size. ## What your store receives | You receive | You never receive | |---|---| | The recommended size, in your labels | Tape measurements | | The sizes that are wearable on this body | The 3D body or any body data | | Whether the full simulation confirmed the size | The shopper's email, name or TryYourSize account | | Status values (`ready`, `computing`, `unavailable`…) | Anything about the shopper's other stores | | In webhooks: the try-on ID, product SKU and recommended size | A shopper identifier in webhooks | This holds for every surface: the browser and mobile SDKs, the [server API](https://docs.tryyoursize.com/guides/server-side-recommendations.md) (its handle is sealed and unreadable to you; raw measurements are never accepted or returned), [webhooks](https://docs.tryyoursize.com/guides/webhooks.md) and the Shopify app (the `_tys_size` line property holds a size only). ## How it is enforced - The shopper's session lives in our origin's storage, which your page cannot read. Our invisible status frame answers your page with sizes only, and only for origins registered for your key. - On mobile, the bridge between our window and your app only ever carries sizes, and both sides validate every message. - The server API's recommendation handle is encrypted: neither your page nor your server can read it. ## Shoppers are in control The shopper's measurements and body belong to them. They can see which stores they are connected to, disconnect a store, download their data or delete their account in their TryYourSize account at any time. A disconnected store gets no more sizes for that shopper (handles are refused with [`handle_invalid`](https://docs.tryyoursize.com/api/errors.md#handle_invalid)), and you receive a `profile.disconnected` webhook. ## Outcomes and A/B data Orders and returns you send carry no customer data: the API and CSV import refuse personal-data fields. The A/B visitor ID is a random first-party ID in your site's storage, stored by us only as a store-scoped hash. List it in your cookie and storage notice as a functional analytics identifier. Results are aggregates only. See [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md). ## Roles For orders, returns and analytics, TryYourSize processes data as your **processor** under the Data Processing Agreement you accept when you go live. --- # Rate limits > X-RateLimit headers on every response, 429 with Retry-After when you go over. Source: https://docs.tryyoursize.com/guides/rate-limits/ Every API response carries: | Header | | |---|---| | `X-RateLimit-Limit` | Requests allowed in the current window. | | `X-RateLimit-Remaining` | Requests left in the window. | | `X-RateLimit-Reset` | When the window resets. | Over the limit, the API answers `429` with code [`rate_limited`](https://docs.tryyoursize.com/api/errors.md#rate_limited) and a `Retry-After` header (seconds). Wait at least that long, then retry with backoff. With an `Idempotency-Key`, a retried `POST` is safe. Today limits apply **per client IP address**. Limits **per key and per plan** are coming; the headers stay the same, so code that reads them keeps working. ## Good practice - Read `X-RateLimit-Remaining` and slow down before you reach zero, for example during a full catalog sync. - Spread bulk work over time, use `limit=100` on lists, and send up to 25 products per [`POST /v1/recommendations`](https://docs.tryyoursize.com/guides/server-side-recommendations.md). - Never retry in a tight loop. --- # Preselect the recommended size > Get the connected shopper's size on page load, in your own size labels, and preselect your size picker. Source: https://docs.tryyoursize.com/guides/recommendation-event/ When the shopper is already connected to TryYourSize on your site, you get their size **on page load**, in **your size labels** (the product's size map, for example Studio "M" → your "Medium"), without them opening anything. ## Preselect your size picker ```ts const guide = elements.create("sizeGuide", { product: "SKU-123" }); guide.on("recommendation", ({ status, size }) => { if (status !== "ready") return; const select = document.querySelector("#size")!; const option = [...select.options].find((o) => o.value === size && !o.disabled); if (option && !select.dataset.touched) { // never override the shopper's own choice select.value = size; select.dispatchEvent(new Event("change", { bubbles: true })); document.querySelector("#size-note")!.textContent = "Recommended by TryYourSize"; } }); guide.mount("#tys-guide"); ``` Without mounting anything: ```ts const rec = await tys.recommendation("SKU-123"); // or from any mounted element: const rec2 = await fit.getRecommendation(); ``` ## The payload | Field | | |---|---| | `product` | Your product ID. | | `status` | `ready` (a size), `computing` (being simulated on the shopper's body), `none` (simulated, no size is wearable), `signed_out` (no TryYourSize session on your site), `unavailable` (connected, but no size for this product yet: no garment data, no body yet, not simulated on this body yet, or the simulation failed). | | `size` | The recommended size in **your** label, or `null`. | | `studioSize` | The same size as entered in TryYourSize Studio, or `null`. | | `wearableSizes` | Sizes that are wearable on this body, in your labels (when simulated). | It fires on mount and on every change (signing in, a new body, a simulation finishing). It never carries measurements, the email or body data. The `sizeGuide` element always reports it; `fit` and `button` start reporting once you listen to `recommendation`. ## Good practice - **Never override the shopper.** Preselect only until they touch the picker, and never to a sold-out or disabled size. - **Say where it came from**, for example "Recommended by TryYourSize" next to the picker. - **Save the size with the cart line** (for example a hidden line property) and send it as `recommended_size` with the order: see [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md). ## Cost A page view never starts a simulation. A body already simulated with this garment answers at once, and many garments answer in under a second; otherwise `status` is `unavailable` until the shopper asks for it ("See it on you" in our window), then `computing`, then `ready`. A delivered size is a [fit check](https://docs.tryyoursize.com/get-started/going-live.md#billing). ## On your server For headless storefronts, or to use the size in server-rendered pages and emails, see [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md). --- # Server-side recommendations > Get the shopper's size on your server with a sealed handle from the browser. Sizes only; measurements never leave TryYourSize. Source: https://docs.tryyoursize.com/guides/server-side-recommendations/ Use this when your server needs the shopper's size: a headless storefront, server-rendered product pages, a size shown in a cart email, or your own recommendation logic. ## How it works 1. **In the browser**, TryYourSize.js asks our origin for a **recommendation handle** for the signed-in shopper. ```ts const tys = TryYourSize("pk_live_…"); const h = await tys.recommendationHandle(); // { handle, expiresAt } | null ``` `null` means the shopper is not signed in to TryYourSize on this site, has no body yet, or is in your [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md). `expiresAt` is an ISO date-time one hour ahead. In React, call it on the client from `useTryYourSize()`. 2. **Your page sends `h.handle` to your own server**, like any other value (a form field, a fetch to your API). 3. **Your server** calls `POST /v1/recommendations` with a secret key (or a restricted key with `fit_checks:read`) and up to 25 products. 4. **The answer** is one entry per product, in the order asked: sizes in your labels, never measurements. **Node.js** ```ts const tys = new TryYourSize(process.env.TYS_SECRET_KEY!); const { mode, data } = await tys.recommendations.create({ handle: req.body.tysHandle, products: ["CREW-TSHIRT", "STRAIGHT-JEANS"], }); for (const r of data) { if (r.status === "ready") console.log(r.product, r.size); // "CREW-TSHIRT", "Medium" } ``` **Python** ```python tys = tryyoursize.TryYourSize(os.environ["TYS_SECRET_KEY"]) res = tys.recommendations.create( handle=request.form["tys_handle"], products=["CREW-TSHIRT", "STRAIGHT-JEANS"], ) for r in res["data"]: if r["status"] == "ready": print(r["product"], r["size"]) ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/recommendations \ -H "Authorization: Bearer $TYS_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"handle":"tys_rh_…","products":["CREW-TSHIRT","STRAIGHT-JEANS"]}' ``` ## The answer ```json { "mode": "live", "data": [ { "product": "CREW-TSHIRT", "status": "ready", "size": "Medium", "studio_size": "M", "wearable_sizes": ["Medium", "Large"] }, { "product": "STRAIGHT-JEANS", "status": "unavailable", "size": null, "studio_size": null, "wearable_sizes": [] } ] } ``` | Field | | |---|---| | `status` | `ready` (a size), `computing` (a simulation the shopper started is running: ask again later), `none` (simulated, no size is wearable), `unavailable` (no published garment, not simulated on this body yet, or the plan has no fit checks left). There is no `signed_out`: a handle always belongs to a signed-in shopper. | | `size` | The recommended size in your label (the product's size map), or `null`. | | `studio_size` | The same size as entered in Studio, or `null`. | | `wearable_sizes` | Sizes wearable on this body, in your labels. | | `method` | A coarse, informational label of how the size was obtained. Do not build logic on its values. | | `tryon_id` | The try-on that produced the size, when there is one. Send it back with orders for attribution. | ## Rules - **It never starts a simulation.** Like a page view, it answers only with a size that is already available (many garments answer in under a second); otherwise the status is `unavailable` until the shopper asks for the try-on in our window. - **A delivered size counts as a fit check**, exactly as in the browser (once per shopper and product per 30 days, live mode only). - **Raw measurements are never accepted or returned** by the server API. ## The handle - It starts with `tys_rh_` and is up to 4 KB. Treat it as opaque. - It is **encrypted and sealed** to one store, one mode and one shopper. Neither your page nor your server can read anything from it; it never carries the shopper's email. - It **expires after 1 hour**. After that the API answers `401` with code [`handle_expired`](https://docs.tryyoursize.com/api/errors.md#handle_expired): ask the browser for a new one. - It is refused with `401` [`handle_invalid`](https://docs.tryyoursize.com/api/errors.md#handle_invalid) after the shopper deletes their account or disconnects your store, and when it was issued for another store. - Its **mode must match your key**: a test handle with a live key (or the reverse) is `403` [`handle_mode_mismatch`](https://docs.tryyoursize.com/api/errors.md#handle_mode_mismatch). > **Tip** > > Ask for a new handle on each page load rather than storing one: it is cheap, and a fresh handle reflects the shopper's current consent. --- # Troubleshooting > Common symptoms and error codes when integrating TryYourSize, with their cause and fix. Search this page for the code you see. Source: https://docs.tryyoursize.com/guides/troubleshooting/ Search this page (or the whole site with /) for the symptom or the error code you see. Each entry says what causes it and how to fix it. Still stuck? Write to support@tryyoursize.com with the **request ID** (`X-Request-ID` header, or `requestId` / `request_id` on SDK errors). ## The element does not appear The size guide, button or inline element never shows on the page. 1. **The visitor is in the A/B holdout.** With an experiment running, 10–50 % of visitors see nothing at all, by design. Check with `await tys.experiment()` (`arm: "holdout"`), or stop the experiment in Studio → Results while testing. 2. **The mount target does not exist yet.** `mount("#size-guide")` throws [`mount_not_found`](https://docs.tryyoursize.com/sdk/errors.md#mount_not_found) when the selector matches nothing. Mount after the container is in the page. 3. **The page's origin is not registered** for the key's mode. Add it in Studio → Settings → Stores → Allowed sites (`https://` and no path; `http://localhost` works in test mode without registering). 4. **A Content Security Policy blocks us.** Allow `script-src https://js.tryyoursize.com` and `frame-src https://embed.tryyoursize.com`. The browser console shows the blocked URL. ## window_unreachable or a loaderror event Our window did not load or connect within 15 seconds. Same causes as above (origin, CSP), or a network filter or ad blocker blocking `tryyoursize.com`. Listen to `loaderror` and fall back to your own size chart. See [`window_unreachable`](https://docs.tryyoursize.com/sdk/errors.md#window_unreachable). ## secret_key_in_browser or secret_key_in_app A secret key (`sk_…`) or restricted key (`rk_…`) reached a browser or an app. Use the publishable key there, and [roll the secret key](https://docs.tryyoursize.com/get-started/keys-and-modes.md) in Studio if the code was deployed. ## app_not_allowed in an iOS or Android app The app is not registered for the key. Add `ios-app://` or `android-app://` (lower case) to the allowed sites for **test mode**. `tys.appOrigin` prints the exact value. Live mode for apps is coming soon. ## The size guide always says "Size guide" The shopper is not connected to TryYourSize on your site (`status: "signed_out"`). That is expected until they sign in once in our window. Each site (origin) has its own connection: a shopper connected on `staging.brand.com` is not connected on `www.brand.com`. ## The status is always unavailable The shopper is connected, but there is no size for this product yet: - The product has **no published garment** in TryYourSize (live mode answers published versions only; test mode answers drafts). Publish it in Studio or with [`garments.publish()`](https://docs.tryyoursize.com/reference/node/garments-publish.md). - The `product` you pass is not the SKU or ID synced to TryYourSize. `tys.products.retrieve("")` on your server tells you. - The shopper has not tried this garment on yet. Page views never start a simulation; the shopper starts it with "See it on you". When it finishes you get another `recommendation` event. - The visitor is in the A/B holdout: the holdout always gets `unavailable`. ## The size is not in my size labels Sizes come back through the product's **size map** (Studio size → your label). Without an entry, you get the Studio label. Map them in Studio, or with [`sizeMap.save()`](https://docs.tryyoursize.com/reference/node/products-size-map-save.md). `unmapped_shop_values` lists your variant values without a Studio size. ## 401 unauthorized from the API The key is missing, mistyped or revoked, or a publishable key was used on the server. Server calls need `Authorization: Bearer sk_…` (or `rk_…`). See [`unauthorized`](https://docs.tryyoursize.com/api/errors.md#unauthorized). ## 403 forbidden from the API A restricted key lacks the scope (each operation lists its **Key scopes**), or a test key reads live data (or the other way round). See [`forbidden`](https://docs.tryyoursize.com/api/errors.md#forbidden). ## handle_expired, handle_invalid or handle_mode_mismatch Recommendation handles last an hour and belong to one mode. Ask the browser for a fresh one with `tys.recommendationHandle()`; use a test secret key with a handle from a test publishable key. A shopper who disconnected your store gets `handle_invalid`. See [`handle_expired`](https://docs.tryyoursize.com/api/errors.md#handle_expired). ## 429 rate_limited Too many requests. Wait for the `Retry-After` seconds and retry with backoff; the SDKs do this for you. With an `Idempotency-Key`, a retried `POST` is safe. See [Rate limits](https://docs.tryyoursize.com/guides/rate-limits.md). ## 409 idempotency_key_in_use or 422 idempotency_key_reused `idempotency_key_in_use`: the first request with this key is still running; retry shortly. `idempotency_key_reused`: you sent the same key with a different body. Use one key per distinct request. See [Idempotency](https://docs.tryyoursize.com/guides/idempotency.md). ## A webhook signature does not verify Almost always the body was parsed and re-serialized before verification. Verify the **raw** bytes: with Express use `express.raw({ type: "application/json" })`; in Flask use `request.get_data()`. Check you use the endpoint's own `whsec_…` secret (test and live endpoints differ), and that the server clock is right (5 minutes of tolerance). See [Webhooks](https://docs.tryyoursize.com/guides/webhooks.md#verify-the-signature). ## Orders do not show up in Results Results count orders sent with [`POST /v1/orders`](https://docs.tryyoursize.com/guides/outcomes.md) in **live** mode. For the A/B comparison each order needs the arm: send `visitor_id` (from `tys.experiment()` in the browser, saved with the cart) or `experiment_arm`. An order without either is counted outside the experiment. ## The shopper picked a size, but my picker changed it Preselect only until the shopper chooses: keep a "touched" flag and never overwrite their choice when a later `recommendation` event arrives. See [Preselect the recommended size](https://docs.tryyoursize.com/guides/recommendation-event.md). --- # Versioning and migrating > Date-based API versions in the TYS-Version header, SDK versions, and how to move to a new version safely. Source: https://docs.tryyoursize.com/guides/versioning/ The server API is versioned by **date**, like Stripe. Send the version you built against in the `TYS-Version` header: ```http TYS-Version: 2026-10-25 ``` - **No header** = the latest version. Every response's `TYS-Version` header says which version answered. - An unknown version is `400` [`invalid_api_version`](https://docs.tryyoursize.com/api/errors.md#invalid_api_version). - **The SDKs pin a version** for you: `@tryyoursize/node` and `tryyoursize` send the version they were released with, so upgrading the library is how you move to a new version. - Additive changes (new endpoints, new optional fields, new event types, new enum values in answers) can ship in any version. Write code that ignores fields it does not know. Breaking changes ship only in a new dated version. The latest version is **2026-10-25**. See the [changelog](https://docs.tryyoursize.com/api/changelog.md). ## Webhooks Webhook envelopes carry their own `api_version` (today `"2026-10"`). See [Webhooks](https://docs.tryyoursize.com/guides/webhooks.md). ## Browser and mobile SDKs TryYourSize.js is versioned by its CDN path (`https://js.tryyoursize.com/v1/tys.js`) and npm semver: improvements reach `/v1/` automatically, breaking changes ship as `/v2/`. The window protocol is versioned separately, so an older SDK keeps working with newer windows. ## Migrating to a new version 1. **Read the [changelog](https://docs.tryyoursize.com/api/changelog.md)** for every version between yours and the target. Each entry lists what changed and what to do. 2. **Try the new version per request, in test mode.** Send `TYS-Version: ` on a few calls (the SDKs take `apiVersion` / `api_version` on the client, and Node.js per call in `RequestOptions`). Responses tell you which version answered. 3. **Upgrade the SDK.** `@tryyoursize/node` and `tryyoursize` pin the version they were released with; a new release moves you to its version. Check `TryYourSize.API_VERSION` (Node.js) or `tryyoursize.API_VERSION` (Python). 4. **Webhooks** carry their own envelope `api_version`: read it, and ignore fields you do not know. 5. **Go live** once your test-mode traffic shows no errors in Studio → Developers → Logs. ## SDK versions | SDK | Versioning | Upgrade | |---|---|---| | TryYourSize.js (CDN) | Major in the path, `/v1/` | Automatic within `/v1/`; change the path for `/v2/` | | `@tryyoursize/sdk`, `@tryyoursize/react` | npm semver | `npm install @tryyoursize/sdk@latest` | | `@tryyoursize/node`, `tryyoursize` (Python) | semver, each release pins one API version | Upgrade the package, run your tests in test mode | | iOS, Android, React Native, Flutter | semver | Update the dependency; the window updates itself | ## Moving from another size tool Keep your existing size chart as the fallback: mount the TryYourSize element next to it (or replace its link with the [size guide](https://docs.tryyoursize.com/products/size-guide.md)), and remove the old tool once the [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md) shows the effect on size returns. Send orders and returns from the start so the comparison has data. --- # Webhooks > Receive signed events (fit.recommended, garment.published…) on your server. Signature verification in Node, Python and plain code; retries. Source: https://docs.tryyoursize.com/guides/webhooks/ TryYourSize sends events to HTTPS endpoints you add in **Studio → Developers → Webhooks**. Endpoints belong to one store and one mode (test or live); up to 16 per store. Each endpoint subscribes to a list of event types, or to all (`*`). Its **signing secret** (`whsec_…`) is shown once. ## Event types Payloads never carry shopper identity or measurements. | Type | `data` | When | |---|---|---| | `fit.recommended` | `tryon_id`, `product_sku`, `recommended_size`, `no_size_fits` | Once per fit result, store and mode | | `garment.published` | `garment_version_id`, `fit_group_id`, `version`, `product_skus` | A garment version was published (sent to test and live endpoints) | | `fit_check.completed` | `fit_check_id`, `garment_version_id`, `status` | A Studio fit check finished or failed | | `profile.disconnected` | `connection_id`, `reason` | A shopper disconnected your store (live mode only) | | `usage.threshold_reached` | | Coming soon: your usage reached a threshold of your plan | | `webhook.test` | | "Send test event" in Studio | ## The envelope ```json { "id": "evt_…", "object": "event", "type": "fit.recommended", "api_version": "2026-10", "created": 1793000000, "mode": "live", "store_id": "…", "data": { "tryon_id": "…", "product_sku": "CREW-TSHIRT", "recommended_size": "M", "no_size_fits": false } } ``` Headers: `TYS-Signature`, `TYS-Event-Id`, `TYS-Event-Type`, `TYS-Delivery-Id`, `TYS-Delivery-Attempt`. The same event can arrive more than once: deduplicate on `TYS-Event-Id` (the envelope's `id`). ## Verify the signature ``` TYS-Signature: t=1793000000,v1=5f2c… ``` `v1` is the hex HMAC-SHA256 of `"."`, keyed with the whole `whsec_…` secret. While you roll a secret, there are two `v1` values (one per secret) for 24 hours: accept the request if **any** matches. Reject timestamps older than **5 minutes**. Always verify the **raw** body, before any JSON parsing. **Node (SDK)** ```ts const tys = new TryYourSize(process.env.TYS_SECRET_KEY!); const app = express(); app.post("/webhooks/tryyoursize", express.raw({ type: "application/json" }), (req, res) => { let event; try { event = tys.webhooks.constructEvent(req.body, req.header("TYS-Signature")!, process.env.TYS_WEBHOOK_SECRET!); } catch { return res.sendStatus(400); } if (event.type === "garment.published") { /* … */ } res.sendStatus(200); }); ``` **Python (SDK)** ```python from flask import Flask, request app = Flask(__name__) @app.post("/webhooks/tryyoursize") def webhook(): try: event = tryyoursize.Webhook.construct_event( request.get_data(), request.headers["TYS-Signature"], os.environ["TYS_WEBHOOK_SECRET"] ) except Exception: return "", 400 if event["type"] == "garment.published": ... return "", 200 ``` **Node (plain)** ```ts const parts = header.split(",").map((p) => p.split("=")); const t = parts.find(([k]) => k === "t")?.[1]; const sigs = parts.filter(([k]) => k === "v1").map(([, v]) => v); if (!t || !sigs.length) return false; if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false; const expected = createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest(); return sigs.some((s) => { const got = Buffer.from(s, "hex"); return got.length === expected.length && timingSafeEqual(got, expected); }); } ``` **Python (plain)** ```python def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool: items = [p.split("=", 1) for p in header.split(",") if "=" in p] t = next((v for k, v in items if k == "t"), None) sigs = [v for k, v in items if k == "v1"] if t is None or not sigs or abs(time.time() - int(t)) > tolerance: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return any(hmac.compare_digest(expected, s) for s in sigs) ``` In any other language: split the header on `,`, take `t` and every `v1`, compute HMAC-SHA256 over `t + "." + raw body` with the secret as the key, hex-encode it, compare in constant time against each `v1`, and check that `t` is within 5 minutes of now. ## Responding and retries - Answer any **2xx** within 10 seconds to acknowledge. Do slow work after you answer. - Redirects are not followed; the response body is ignored. - On another status, a timeout or a network error, we retry: an attempt now, then after **1 min, 5 min, 30 min, 2 h, 5 h and 10 h**, then every **12 h**: 12 attempts over about **72 hours**. - Answer **410 Gone** to stop the retries of that delivery. - Disabling or deleting the endpoint stops pending deliveries. Studio shows every delivery with its payload and attempts, and can **resend** one. ## Rolling the secret In Studio, roll an endpoint's secret: the previous secret keeps signing (as a second `v1`) for 24 hours, so you can deploy the new one without dropping events. --- # Mobile SDK errors > Every error code of the iOS, Android, React Native and Flutter SDKs, what causes it and how to fix it. Source: https://docs.tryyoursize.com/mobile/errors/ The mobile SDKs raise errors with the same stable codes on every platform: `TryYourSizeError` (Swift, `code.rawValue`), `TryYourSizeException` (Kotlin, `code.wire`; Dart, `code`) and, in React Native, `loaderror` events with `code`. Each error's `docURL` / `docUrl` points at the matching section below. ## secret_key_in_app A secret (`sk_…`) or restricted (`rk_…`) key was passed to the app. Apps are public: anyone can extract a key from them. Use your **publishable** key and [roll the secret key](https://docs.tryyoursize.com/get-started/keys-and-modes.md) if a build shipped with it. ## invalid_key Not a publishable key: expected `pk_test_…` or `pk_live_…`. ## invalid_app_identifier The bundle identifier (iOS) or package name (Android) cannot be used as an app origin. It must be reverse DNS, for example `com.brand.shop`. Set `bundleIdentifier` / `packageName` in the configuration if your build uses an unusual identifier. ## invalid_option An option is invalid: `product` must be 1–120 characters; `embedOrigin` must be `https` (`http` only for `localhost`). ## window_unreachable Our window did not answer: the device is offline, or a network filter blocks `embed.tryyoursize.com`. The sheet shows the shopper a short note in their language; your app gets the error with `loadError`. ## app_not_allowed This app is not registered for the key's store. In Studio, add `ios-app://` or `android-app://` (lower case) to the allowed sites for **test mode**. Live mode for apps is coming soon. ## sign_in_cancelled The shopper closed sign-in before finishing. Nothing to do. ## sign_in_unavailable Sign-in through the system browser is not available yet; shoppers sign in inside the window (email and a code), which works today. --- # SDK reference > Every public method, component and event of the TryYourSize SDKs: web, React, Node.js, Python, iOS, Android, React Native and Flutter. Source: https://docs.tryyoursize.com/reference/ One page per public method, component and event, generated from each SDK's source code. Examples come in tabs; pick your language once (header switcher or any tab) and every page follows it. The HTTP endpoints are in the [HTTP API reference](https://docs.tryyoursize.com/api/reference.md), generated from our OpenAPI spec. - [Web SDK](https://docs.tryyoursize.com/reference/web.md): @tryyoursize/sdk · TryYourSize.js for any storefront: the client, elements, events and web components. - [React](https://docs.tryyoursize.com/reference/react.md): @tryyoursize/react · A provider, hooks and element components for TryYourSize.js, shaped like @stripe/react-stripe-js. - [Node.js](https://docs.tryyoursize.com/reference/node.md): @tryyoursize/node · The server SDK for Node.js 18+: recommendations, catalog, orders and returns, audit log, webhooks. - [Python](https://docs.tryyoursize.com/reference/python.md): tryyoursize · The server SDK for Python 3.9+, standard library only: recommendations, catalog, orders and returns, audit log, webhooks. - [iOS](https://docs.tryyoursize.com/reference/ios.md): TryYourSize (Swift) · The native Size guide button (SwiftUI and UIKit), the shopper's recommended size, and the TryYourSize window in a sheet. - [Android](https://docs.tryyoursize.com/reference/android.md): com.tryyoursize:tryyoursize-android · The native Size guide button (Compose and Views), the shopper's recommended size, and the TryYourSize window. - [React Native](https://docs.tryyoursize.com/reference/react-native.md): @tryyoursize/react-native · The native Size guide button and the shopper's recommended size in React Native, over the iOS and Android SDKs. - [Flutter](https://docs.tryyoursize.com/reference/flutter.md): tryyoursize (Dart) · The native Size guide button and the shopper's recommended size in Flutter, over the iOS and Android SDKs. ## Browser and apps, or server? | Where it runs | Key | SDKs | |---|---|---| | Your storefront (browser) | Publishable `pk_…` | [Web SDK](https://docs.tryyoursize.com/reference/web.md), [React](https://docs.tryyoursize.com/reference/react.md) | | Your apps | Publishable `pk_…` | [iOS](https://docs.tryyoursize.com/reference/ios.md), [Android](https://docs.tryyoursize.com/reference/android.md), [React Native](https://docs.tryyoursize.com/reference/react-native.md), [Flutter](https://docs.tryyoursize.com/reference/flutter.md) | | Your server | Secret `sk_…` or restricted `rk_…` | [Node.js](https://docs.tryyoursize.com/reference/node.md), [Python](https://docs.tryyoursize.com/reference/python.md) | Error codes: [web SDK errors](https://docs.tryyoursize.com/sdk/errors.md), [mobile SDK errors](https://docs.tryyoursize.com/mobile/errors.md), [API errors](https://docs.tryyoursize.com/api/errors.md). --- # Android reference (TryYourSize for Kotlin) > The native Size guide button (Compose and Views), the shopper's recommended size, and the TryYourSize window. Source: https://docs.tryyoursize.com/reference/android/ *SDK* · `com.tryyoursize:tryyoursize-android` · v0.1.0 minSdk 24, Jetpack Compose and Views. The window needs an androidx `ComponentActivity` (for example `AppCompatActivity`). Register your app for the key in Studio as `android-app://` (test mode today; live mode for apps is coming soon). ## Install **Gradle (Kotlin)** ```kotlin dependencies { implementation("com.tryyoursize:tryyoursize-android:0.1.0") } ``` ## Initialize **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…") // a secret key throws ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…") // a secret key throws ``` Error codes: [Mobile SDK errors](https://docs.tryyoursize.com/mobile/errors.md). ## Client | | | |---|---| | [`constructor`](https://docs.tryyoursize.com/reference/android/constructor.md) | Creates the client for your publishable key. | | [`Configuration`](https://docs.tryyoursize.com/reference/android/configuration.md) | TryYourSize.Configuration | | [`recommendation()`](https://docs.tryyoursize.com/reference/android/recommendation.md) | The shopper's recommended size for a product, in your size labels. | | [`getRecommendation()`](https://docs.tryyoursize.com/reference/android/get-recommendation.md) | The same, with a callback (Java). | | [`onRecommendation()`](https://docs.tryyoursize.com/reference/android/on-recommendation.md) | Calls handler now (once known) and on every change: signing in, a new body, a simulation finishing. | | [`on (events)`](https://docs.tryyoursize.com/reference/android/on.md) | Window events: Ready, Open, Close, Size, LoadError. | | [`present()`](https://docs.tryyoursize.com/reference/android/present.md) | Opens the measuring / try-on window for a product, full screen over activity. | | [`appearance`](https://docs.tryyoursize.com/reference/android/appearance-property.md) | Live restyle (e.g. | | [`appOrigin`](https://docs.tryyoursize.com/reference/android/app-origin.md) | android-app://: what the store registers as an allowed origin. | ## Size guide button | | | |---|---| | [`SizeGuide`](https://docs.tryyoursize.com/reference/android/size-guide.md) | The "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". | | [`SizeGuideView`](https://docs.tryyoursize.com/reference/android/size-guide-view.md) | The same button as an Android View: set client and product. | ## Types | | | |---|---| | [`Recommendation`](https://docs.tryyoursize.com/reference/android/recommendation-type.md) | The shopper's recommended size for one product, in the store's own size labels. | | [`Appearance`](https://docs.tryyoursize.com/reference/android/appearance.md) | Colours (ARGB ints), font and corner radius for the button and the window. | | [`TryYourSizeException`](https://docs.tryyoursize.com/reference/android/errors.md) | Every error the SDK raises: a stable code, a message and a doc link (like the web SDK). | --- # appOrigin > android-app://: what the store registers as an allowed origin. Source: https://docs.tryyoursize.com/reference/android/app-origin/ *Property* · `com.tryyoursize:tryyoursize-android` `android-app://`: what the store registers as an allowed origin. ```kotlin public val appOrigin: String ``` ## Example **Kotlin** ```kotlin Log.i("tys", tys.appOrigin) // "android-app://com.brand.shop": add it in Studio ``` --- # appearance > Live restyle (e.g. Source: https://docs.tryyoursize.com/reference/android/appearance-property/ *Property* · `com.tryyoursize:tryyoursize-android` Live restyle (e.g. when your app switches to dark theme); an open window updates at once. ```kotlin public var appearance: Appearance ``` ## Example **Kotlin** ```kotlin tys.appearance = tys.appearance.copy(colorPrimary = 0xFF3B2F26.toInt()) ``` **Swift** ```swift tys.appearance.colorPrimary = TYSColor(hex: "#3b2f26") // an open window restyles at once ``` **React Native** ```tsx setAppearance({ variables: { colorPrimary: "#3b2f26" } }); ``` **Flutter** ```dart await tys.setAppearance(const Appearance(colorPrimary: Color(0xFF3B2F26))); ``` --- # Appearance > Colours (ARGB ints), font and corner radius for the button and the window. Source: https://docs.tryyoursize.com/reference/android/appearance/ *Type* · `com.tryyoursize:tryyoursize-android` Colours (ARGB ints), font and corner radius for the button and the window. With `INHERIT` (default) they follow your app theme, dark included. ## Properties - **`theme`** (`Theme`, optional, default `Theme.INHERIT`) - **`colorPrimary`** (`Int?`, optional, default `null`): ARGB colour ints (android.graphics.Color / Compose `toArgb()`). Buttons, the tape line, the "connected" dot. - **`colorBackground`** (`Int?`, optional, default `null`) - **`colorText`** (`Int?`, optional, default `null`) - **`fontFamily`** (`String?`, optional, default `null`): A CSS font-family for the window (e.g. "Inter, Roboto, sans-serif"). The native button uses the Compose text style where it is placed. - **`borderRadius`** (`Float?`, optional, default `null`): Corner radius in dp (0–32) for cards, inputs and buttons. - **`fonts`** (`List`, optional, default `emptyList()`): Stylesheets with @font-face rules for the window (Google Fonts only). ## Example **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # TryYourSize.Configuration > TryYourSize.Configuration Source: https://docs.tryyoursize.com/reference/android/configuration/ *Type* · `com.tryyoursize:tryyoursize-android` ## Properties - **`appearance`** (`Appearance`, optional, default `Appearance()`): Colours, font and corner radius for the button and the window. - **`locale`** (`String?`, optional, default `null`): BCP 47 language for the button and the window; null: the device's language. - **`packageName`** (`String?`, optional, default `null`): The app's identity registered for the store; null: the context's package name. - **`embedOrigin`** (`String`, optional, default `"https://embed.tryyoursize.com"`): Our window's origin (change only for staging or local development). - **`webContentsDebugging`** (`Boolean`, optional, default `false`): Lets chrome://inspect attach to the window. Debug builds only. ## Example **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # TryYourSize(context, publishableKey, configuration) > Creates the client for your publishable key. Source: https://docs.tryyoursize.com/reference/android/constructor/ *Constructor* · `com.tryyoursize:tryyoursize-android` Creates the client for your publishable key. Keep one per app. ```kotlin public class TryYourSize @JvmOverloads constructor( context: Context, public val publishableKey: String, public val configuration: Configuration = Configuration(), ) ``` ## Parameters - **`context`** (`Context`, required): Any context; the application context is kept. - **`publishableKey`** (`String`, required): Your publishable key (`pk_test_…` / `pk_live_…`). - **`configuration`** (`Configuration`, optional, default `Configuration()`): See `Configuration`(/reference/android/configuration/). ## Errors `TryYourSizeException` with code `SECRET_KEY_IN_APP`, `INVALID_KEY`, `INVALID_APP_IDENTIFIER` or `INVALID_OPTION`. ## Example **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # TryYourSizeException > Every error the SDK raises: a stable code, a message and a doc link (like the web SDK). Source: https://docs.tryyoursize.com/reference/android/errors/ *Type* · `com.tryyoursize:tryyoursize-android` Every error the SDK raises: a stable `code`, a message and a doc link (like the web SDK). Each error has a `code` (its `wire` value is the documented code), a message and a `docUrl`. Explanations: [Mobile SDK errors](https://docs.tryyoursize.com/mobile/errors.md). ## Codes - **`SECRET_KEY_IN_APP`** (`"secret_key_in_app"`, optional): A secret (`sk_`) or restricted (`rk_`) key was passed. Those belong on your server. - **`INVALID_KEY`** (`"invalid_key"`, optional): Not a publishable key (`pk_test_…` / `pk_live_…`). - **`INVALID_APP_IDENTIFIER`** (`"invalid_app_identifier"`, optional): The package name can't be used as an app origin. - **`INVALID_OPTION`** (`"invalid_option"`, optional) - **`WINDOW_UNREACHABLE`** (`"window_unreachable"`, optional): Our window did not answer (offline, or blocked). - **`APP_NOT_ALLOWED`** (`"app_not_allowed"`, optional): This app (`android-app://`) is not registered for the key's store. - **`SIGN_IN_CANCELLED`** (`"sign_in_cancelled"`, optional) - **`SIGN_IN_UNAVAILABLE`** (`"sign_in_unavailable"`, optional): System-browser sign-in is not available yet. ## Example **Kotlin** ```kotlin try { tys = TryYourSize(context, key) } catch (e: TryYourSizeException) { if (e.code == TryYourSizeException.Code.SECRET_KEY_IN_APP) error("Use the pk_ key") } ``` --- # getRecommendation() > The same, with a callback (Java). Source: https://docs.tryyoursize.com/reference/android/get-recommendation/ *Method* · `com.tryyoursize:tryyoursize-android` The same, with a callback (Java). ```kotlin public fun getRecommendation(product: String, callback: (Result) -> Unit) ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. - **`callback`** (`(Result) -> Unit`, required): Called once with the result. ## Example **Kotlin** ```kotlin tys.getRecommendation("SKU-123") { result -> result.onSuccess { rec -> if (rec.status == Recommendation.Status.READY) selected = rec.size } } ``` --- # onRecommendation() > Calls handler now (once known) and on every change: signing in, a new body, a simulation finishing. Source: https://docs.tryyoursize.com/reference/android/on-recommendation/ *Method* · `com.tryyoursize:tryyoursize-android` Calls `handler` now (once known) and on every change: signing in, a new body, a simulation finishing. Keep the subscription; `cancel()` it when done. Main thread. ```kotlin @JvmOverloads public fun onRecommendation( product: String, onError: ((TryYourSizeException) -> Unit)? = null, handler: (Recommendation) -> Unit, ): TysSubscription ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. - **`onError`** (`((TryYourSizeException) -> Unit)?`, optional, default `null`): Called when our window cannot be reached or the app is not registered. - **`handler`** (`(Recommendation) -> Unit`, required): Called now (once known) and on every change. ## Returns A subscription; `cancel()` it when done. ## Example **Kotlin** ```kotlin val sub = tys.onRecommendation("SKU-123") { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } // sub.cancel() in onDestroy ``` **Swift** ```swift let sub = tys.onRecommendation(for: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } // keep `sub`; sub.cancel() when the screen goes away ``` **React Native** ```tsx useEffect(() => { const sub = addRecommendationListener("SKU-123", (rec) => { if (rec.status === "ready" && !touched) setSize(rec.size); }); return () => sub.remove(); }, []); ``` **Flutter** ```dart final sub = tys.onRecommendation('SKU-123').listen((rec) { if (rec.status == RecommendationStatus.ready && !touched) setState(() => size = rec.size); }); // sub.cancel() in dispose() ``` --- # on() > Window events: Ready, Open, Close, Size, LoadError. Source: https://docs.tryyoursize.com/reference/android/on/ *Method* · `com.tryyoursize:tryyoursize-android` Window events: Ready, Open, Close, Size, LoadError. ```kotlin public fun on(handler: (Event) -> Unit): TysSubscription ``` ## Parameters - **`handler`** (`(Event) -> Unit`, required): Called with each window event. ## Events - **`Ready(val product: String)`** (optional): The window is shown and connected. - **`Open(val product: String)`** (optional) - **`Close(val product: String)`** (optional) - **`Size(val selection: SizeSelection)`** (optional): The shopper chose a size: preselect it. - **`LoadError(val error: TryYourSizeException)`** (optional): Our window could not be reached, or this app is not registered for the key. ## Returns A subscription; `cancel()` stops the events. ## Example **Kotlin** ```kotlin val sub = tys.on { event -> when (event) { is TryYourSize.Event.Size -> selected = event.selection.size is TryYourSize.Event.LoadError -> Log.w("tys", event.error.code.wire) else -> Unit } } ``` **Swift** ```swift let sub = tys.on { event in switch event { case .size(let selection): selectedSize = selection.size // "Use this size" case .loadError(let error): print(error.code) default: break } } ``` **React Native** ```tsx const sub = addEventListener((e) => { if (e.type === "size") setSize(e.selection.size); if (e.type === "loaderror") console.warn(e.code); }); ``` **Flutter** ```dart tys.events.listen((e) { if (e is SizeSelected) setState(() => size = e.selection.size); if (e is LoadError) debugPrint(e.error.code); }); ``` --- # present() > Opens the measuring / try-on window for a product, full screen over activity. Source: https://docs.tryyoursize.com/reference/android/present/ *Method* · `com.tryyoursize:tryyoursize-android` Opens the measuring / try-on window for a product, full screen over `activity`. ```kotlin @JvmOverloads public fun present(activity: ComponentActivity, product: String, step: OpenStep? = null) ``` ## Parameters - **`activity`** (`ComponentActivity`, required): An androidx `ComponentActivity`. - **`product`** (`String`, required): Your product ID or SKU. - **`step`** (`OpenStep?`, optional, default `null`): `OpenStep.CREATE`, `IMPROVE` or `VIEW`. ## Example **Kotlin** ```kotlin tys.present(activity, "SKU-123") // or step = OpenStep.VIEW ``` **Swift** ```swift tys.present(product: "SKU-123") // or step: .view, from: viewController ``` **React Native** ```tsx present("SKU-123"); // or present("SKU-123", "view") ``` **Flutter** ```dart await tys.present('SKU-123'); // or step: OpenStep.view ``` --- # Recommendation > The shopper's recommended size for one product, in the store's own size labels. Source: https://docs.tryyoursize.com/reference/android/recommendation-type/ *Type* · `com.tryyoursize:tryyoursize-android` The shopper's recommended size for one product, in the store's own size labels. ## Properties - **`product`** (`String`, required) - **`status`** (`Status`, required) - **`size`** (`String?`, optional, default `null`): The recommended size in the store's label (its size map), or null. - **`studioSize`** (`String?`, optional, default `null`): The same size as entered in TryYourSize Studio, or null. - **`wearableSizes`** (`List?`, optional, default `null`): Sizes that can be worn on this body, in the store's labels (when simulated). ## Example **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` ## Notes `status`: `READY`, `COMPUTING`, `NO_SIZE` (`"none"` on the web), `SIGNED_OUT`, `UNAVAILABLE`. --- # recommendation() > The shopper's recommended size for a product, in your size labels. Source: https://docs.tryyoursize.com/reference/android/recommendation/ *Method* · `com.tryyoursize:tryyoursize-android` The shopper's recommended size for a product, in your size labels. Never starts a simulation; never carries measurements or body data. Main thread. ```kotlin public suspend fun recommendation(product: String): Recommendation ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. ## Returns The shopper's recommended size. See [Recommendation](https://docs.tryyoursize.com/reference/android/recommendation-type.md). ## Example **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` --- # SizeGuideView (Views) > The same button as an Android View: set client and product. Source: https://docs.tryyoursize.com/reference/android/size-guide-view/ *Component* · `com.tryyoursize:tryyoursize-android` The same button as an Android `View`: set `client` and `product`. ## Parameters - **`context`** (`Context`, required) - **`attrs`** (`AttributeSet?`, optional, default `null`) - **`defStyleAttr`** (`Int`, optional, default `0`) ## Properties - **`client`** (`TryYourSize?`, required): Your client. - **`product`** (`String?`, required): Your product ID or SKU. - **`variant`** (`SizeGuideVariant`, required): `LINK` (default) or `BUTTON`. - **`label`** (`String?`, required): Your own words instead of "Size guide". - **`onRecommendation`** (`((Recommendation) -> Unit)?`, required): The shopper's recommended size, on show and on every change. ## Example **Kotlin** ```kotlin // Views val guide = SizeGuideView(context).apply { client = tys product = "SKU-123" variant = SizeGuideVariant.BUTTON onRecommendation = { rec -> /* … */ } } ``` **Swift** ```swift // UIKit let guide = SizeGuideButton(client: tys, product: "SKU-123", variant: .button) guide.onRecommendation = { rec in /* … */ } stack.addArrangedSubview(guide) ``` --- # SizeGuide (Compose) > The "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". Source: https://docs.tryyoursize.com/reference/android/size-guide/ *Component* · `com.tryyoursize:tryyoursize-android` The "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". A tap opens the window. ```kotlin @Composable public fun SizeGuide( client: TryYourSize, product: String, modifier: Modifier = Modifier, variant: SizeGuideVariant = SizeGuideVariant.LINK, label: String? = null, textStyle: TextStyle = TextStyle(fontSize = 15.sp), onRecommendation: ((Recommendation) -> Unit)? = null, ) ``` ## Parameters - **`client`** (`TryYourSize`, required): Your client. - **`product`** (`String`, required): Your product ID or SKU. - **`modifier`** (`Modifier`, optional, default `Modifier`): Compose modifier. - **`variant`** (`SizeGuideVariant`, optional, default `SizeGuideVariant.LINK`): `LINK` (32 dp tall, 48 dp touch target) or `BUTTON`. - **`label`** (`String?`, optional, default `null`): your own words instead of "Size guide". - **`textStyle`** (`TextStyle`, optional, default `TextStyle(fontSize = 15.sp)`): your text style (font, size); the colour defaults to the app theme's text colour. - **`onRecommendation`** (`((Recommendation) -> Unit)?`, optional, default `null`): the shopper's recommended size, on show and on every change (sizes only). ## Example **Kotlin** ```kotlin // Compose, where your "Size guide" link was SizeGuide(tys, "SKU-123", textStyle = MaterialTheme.typography.bodyMedium) { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } ``` **Swift** ```swift // SwiftUI, where your "Size guide" link was SizeGuide(client: tys, product: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } ``` **React Native** ```tsx rec.status === "ready" && !touched && setSize(rec.size)} /> ``` **Flutter** ```dart const SizeGuide(product: 'SKU-123') // listen to tys.onRecommendation to preselect ``` --- # Flutter reference (package:tryyoursize) > The native Size guide button and the shopper's recommended size in Flutter, over the iOS and Android SDKs. Source: https://docs.tryyoursize.com/reference/flutter/ *SDK* · `tryyoursize (Dart)` · v0.1.0 Flutter 3.19+ (Dart 3.3+). The button is a native platform view and the window is our window in a native sheet, from the [iOS](https://docs.tryyoursize.com/reference/ios.md) and [Android](https://docs.tryyoursize.com/reference/android.md) SDKs. On Android your `MainActivity` must extend `FlutterFragmentActivity`. Register `ios-app://` and `android-app://` for your key in Studio. ## Install **pubspec.yaml** ```yaml dependencies: tryyoursize: ^0.1.0 ``` ## Initialize **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…'); ``` ## Client | | | |---|---| | [`TryYourSize.create()`](https://docs.tryyoursize.com/reference/flutter/create.md) | Creates the client. | | [`recommendation()`](https://docs.tryyoursize.com/reference/flutter/recommendation.md) | The shopper's recommended size for a product, in your size labels. | | [`onRecommendation()`](https://docs.tryyoursize.com/reference/flutter/on-recommendation.md) | The recommendation now (once known) and on every change: signing in, a new body, a simulation finishing. | | [`events`](https://docs.tryyoursize.com/reference/flutter/events.md) | Window events: ready, open, close, size, loadError. | | [`present()`](https://docs.tryyoursize.com/reference/flutter/present.md) | Opens the measuring / try-on window for a product. | | [`setAppearance()`](https://docs.tryyoursize.com/reference/flutter/set-appearance.md) | Live restyle (e.g. | ## Widgets | | | |---|---| | [`SizeGuide`](https://docs.tryyoursize.com/reference/flutter/size-guide.md) | The compact "Size guide" button, drawn natively: "Size guide" → "Calculating your size…" → "Your size: M". | ## Types | | | |---|---| | [`Recommendation`](https://docs.tryyoursize.com/reference/flutter/recommendation-type.md) | The shopper's recommended size for one product, in the store's own size labels. | | [`Appearance`](https://docs.tryyoursize.com/reference/flutter/appearance.md) | Like the web SDK's (and Stripe's) Appearance API. | | [`TryYourSizeException`](https://docs.tryyoursize.com/reference/flutter/errors.md) | Every error the SDK raises: a stable code (e.g. | --- # Appearance > Like the web SDK's (and Stripe's) Appearance API. Source: https://docs.tryyoursize.com/reference/flutter/appearance/ *Type* · `tryyoursize (Dart)` Like the web SDK's (and Stripe's) Appearance API. Unset values inherit the app's. ## Properties - **`theme`** (`AppearanceTheme`, required) - **`colorPrimary`** (`Color?`, required): Buttons, the tape line, the "connected" dot. - **`colorBackground`** (`Color?`, required) - **`colorText`** (`Color?`, required) - **`fontFamily`** (`String?`, required): A CSS font-family for the window (e.g. "Inter, sans-serif"). - **`borderRadius`** (`double?`, required): Corner radius in logical pixels (0–32). - **`fonts`** (`List`, required): Google Fonts stylesheet URLs for the window. ## Example **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` --- # TryYourSize.create() > Creates the client. Source: https://docs.tryyoursize.com/reference/flutter/create/ *Function* · `tryyoursize (Dart)` Creates the client. Throws `TryYourSizeException` (`secret_key_in_app`, `invalid_key`, `invalid_app_identifier`, `invalid_option`). ```dart static Future create( String publishableKey, { Appearance appearance = const Appearance(), String? locale, String? embedOrigin, }) ``` ## Parameters - **`publishableKey`** (`String`, required): Your publishable key. - **`appearance`** (`Appearance`, optional, default `const Appearance()`): See `Appearance`(/reference/flutter/appearance/). - **`locale`** (`String?`, optional): BCP 47 language; default: the device language. - **`embedOrigin`** (`String?`, optional): Development only. ## Example **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` --- # TryYourSizeException > Every error the SDK raises: a stable code (e.g. Source: https://docs.tryyoursize.com/reference/flutter/errors/ *Type* · `tryyoursize (Dart)` Every error the SDK raises: a stable `code` (e.g. `app_not_allowed`) and a message. Codes: [Mobile SDK errors](https://docs.tryyoursize.com/mobile/errors.md). ## Properties - **`code`** (`String`, required) - **`message`** (`String`, required) ## Example **Flutter** ```dart try { tys = await TryYourSize.create(key); } on TryYourSizeException catch (e) { debugPrint('${e.code}: ${e.message}'); } ``` --- # events > Window events: ready, open, close, size, loadError. Source: https://docs.tryyoursize.com/reference/flutter/events/ *Property* · `tryyoursize (Dart)` Window events: ready, open, close, size, loadError. Event classes: `WindowReady`, `WindowOpened`, `WindowClosed`, `SizeSelected` (preselect `selection.size`) and `LoadError`. ```dart Stream get events ``` ## Example **Flutter** ```dart tys.events.listen((e) { if (e is SizeSelected) setState(() => size = e.selection.size); if (e is LoadError) debugPrint(e.error.code); }); ``` **Swift** ```swift let sub = tys.on { event in switch event { case .size(let selection): selectedSize = selection.size // "Use this size" case .loadError(let error): print(error.code) default: break } } ``` **Kotlin** ```kotlin val sub = tys.on { event -> when (event) { is TryYourSize.Event.Size -> selected = event.selection.size is TryYourSize.Event.LoadError -> Log.w("tys", event.error.code.wire) else -> Unit } } ``` **React Native** ```tsx const sub = addEventListener((e) => { if (e.type === "size") setSize(e.selection.size); if (e.type === "loaderror") console.warn(e.code); }); ``` --- # onRecommendation() > The recommendation now (once known) and on every change: signing in, a new body, a simulation finishing. Source: https://docs.tryyoursize.com/reference/flutter/on-recommendation/ *Method* · `tryyoursize (Dart)` The recommendation now (once known) and on every change: signing in, a new body, a simulation finishing. ```dart Stream onRecommendation(String product) ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. ## Returns A stream: the recommendation now (once known) and on every change. Cancel the subscription when done. ## Example **Flutter** ```dart final sub = tys.onRecommendation('SKU-123').listen((rec) { if (rec.status == RecommendationStatus.ready && !touched) setState(() => size = rec.size); }); // sub.cancel() in dispose() ``` **Swift** ```swift let sub = tys.onRecommendation(for: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } // keep `sub`; sub.cancel() when the screen goes away ``` **Kotlin** ```kotlin val sub = tys.onRecommendation("SKU-123") { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } // sub.cancel() in onDestroy ``` **React Native** ```tsx useEffect(() => { const sub = addRecommendationListener("SKU-123", (rec) => { if (rec.status === "ready" && !touched) setSize(rec.size); }); return () => sub.remove(); }, []); ``` --- # present() > Opens the measuring / try-on window for a product. Source: https://docs.tryyoursize.com/reference/flutter/present/ *Method* · `tryyoursize (Dart)` Opens the measuring / try-on window for a product. ```dart Future present(String product, {OpenStep? step}) ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. - **`step`** (`OpenStep?`, optional): `OpenStep.create`, `improve` or `view`. ## Example **Flutter** ```dart await tys.present('SKU-123'); // or step: OpenStep.view ``` **Swift** ```swift tys.present(product: "SKU-123") // or step: .view, from: viewController ``` **Kotlin** ```kotlin tys.present(activity, "SKU-123") // or step = OpenStep.VIEW ``` **React Native** ```tsx present("SKU-123"); // or present("SKU-123", "view") ``` --- # Recommendation > The shopper's recommended size for one product, in the store's own size labels. Source: https://docs.tryyoursize.com/reference/flutter/recommendation-type/ *Type* · `tryyoursize (Dart)` The shopper's recommended size for one product, in the store's own size labels. ## Properties - **`product`** (`String`, required) - **`status`** (`RecommendationStatus`, required) - **`size`** (`String?`, required): The recommended size in the store's label (its size map), or null. - **`studioSize`** (`String?`, required): The same size as entered in TryYourSize Studio, or null. - **`wearableSizes`** (`List?`, required): Sizes that can be worn on this body, in the store's labels (when simulated). ## Example **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` ## Notes `status`: `ready`, `computing`, `noSize` (`"none"` on the web), `signedOut`, `unavailable`. --- # recommendation() > The shopper's recommended size for a product, in your size labels. Source: https://docs.tryyoursize.com/reference/flutter/recommendation/ *Method* · `tryyoursize (Dart)` The shopper's recommended size for a product, in your size labels. Never starts a simulation; never carries measurements or body data. ```dart Future recommendation(String product) ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. ## Returns The shopper's recommended size. See [Recommendation](https://docs.tryyoursize.com/reference/flutter/recommendation-type.md). ## Example **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` --- # setAppearance() > Live restyle (e.g. Source: https://docs.tryyoursize.com/reference/flutter/set-appearance/ *Method* · `tryyoursize (Dart)` Live restyle (e.g. when your app switches to dark theme). ```dart Future setAppearance(Appearance appearance) ``` ## Parameters - **`appearance`** (`Appearance`, required): The new appearance. ## Example **Flutter** ```dart await tys.setAppearance(const Appearance(colorPrimary: Color(0xFF3B2F26))); ``` **Swift** ```swift tys.appearance.colorPrimary = TYSColor(hex: "#3b2f26") // an open window restyles at once ``` **Kotlin** ```kotlin tys.appearance = tys.appearance.copy(colorPrimary = 0xFF3B2F26.toInt()) ``` **React Native** ```tsx setAppearance({ variables: { colorPrimary: "#3b2f26" } }); ``` --- # SizeGuide > The compact "Size guide" button, drawn natively: "Size guide" → "Calculating your size…" → "Your size: M". Source: https://docs.tryyoursize.com/reference/flutter/size-guide/ *Component* · `tryyoursize (Dart)` The compact "Size guide" button, drawn natively: "Size guide" → "Calculating your size…" → "Your size: M". A tap opens the window. Listen to `onRecommendation` to preselect your picker. ```dart const SizeGuide({ super.key, required this.product, this.variant = SizeGuideVariant.link, this.label, this.width = 220, }) ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. - **`variant`** (`SizeGuideVariant`, optional, default `SizeGuideVariant.link`): `link` (default) or `button`. - **`label`** (`String?`, optional): Your own words instead of "Size guide". - **`width`** (`double`, optional, default `220`): Platform views need a bounded width (default 220). ## Example **Flutter** ```dart const SizeGuide(product: 'SKU-123') // listen to tys.onRecommendation to preselect ``` **Swift** ```swift // SwiftUI, where your "Size guide" link was SizeGuide(client: tys, product: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } ``` **Kotlin** ```kotlin // Compose, where your "Size guide" link was SizeGuide(tys, "SKU-123", textStyle = MaterialTheme.typography.bodyMedium) { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } ``` **React Native** ```tsx rec.status === "ready" && !touched && setSize(rec.size)} /> ``` --- # iOS reference (TryYourSize for Swift) > The native Size guide button (SwiftUI and UIKit), the shopper's recommended size, and the TryYourSize window in a sheet. Source: https://docs.tryyoursize.com/reference/ios/ *SDK* · `TryYourSize (Swift)` · v0.1.0 iOS 16+, Swift 5.9+, SwiftUI and UIKit. A client per publishable key, the native **Size guide** button, the shopper's recommended size (sizes only), and our window in a sheet. Register your app for the key in Studio as `ios-app://` (test mode today; live mode for apps is coming soon). ## Install **Swift Package Manager** ```swift // File → Add Package Dependencies → the TryYourSize package, product "TryYourSize". // Package.swift: .product(name: "TryYourSize", package: "TryYourSize") ``` **CocoaPods** ```ruby pod 'TryYourSize' ``` ## Initialize **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…") // a secret key throws ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…") // a secret key throws ``` **React Native** ```tsx await configure("pk_test_…"); // once, at startup ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…'); ``` Error codes: [Mobile SDK errors](https://docs.tryyoursize.com/mobile/errors.md). ## Client | | | |---|---| | [`init`](https://docs.tryyoursize.com/reference/ios/init.md) | Creates the client for your publishable key. | | [`Configuration`](https://docs.tryyoursize.com/reference/ios/configuration.md) | Options of the client. | | [`recommendation(for:)`](https://docs.tryyoursize.com/reference/ios/recommendation.md) | The shopper's recommended size for a product, in your size labels. | | [`onRecommendation`](https://docs.tryyoursize.com/reference/ios/on-recommendation.md) | Calls handler now (once known) and on every change: signing in, a new body, a simulation finishing. | | [`on (events)`](https://docs.tryyoursize.com/reference/ios/on.md) | Window events: ready, open, close, size, loadError. | | [`present`](https://docs.tryyoursize.com/reference/ios/present.md) | Opens the measuring / try-on window for a product, as a sheet over presenter (default: the app's top-most view controller). | | [`appearance`](https://docs.tryyoursize.com/reference/ios/appearance-property.md) | Live restyle (e.g. | | [`appOrigin`](https://docs.tryyoursize.com/reference/ios/app-origin.md) | ios-app://: what the store registers as an allowed origin. | ## Size guide button | | | |---|---| | [`SizeGuide`](https://docs.tryyoursize.com/reference/ios/size-guide.md) | The "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". | | [`SizeGuideButton`](https://docs.tryyoursize.com/reference/ios/size-guide-button.md) | The same button as a UIView. | ## Types | | | |---|---| | [`Recommendation`](https://docs.tryyoursize.com/reference/ios/recommendation-type.md) | The shopper's recommended size for one product, in the store's own size labels. | | [`Appearance`](https://docs.tryyoursize.com/reference/ios/appearance.md) | Colours, font and corner radius for the button and the window. | | [`TryYourSizeError`](https://docs.tryyoursize.com/reference/ios/errors.md) | Every error the SDK raises: a stable code, a message and a doc link (like the web SDK). | --- # appOrigin > ios-app://: what the store registers as an allowed origin. Source: https://docs.tryyoursize.com/reference/ios/app-origin/ *Property* · `TryYourSize (Swift)` `ios-app://`: what the store registers as an allowed origin. ```swift public let appOrigin: String ``` ## Example **Swift** ```swift print(tys.appOrigin) // "ios-app://com.brand.shop": add it in Studio ``` --- # appearance > Live restyle (e.g. Source: https://docs.tryyoursize.com/reference/ios/appearance-property/ *Property* · `TryYourSize (Swift)` Live restyle (e.g. when your app switches theme); an open window updates at once. ```swift public var appearance: Appearance ``` ## Example **Swift** ```swift tys.appearance.colorPrimary = TYSColor(hex: "#3b2f26") // an open window restyles at once ``` **Kotlin** ```kotlin tys.appearance = tys.appearance.copy(colorPrimary = 0xFF3B2F26.toInt()) ``` **React Native** ```tsx setAppearance({ variables: { colorPrimary: "#3b2f26" } }); ``` **Flutter** ```dart await tys.setAppearance(const Appearance(colorPrimary: Color(0xFF3B2F26))); ``` ## See also - [Appearance](https://docs.tryyoursize.com/reference/ios/appearance.md) --- # Appearance > Colours, font and corner radius for the button and the window. Source: https://docs.tryyoursize.com/reference/ios/appearance/ *Type* · `TryYourSize (Swift)` Colours, font and corner radius for the button and the window. With `.inherit` (default) the button takes your SwiftUI font and text colour and the window your app's colours, dark mode included. ## Properties - **`theme`** (`Theme`, required) - **`colorPrimary`** (`TYSColor?`, required): Buttons and the tape line on the body; the "connected" dot on the size guide. - **`colorBackground`** (`TYSColor?`, required) - **`colorText`** (`TYSColor?`, required) - **`fontFamily`** (`String?`, required): A CSS font-family for the window (e.g. "Inter, -apple-system, sans-serif"). The native button uses the SwiftUI font of where it is placed. - **`borderRadius`** (`Double?`, required): Corner radius in points (0–32) for cards, inputs and buttons. - **`fonts`** (`[URL]`, required): Stylesheets with @font-face rules for the window (Google Fonts only). ## Example **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # TryYourSize.Configuration > Options of the client. Source: https://docs.tryyoursize.com/reference/ios/configuration/ *Type* · `TryYourSize (Swift)` Options of the client. ## Properties - **`appearance`** (`Appearance`, required): Colours, font and corner radius for the button and the window. - **`locale`** (`String?`, required): BCP 47 language for the button and the window; nil: the app's preferred language. - **`bundleIdentifier`** (`String?`, required): The app's identity registered for the store; nil: `Bundle.main.bundleIdentifier`. - **`embedOrigin`** (`URL`, required): Our window's origin (change only for staging or local development). - **`inspectable`** (`Bool`, required): Lets Safari's Web Inspector attach to the window (iOS 16.4+). Debug builds only. ## Example **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # TryYourSizeError > Every error the SDK raises: a stable code, a message and a doc link (like the web SDK). Source: https://docs.tryyoursize.com/reference/ios/errors/ *Type* · `TryYourSize (Swift)` Every error the SDK raises: a stable `code`, a message and a doc link (like the web SDK). Each error has a stable `code`, a `message` and a `docURL`. Explanations: [Mobile SDK errors](https://docs.tryyoursize.com/mobile/errors.md). ## Codes - **`.secretKeyInApp`** (`"secret_key_in_app"`, optional): A secret (`sk_`) or restricted (`rk_`) key was passed. Those belong on your server. - **`.invalidKey`** (`"invalid_key"`, optional): Not a publishable key (`pk_test_…` / `pk_live_…`). - **`.invalidAppIdentifier`** (`"invalid_app_identifier"`, optional): The bundle identifier can't be used as an app origin. - **`.invalidOption`** (`"invalid_option"`, optional) - **`.windowUnreachable`** (`"window_unreachable"`, optional): Our window did not answer (offline, or blocked). - **`.appNotAllowed`** (`"app_not_allowed"`, optional): This app (`ios-app://`) is not registered for the key's store. - **`.signInCancelled`** (`"sign_in_cancelled"`, optional) - **`.signInUnavailable`** (`"sign_in_unavailable"`, optional): System-browser sign-in is not available yet. ## Example **Swift** ```swift do { tys = try TryYourSize(publishableKey: key) } catch let error as TryYourSizeError where error.code == .secretKeyInApp { assertionFailure("Use the pk_ key in the app") } ``` --- # TryYourSize(publishableKey:configuration:) > Creates the client for your publishable key. Source: https://docs.tryyoursize.com/reference/ios/init/ *Constructor* · `TryYourSize (Swift)` Creates the client for your publishable key. Keep one per app. ```swift public init(publishableKey: String, configuration: Configuration = Configuration()) throws ``` ## Parameters - **`publishableKey`** (`String`, required): Your publishable key (`pk_test_…` / `pk_live_…`). - **`configuration`** (`Configuration`, optional, default `Configuration()`): Appearance, language and the app identity. See `Configuration`(/reference/ios/configuration/). ## Errors `TryYourSizeError` with code `secret_key_in_app`, `invalid_key`, `invalid_app_identifier` or `invalid_option`. ## Example **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # onRecommendation(for:onError:_:) > Calls handler now (once known) and on every change: signing in, a new body, a simulation finishing. Source: https://docs.tryyoursize.com/reference/ios/on-recommendation/ *Method* · `TryYourSize (Swift)` Calls `handler` now (once known) and on every change: signing in, a new body, a simulation finishing. Keep the subscription; `cancel()` it when done. ```swift @discardableResult public func onRecommendation(for product: String, onError: ((TryYourSizeError) -> Void)? = nil, _ handler: @escaping (Recommendation) -> Void) -> TYSSubscription ``` ## Parameters - **`for`** (`String`, required): Your product ID or SKU. - **`onError`** (`((TryYourSizeError) -> Void)?`, optional, default `nil`): Called when our window cannot be reached or the app is not registered. - **`handler`** (`@escaping (Recommendation) -> Void`, required): Called now (once known) and on every change. ## Returns A subscription: keep it, and `cancel()` it when you are done. ## Example **Swift** ```swift let sub = tys.onRecommendation(for: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } // keep `sub`; sub.cancel() when the screen goes away ``` **Kotlin** ```kotlin val sub = tys.onRecommendation("SKU-123") { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } // sub.cancel() in onDestroy ``` **React Native** ```tsx useEffect(() => { const sub = addRecommendationListener("SKU-123", (rec) => { if (rec.status === "ready" && !touched) setSize(rec.size); }); return () => sub.remove(); }, []); ``` **Flutter** ```dart final sub = tys.onRecommendation('SKU-123').listen((rec) { if (rec.status == RecommendationStatus.ready && !touched) setState(() => size = rec.size); }); // sub.cancel() in dispose() ``` --- # on(_:) > Window events: ready, open, close, size, loadError. Source: https://docs.tryyoursize.com/reference/ios/on/ *Method* · `TryYourSize (Swift)` Window events: ready, open, close, size, loadError. ```swift @discardableResult public func on(_ handler: @escaping (Event) -> Void) -> TYSSubscription ``` ## Parameters - **`handler`** (`@escaping (Event) -> Void`, required): Called with each window event. ## Events - **`.ready(product: String)`** (optional): The window is shown and connected. - **`.open(product: String)`** (optional) - **`.close(product: String)`** (optional) - **`.size(SizeSelection)`** (optional): The shopper chose a size: preselect it. - **`.loadError(TryYourSizeError)`** (optional): Our window could not be reached, or this app is not registered for the key. ## Returns A subscription; `cancel()` stops the events. ## Example **Swift** ```swift let sub = tys.on { event in switch event { case .size(let selection): selectedSize = selection.size // "Use this size" case .loadError(let error): print(error.code) default: break } } ``` **Kotlin** ```kotlin val sub = tys.on { event -> when (event) { is TryYourSize.Event.Size -> selected = event.selection.size is TryYourSize.Event.LoadError -> Log.w("tys", event.error.code.wire) else -> Unit } } ``` **React Native** ```tsx const sub = addEventListener((e) => { if (e.type === "size") setSize(e.selection.size); if (e.type === "loaderror") console.warn(e.code); }); ``` **Flutter** ```dart tys.events.listen((e) { if (e is SizeSelected) setState(() => size = e.selection.size); if (e is LoadError) debugPrint(e.error.code); }); ``` --- # present(product:step:from:) > Opens the measuring / try-on window for a product, as a sheet over presenter (default: the app's top-most view controller). Source: https://docs.tryyoursize.com/reference/ios/present/ *Method* · `TryYourSize (Swift)` Opens the measuring / try-on window for a product, as a sheet over `presenter` (default: the app's top-most view controller). Swiping the sheet down asks the window first: with unsaved measurements, the shopper confirms. ```swift public func present(product: String, step: OpenStep? = nil, from presenter: UIViewController? = nil) ``` ## Parameters - **`product`** (`String`, required): Your product ID or SKU. - **`step`** (`OpenStep?`, optional, default `nil`): `.create`, `.improve` or `.view` (default: where the shopper is). - **`from`** (`UIViewController?`, optional, default `nil`): The presenting view controller (default: the top-most one). ## Example **Swift** ```swift tys.present(product: "SKU-123") // or step: .view, from: viewController ``` **Kotlin** ```kotlin tys.present(activity, "SKU-123") // or step = OpenStep.VIEW ``` **React Native** ```tsx present("SKU-123"); // or present("SKU-123", "view") ``` **Flutter** ```dart await tys.present('SKU-123'); // or step: OpenStep.view ``` --- # Recommendation > The shopper's recommended size for one product, in the store's own size labels. Source: https://docs.tryyoursize.com/reference/ios/recommendation-type/ *Type* · `TryYourSize (Swift)` The shopper's recommended size for one product, in the store's own size labels. ## Properties - **`product`** (`String`, required) - **`status`** (`Status`, required) - **`size`** (`String?`, required): The recommended size in the store's label (its size map), or nil. - **`studioSize`** (`String?`, required): The same size as entered in TryYourSize Studio, or nil. - **`wearableSizes`** (`[String]?`, required): Sizes that can be worn on this body, in the store's labels (when simulated). ## Example **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` ## Notes `status`: `.ready` (`size` is set), `.computing`, `.noSize` (`"none"` on the web: no size can be worn), `.signedOut`, `.unavailable`. --- # recommendation(for:) > The shopper's recommended size for a product, in your size labels. Source: https://docs.tryyoursize.com/reference/ios/recommendation/ *Method* · `TryYourSize (Swift)` The shopper's recommended size for a product, in your size labels. Never starts a simulation; never carries measurements or body data. ```swift public func recommendation(for product: String) async throws -> Recommendation ``` ## Parameters - **`for`** (`String`, required): Your product ID or SKU. ## Returns The shopper's recommended size. See [Recommendation](https://docs.tryyoursize.com/reference/ios/recommendation-type.md). ## Example **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` --- # SizeGuideButton (UIKit) > The same button as a UIView. Source: https://docs.tryyoursize.com/reference/ios/size-guide-button/ *Component* · `TryYourSize (Swift)` The same button as a `UIView`. Auto Layout sizes it from its content. `product`, `variant`, `label` and `onRecommendation` can be changed later. ```swift public init(client: TryYourSize, product: String, variant: SizeGuide.Variant = .link, label: String? = nil) ``` ## Parameters - **`client`** (`TryYourSize`, required): Your client. - **`product`** (`String`, required): Your product ID or SKU. - **`variant`** (`SizeGuide.Variant`, optional, default `.link`) - **`label`** (`String?`, optional, default `nil`) ## Example **Swift** ```swift // UIKit let guide = SizeGuideButton(client: tys, product: "SKU-123", variant: .button) guide.onRecommendation = { rec in /* … */ } stack.addArrangedSubview(guide) ``` **Kotlin** ```kotlin // Views val guide = SizeGuideView(context).apply { client = tys product = "SKU-123" variant = SizeGuideVariant.BUTTON onRecommendation = { rec -> /* … */ } } ``` --- # SizeGuide (SwiftUI) > The "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". Source: https://docs.tryyoursize.com/reference/ios/size-guide/ *Component* · `TryYourSize (Swift)` The "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". A tap opens the window. It takes the SwiftUI font and colour of where you put it. ```swift public init(client: TryYourSize, product: String, variant: Variant = .link, label: String? = nil, onRecommendation: ((Recommendation) -> Void)? = nil) ``` ## Parameters - **`client`** (`TryYourSize`, required): Your client. - **`product`** (`String`, required): Your product ID or SKU. - **`variant`** (`Variant`, optional, default `.link`): `.link` (link-sized text with the mark, 44 pt hit area) or `.button` (a small bordered button). - **`label`** (`String?`, optional, default `nil`): your own words instead of "Size guide". - **`onRecommendation`** (`((Recommendation) -> Void)?`, optional, default `nil`): the shopper's recommended size, on appear and on every change (sizes only). ## Example **Swift** ```swift // SwiftUI, where your "Size guide" link was SizeGuide(client: tys, product: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } ``` **Kotlin** ```kotlin // Compose, where your "Size guide" link was SizeGuide(tys, "SKU-123", textStyle = MaterialTheme.typography.bodyMedium) { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } ``` **React Native** ```tsx rec.status === "ready" && !touched && setSize(rec.size)} /> ``` **Flutter** ```dart const SizeGuide(product: 'SKU-123') // listen to tys.onRecommendation to preselect ``` --- # Node.js reference (@tryyoursize/node) > The server SDK for Node.js 18+: recommendations, catalog, orders and returns, audit log, webhooks. Source: https://docs.tryyoursize.com/reference/node/ *SDK* · `@tryyoursize/node` · v0.1.0 The official Node.js SDK for the TryYourSize server API. Use it **on your server** with a secret key (`sk_test_…` / `sk_live_…`) or a restricted key (`rk_…`); publishable keys (`pk_…`) are refused. Typed from our OpenAPI spec, with retries, idempotency keys and auto-pagination built in. Node 18 or newer (or pass your own `fetch`). ## Install **npm** ```bash npm install @tryyoursize/node ``` **pip** ```bash pip install tryyoursize ``` ## Initialize **Node.js** ```ts const tys = new TryYourSize(process.env.TRYYOURSIZE_SECRET_KEY!, { maxRetries: 2, // network errors, timeouts, 429, 5xx timeoutMs: 30_000, }); ``` **Python** ```python tys = tryyoursize.TryYourSize(os.environ["TRYYOURSIZE_SECRET_KEY"], max_retries=2, timeout=30.0) ``` Every method maps to one HTTP operation of the [HTTP API reference](https://docs.tryyoursize.com/api/reference.md). Errors are typed: see [Errors](https://docs.tryyoursize.com/reference/node/errors.md) and the [API error codes](https://docs.tryyoursize.com/api/errors.md). ## Client | | | |---|---| | [`new TryYourSize()`](https://docs.tryyoursize.com/reference/node/client.md) | Creates a client for one secret or restricted key. | | [`mode`](https://docs.tryyoursize.com/reference/node/mode.md) | "test" or "live", from the key prefix. | | [`lastResponse`](https://docs.tryyoursize.com/reference/node/last-response.md) | Meta of the last answered request (success or API error). | | [`RequestOptions`](https://docs.tryyoursize.com/reference/node/request-options.md) | Options of one call: the last argument of every method. | ## Recommendations | | | |---|---| | [`recommendations.create()`](https://docs.tryyoursize.com/reference/node/recommendations-create.md) | handle comes from the browser SDK (await tys.recommendationHandle()), products are your SKUs / style IDs. | ## Products | | | |---|---| | [`products.list()`](https://docs.tryyoursize.com/reference/node/products-list.md) | Newest first. | | [`products.create()`](https://docs.tryyoursize.com/reference/node/products-create.md) | tys.products.create() | | [`products.retrieve()`](https://docs.tryyoursize.com/reference/node/products-retrieve.md) | tys.products.retrieve() | | [`products.update()`](https://docs.tryyoursize.com/reference/node/products-update.md) | Replaces the SKU, name and category (all three required). | | [`products.sizeMap.retrieve()`](https://docs.tryyoursize.com/reference/node/products-size-map-retrieve.md) | tys.products.sizeMap.retrieve() | | [`products.sizeMap.save()`](https://docs.tryyoursize.com/reference/node/products-size-map-save.md) | Replaces the size map. | | [`products.garments.list()`](https://docs.tryyoursize.com/reference/node/products-garments-list.md) | Garment versions, newest first (a single page: has_more is always false). | | [`products.garments.createDraft()`](https://docs.tryyoursize.com/reference/node/products-garments-create-draft.md) | Starts a new draft version (sizes, POMs in mm, grading, fabric). | ## Garments | | | |---|---| | [`garments.retrieve()`](https://docs.tryyoursize.com/reference/node/garments-retrieve.md) | tys.garments.retrieve() | | [`garments.updateDraft()`](https://docs.tryyoursize.com/reference/node/garments-update-draft.md) | Replaces a draft's content (published versions never change: 409). | | [`garments.publish()`](https://docs.tryyoursize.com/reference/node/garments-publish.md) | Publishes a complete draft. | ## Fit groups | | | |---|---| | [`fitGroups.list()`](https://docs.tryyoursize.com/reference/node/fit-groups-list.md) | tys.fitGroups.list() | | [`fitGroups.create()`](https://docs.tryyoursize.com/reference/node/fit-groups-create.md) | tys.fitGroups.create() | | [`fitGroups.retrieve()`](https://docs.tryyoursize.com/reference/node/fit-groups-retrieve.md) | tys.fitGroups.retrieve() | | [`fitGroups.linkProducts()`](https://docs.tryyoursize.com/reference/node/fit-groups-link-products.md) | Moves products into the group (they then use its garment). | ## Outcomes | | | |---|---| | [`orders.create()`](https://docs.tryyoursize.com/reference/node/orders-create.md) | tys.orders.create() | | [`returns.create()`](https://docs.tryyoursize.com/reference/node/returns-create.md) | tys.returns.create() | ## Audit log | | | |---|---| | [`auditEvents.list()`](https://docs.tryyoursize.com/reference/node/audit-events-list.md) | tys.auditEvents.list() | ## Webhooks | | | |---|---| | [`webhooks.constructEvent()`](https://docs.tryyoursize.com/reference/node/webhooks-construct-event.md) | Verifies a delivery's TYS-Signature header against the raw request body and returns the parsed event. | | [`webhooks.verifySignature()`](https://docs.tryyoursize.com/reference/node/webhooks-verify-signature.md) | Checks the signature only (throws when it does not verify). | | [`webhooks.generateTestHeader()`](https://docs.tryyoursize.com/reference/node/webhooks-generate-test-header.md) | Builds a valid TYS-Signature header for a payload, to test your webhook handler. | ## Pagination and errors | | | |---|---| | [`PagePromise`](https://docs.tryyoursize.com/reference/node/page-promise.md) | What every list() returns: await it for one page ({ data, has_more, next_cursor }), or iterate it with for await to walk every item of every page. | | [`Errors`](https://docs.tryyoursize.com/reference/node/errors.md) | Every error of an API call is a TryYourSizeError subclass, by HTTP status: InvalidRequestError (400, 422), AuthenticationError (401), PermissionError (403), NotFoundError (404), ConflictError (409), R | --- # tys.auditEvents.list() > tys.auditEvents.list() Source: https://docs.tryyoursize.com/reference/node/audit-events-list/ *Method* · `@tryyoursize/node` ```ts list(params: AuditEventListParams = {}, options?: RequestOptions): PagePromise ``` ## Parameters - **`params`** (`AuditEventListParams`, optional, default `{}`): `action` (a trailing `.` matches a prefix), `actor`, `store_id`, `target`, `from`, `to`, `limit` (≤ 200), `cursor`. - **`options`** (`RequestOptions`, optional) ## Returns `PagePromise`: Audit events, newest first. Await it for one page (`{ data, has_more, next_cursor }`), or `for await` over every item. See [PagePromise](https://docs.tryyoursize.com/reference/node/page-promise.md). ## Example **Node.js** ```ts for await (const e of tys.auditEvents.list({ action: "api_key." })) { console.log(e.created_at, e.actor_type, e.action); } ``` **Python** ```python for event in tys.audit_events.list(action="api_key.").auto_paging_iter(): print(event["created_at"], event["actor_type"], event["action"]) ``` **cURL** ```bash curl "https://api.tryyoursize.com/v1/audit_events?action=api_key." \ -H "Authorization: Bearer $TRYYOURSIZE_RESTRICTED_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [auditEvents.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listauditeventswithkey.md) --- # new TryYourSize() > Creates a client for one secret or restricted key. Source: https://docs.tryyoursize.com/reference/node/client/ *Constructor* · `@tryyoursize/node` Creates a client for one secret or restricted key. Create it once and reuse it. ```ts constructor(apiKey: string, options: TryYourSizeOptions = {}) ``` ## Parameters - **`apiKey`** (`string`, required): A secret key (`sk_test_…` / `sk_live_…`) or a restricted key (`rk_…`). Keep it in an environment variable. - **`options`** (`TryYourSizeOptions`, optional, default `{}`) - **`apiVersion`** (`string | undefined`, optional): TYS-Version sent with every request (default: the version this SDK was built against). - **`maxRetries`** (`number | undefined`, optional): Retries of network errors, timeouts, 429, 5xx and 409 idempotency_key_in_use (default 2). - **`timeoutMs`** (`number | undefined`, optional): Per-attempt timeout in ms (default 30,000). - **`baseUrl`** (`string | undefined`, optional): API origin (default https://api.tryyoursize.com). - **`fetch`** (`typeof fetch | undefined`, optional): A fetch implementation (default: the global fetch of Node 18+). ## Errors `TypeError` for a missing key, a publishable key, or no `fetch`. ## Example **Node.js** ```ts const tys = new TryYourSize(process.env.TRYYOURSIZE_SECRET_KEY!, { maxRetries: 2, // network errors, timeouts, 429, 5xx timeoutMs: 30_000, }); ``` **Python** ```python tys = tryyoursize.TryYourSize(os.environ["TRYYOURSIZE_SECRET_KEY"], max_retries=2, timeout=30.0) ``` --- # Errors > Every error of an API call is a TryYourSizeError subclass, by HTTP status: InvalidRequestError (400, 422), AuthenticationError (401), PermissionError (403), NotFoundError (404), ConflictError (409), R Source: https://docs.tryyoursize.com/reference/node/errors/ *Class* · `@tryyoursize/node` Every error of an API call is a `TryYourSizeError` subclass, by HTTP status: `InvalidRequestError` (400, 422), `AuthenticationError` (401), `PermissionError` (403), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429, with `retryAfter`), `APIError` (5xx) and `ConnectionError` (no answer). Webhook checks throw `SignatureVerificationError`. ```ts class TryYourSizeError extends Error ``` ## Properties - **`status`** (`number | undefined`, required) - **`code`** (`string | undefined`, required) - **`docUrl`** (`string | undefined`, required) - **`requestId`** (`string | undefined`, required) - **`raw`** (`unknown`, required) - **`headers`** (`Headers | undefined`, required) - **`issues`** (`GarmentIssue[] | undefined`, required): Values to fix, for 422 answers of the garment endpoints (schema GarmentRejected). ## Example **Node.js** ```ts try { await tys.products.retrieve("NO-SUCH-SKU"); } catch (err) { if (err instanceof RateLimitError) await sleep((err.retryAfter ?? 1) * 1000); else if (err instanceof TryYourSizeError) console.error(err.status, err.code, err.requestId); } ``` **Python** ```python try: tys.products.retrieve("NO-SUCH-SKU") except tryyoursize.NotFoundError: pass except tryyoursize.APIError as err: print(err.status, err.code, err.request_id) ``` ## See also - [API error codes](https://docs.tryyoursize.com/api/errors.md) --- # tys.fitGroups.create() > tys.fitGroups.create() Source: https://docs.tryyoursize.com/reference/node/fit-groups-create/ *Method* · `@tryyoursize/node` ```ts create(input: FitGroupInput, options?: RequestOptions): Promise ``` ## Parameters - **`input`** (`FitGroupInput`, required) - **`name`** (`string`, required) - **`product_ids`** (`string[]`, optional): Products to put in the new group (create only). - **`source_product_id`** (`string`, optional): "Use the garment of…": the new group takes over this product's garment versions (create only). - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The fit group. ## Example **Node.js** ```ts const group = await tys.fitGroups.create({ name: "Basic tee", product_ids: [navy.id, black.id], // colourways that share one garment }); ``` **Python** ```python group = tys.fit_groups.create(name="Basic tee", product_ids=[navy["id"], black["id"]]) ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/fit_groups \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"name":"Basic tee","product_ids":["",""]}' ``` ## See also - [fitGroups.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createcatalogfitgroup.md) --- # tys.fitGroups.linkProducts() > Moves products into the group (they then use its garment). Source: https://docs.tryyoursize.com/reference/node/fit-groups-link-products/ *Method* · `@tryyoursize/node` Moves products into the group (they then use its garment). ```ts linkProducts(id: string, params: LinkProductsRequest, options?: RequestOptions): Promise ``` ## Parameters - **`id`** (`string`, required): The fit group ID. - **`params`** (`LinkProductsRequest`, required) - **`product_ids`** (`string[]`, required) - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The fit group. ## Example **Node.js** ```ts await tys.fitGroups.linkProducts(group.id, { product_ids: [white.id] }); ``` **Python** ```python tys.fit_groups.link_products(group["id"], product_ids=[white["id"]]) ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/fit_groups//products \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"product_ids":[""]}' ``` ## See also - [fitGroups.linkProducts (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/linkcatalogfitgroupproducts.md) --- # tys.fitGroups.list() > tys.fitGroups.list() Source: https://docs.tryyoursize.com/reference/node/fit-groups-list/ *Method* · `@tryyoursize/node` ```ts list(params: FitGroupListParams = {}, options?: RequestOptions): PagePromise ``` ## Parameters - **`params`** (`FitGroupListParams`, optional, default `{}`): `limit` and `cursor`. - **`options`** (`RequestOptions`, optional) ## Returns `PagePromise`: Fit groups. Await it for one page (`{ data, has_more, next_cursor }`), or `for await` over every item. See [PagePromise](https://docs.tryyoursize.com/reference/node/page-promise.md). ## Example **Node.js** ```ts for await (const g of tys.fitGroups.list()) console.log(g.id, g.name); ``` **Python** ```python for group in tys.fit_groups.list().auto_paging_iter(): print(group["id"], group["name"]) ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/fit_groups \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [fitGroups.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listcatalogfitgroups.md) --- # tys.fitGroups.retrieve() > tys.fitGroups.retrieve() Source: https://docs.tryyoursize.com/reference/node/fit-groups-retrieve/ *Method* · `@tryyoursize/node` ```ts retrieve(id: string, options?: RequestOptions): Promise ``` ## Parameters - **`id`** (`string`, required): The fit group ID. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The fit group. ## Example **Node.js** ```ts const group = await tys.fitGroups.retrieve(groupId); ``` **Python** ```python group = tys.fit_groups.retrieve(group_id) ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/fit_groups/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [fitGroups.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcatalogfitgroup.md) --- # tys.garments.publish() > Publishes a complete draft. Source: https://docs.tryyoursize.com/reference/node/garments-publish/ *Method* · `@tryyoursize/node` Publishes a complete draft. Key scope: `garments:publish`. ```ts publish(id: string, options?: RequestOptions): Promise ``` ## Parameters - **`id`** (`string`, required): The draft's ID. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The published version. Shoppers get sizes for it from now on. ## Errors `InvalidRequestError` (422) with `issues` listing what is missing. ## Example **Node.js** ```ts try { await tys.garments.publish(draft.id); } catch (err) { if (err instanceof InvalidRequestError) console.log(err.issues); // what is missing } ``` **Python** ```python try: tys.garments.publish(draft["id"]) except tryyoursize.InvalidRequestError as err: print(err.issues) # what is missing ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/garments//publish \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [garments.publish (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/publishcataloggarment.md) --- # tys.garments.retrieve() > tys.garments.retrieve() Source: https://docs.tryyoursize.com/reference/node/garments-retrieve/ *Method* · `@tryyoursize/node` ```ts retrieve(id: string, options?: RequestOptions): Promise ``` ## Parameters - **`id`** (`string`, required): The garment version ID. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The garment version. ## Example **Node.js** ```ts const version = await tys.garments.retrieve(garmentId); ``` **Python** ```python version = tys.garments.retrieve(garment_id) ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/garments/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [garments.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcataloggarment.md) --- # tys.garments.updateDraft() > Replaces a draft's content (published versions never change: 409). Source: https://docs.tryyoursize.com/reference/node/garments-update-draft/ *Method* · `@tryyoursize/node` Replaces a draft's content (published versions never change: 409). ```ts updateDraft(id: string, garment: GarmentInput, options?: RequestOptions): Promise ``` ## Parameters - **`id`** (`string`, required): The draft's ID. - **`garment`** (`GarmentInput`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`fit_intent`** (`FitIntent`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - **`sizes`** (`string[]`, required): Size labels, smallest to largest. - **`poms`** (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - **`grading`** (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - **`fabric`** (`Fabric`, required): What the tech pack / mill sheet says. - **`import_id`** (`string`, optional): Pattern import linked to this version (omit to unlink). - **`season`** (`string`, optional): Season tag, e.g. "SS27". - **`tolerances_mm`** (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - **`spec_source`** (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The updated draft. Published versions never change (409 `conflict`). ## Example **Node.js** ```ts await tys.garments.updateDraft(draft.id, { ...garment, season: "SS27" }); // replaces the draft ``` **Python** ```python tys.garments.update_draft(draft["id"], {**garment, "season": "SS27"}) # replaces the draft ``` **cURL** ```bash curl -X PUT https://api.tryyoursize.com/v1/garments/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"category":"tshirt","fit_intent":"regular","sizes":["S","M","L"],"poms":{"S":{"chest_width":480},"M":{"chest_width":510},"L":{"chest_width":540}},"fabric":{"composition":[{"fiber":"cotton","pct":100}],"weight_gsm":180}}' ``` ## See also - [garments.updateDraft (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/updatecataloggarmentdraft.md) --- # tys.lastResponse > Meta of the last answered request (success or API error). Source: https://docs.tryyoursize.com/reference/node/last-response/ *Property* · `@tryyoursize/node` Meta of the last answered request (success or API error). ```ts lastResponse: ResponseMeta | undefined; ``` ## Properties - **`method`** (`string`, required) - **`path`** (`string`, required) - **`status`** (`number`, required) - **`requestId`** (`string | undefined`, required) - **`apiVersion`** (`string | undefined`, required): TYS-Version the API answered with. - **`idempotentReplayed`** (`boolean`, required): True when the answer is a stored idempotent replay. - **`idempotencyKey`** (`string | undefined`, required) - **`rateLimit`** (`RateLimit`, required) - **`limit`** (`number | undefined`, required) - **`remaining`** (`number | undefined`, required) - **`reset`** (`number | undefined`, required): Unix seconds when the window resets (as sent by the API). - **`headers`** (`Headers`, required) ## Example **Node.js** ```ts await tys.products.list(); console.log(tys.lastResponse?.requestId, tys.lastResponse?.rateLimit.remaining); ``` --- # tys.mode > "test" or "live", from the key prefix. Source: https://docs.tryyoursize.com/reference/node/mode/ *Property* · `@tryyoursize/node` `"test"` or `"live"`, from the key prefix. ```ts get mode(): "test" | "live" | undefined ``` ## Example **Node.js** ```ts if (tys.mode === "live") enableOrderSync(); ``` **Python** ```python if tys.mode == "live": enable_order_sync() ``` --- # tys.orders.create() > tys.orders.create() Source: https://docs.tryyoursize.com/reference/node/orders-create/ *Method* · `@tryyoursize/node` ```ts create(order: OutcomeOrderInput, options?: RequestOptions): Promise ``` ## Parameters - **`order`** (`OutcomeOrderInput`, required) - **`id`** (`string`, required): Your order ID. - **`ordered_at`** (`string`, required) - **`currency`** (`string`, optional): ISO 4217, e.g. EUR. - **`visitor_id`** (`string`, optional): The SDK's anonymous visitor ID (`(await tys.experiment())?.visitorId`), saved with the cart. TryYourSize recomputes the A/B arm from it and stores only its hash. Never a customer ID. - **`experiment_arm`** (`"treatment" | "holdout"`, optional): The arm, when you cannot send visitor_id. - **`lines`** (`OutcomeLineInput[]`, required) - **`id`** (`string`, required): Your order line ID (unique within the order). - **`product`** (`string`, required): Your product ID (the one you pass to the SDK). - **`variant`** (`string`, optional) - **`size`** (`string`, required): The size bought, in your labels. - **`quantity`** (`integer`, required) - **`unit_price_minor`** (`integer`, optional): Price of one unit in minor units (5995 = 59.95). Optional; used for the refund value estimate. - **`tys_shown`** (`boolean`, optional): A TryYourSize element was shown for this purchase. - **`recommended_size`** (`string`, optional): The size TryYourSize recommended (the SDK's recommendation event, or the _tys_size cart line property). - **`attribution`** (`"cart_attribute" | "sdk" | "api"`, optional): Where recommended_size came from. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: What was recorded. - **`id`** (`string`, required) - **`external_id`** (`string`, required) - **`created`** (`boolean`, required): False when the order already existed and was updated. - **`experiment_arm`** (`"treatment" | "holdout"`, optional) - **`lines`** (`integer`, required) ## Example **Node.js** ```ts await tys.orders.create({ id: "1001", // your order ID: re-sending updates it ordered_at: new Date().toISOString(), currency: "EUR", visitor_id, // from tys.experiment() in the browser, saved with the cart lines: [ { id: "1", product: "TEE-001", size: "M", quantity: 1, recommended_size: "M", tys_shown: true }, ], }); ``` **Python** ```python from datetime import datetime, timezone tys.orders.create({ "id": "1001", "ordered_at": datetime.now(timezone.utc).isoformat(), "currency": "EUR", "visitor_id": visitor_id, "lines": [{"id": "1", "product": "TEE-001", "size": "M", "quantity": 1, "recommended_size": "M", "tys_shown": True}], }) ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/orders \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"id":"1001","ordered_at":"2026-10-26T10:00:00Z","lines":[{"id":"1","product":"TEE-001","size":"M","quantity":1}]}' ``` ## See also - [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md) - [orders.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createoutcomeorder.md) --- # PagePromise > What every list() returns: await it for one page ({ data, has_more, next_cursor }), or iterate it with for await to walk every item of every page. Source: https://docs.tryyoursize.com/reference/node/page-promise/ *Class* · `@tryyoursize/node` What every `list()` returns: await it for one page (`{ data, has_more, next_cursor }`), or iterate it with `for await` to walk every item of every page. ## Methods - **`then / catch / finally`** (`Promise`, required): Awaiting gives the first page. - **`[Symbol.asyncIterator]()`** (`AsyncGenerator`, required): `for await (const item of list)`: every item, following `next_cursor`. - **`pages()`** (`AsyncGenerator`, required): Every page, starting with the first. - **`autoPagingToArray(opts?)`** (`Promise`, required): Collects items across pages, at most `opts.limit` (default 10,000). - **`autoPagingEach(fn)`** (`Promise`, required): Calls `fn` for each item; return `false` to stop. ## Example **Node.js** ```ts const first = await tys.products.list({ limit: 20 }); const all = await tys.products.list().autoPagingToArray({ limit: 500 }); await tys.fitGroups.list().autoPagingEach((g) => { console.log(g.name); }); ``` **Python** ```python page = tys.products.list(limit=20) next_page = page.next_page() for product in page.auto_paging_iter(): ... ``` ## See also - [Pagination](https://docs.tryyoursize.com/guides/pagination.md) --- # tys.products.create() > tys.products.create() Source: https://docs.tryyoursize.com/reference/node/products-create/ *Method* · `@tryyoursize/node` ```ts create(input: ProductInput, options?: RequestOptions): Promise ``` ## Parameters - **`input`** (`ProductInput`, required) - **`external_id`** (`string`, required) - **`name`** (`string`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The product. - **`id`** (`string`, required) - **`external_id`** (`string`, required): The brand's SKU or style ID (unique per brand). - **`name`** (`string`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`published_version`** (`integer`, optional): Garment version shoppers are fitted against, if any. - **`draft_version`** (`integer`, optional): Garment version being edited, if any. - **`created_at`** (`string`, required) - **`updated_at`** (`string`, required) - **`fit_group_id`** (`string`, optional): The fit group whose garment this product uses (absent until grouped). - **`brand_id`** (`string`, optional): The brand the product belongs to; every store of the brand sees it. - **`home_store_id`** (`string`, optional): The store the product was created in. - **`retired_at`** (`string`, optional): Set when the product is retired; shoppers no longer get a size for it. - **`available`** (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). ## Example **Node.js** ```ts const product = await tys.products.create({ external_id: "TEE-001", // your SKU / style ID, unique per store name: "Basic tee", category: "tshirt", }); ``` **Python** ```python product = tys.products.create(external_id="TEE-001", name="Basic tee", category="tshirt") ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/products \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"external_id":"TEE-001","name":"Basic tee","category":"tshirt"}' ``` ## See also - [products.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createcatalogproduct.md) --- # tys.products.garments.createDraft() > Starts a new draft version (sizes, POMs in mm, grading, fabric). Source: https://docs.tryyoursize.com/reference/node/products-garments-create-draft/ *Method* · `@tryyoursize/node` Starts a new draft version (sizes, POMs in mm, grading, fabric). 422 errors carry `issues`. ```ts createDraft(idOrSku: string, garment: GarmentInput, options?: RequestOptions): Promise ``` ## Parameters - **`idOrSku`** (`string`, required): The product's TryYourSize ID or your SKU (URL-encoded for you, so `TEE/01` works). - **`garment`** (`GarmentInput`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`fit_intent`** (`FitIntent`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - **`sizes`** (`string[]`, required): Size labels, smallest to largest. - **`poms`** (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - **`grading`** (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - **`fabric`** (`Fabric`, required): What the tech pack / mill sheet says. - **`import_id`** (`string`, optional): Pattern import linked to this version (omit to unlink). - **`season`** (`string`, optional): Season tag, e.g. "SS27". - **`tolerances_mm`** (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - **`spec_source`** (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The draft garment version, with `issues` that still block publishing. ## Example **Node.js** ```ts const garment: GarmentInput = { category: "tshirt", fit_intent: "regular", sizes: ["S", "M", "L"], poms: { S: { chest_width: 480 }, M: { chest_width: 510 }, L: { chest_width: 540 } }, // mm fabric: { composition: [{ fiber: "cotton", pct: 100 }], weight_gsm: 180 }, }; const draft = await tys.products.garments.createDraft("TEE-001", garment); console.log(draft.version, draft.issues); // what still blocks publishing ``` **Python** ```python garment = { "category": "tshirt", "fit_intent": "regular", "sizes": ["S", "M", "L"], "poms": {"S": {"chest_width": 480}, "M": {"chest_width": 510}, "L": {"chest_width": 540}}, # mm "fabric": {"composition": [{"fiber": "cotton", "pct": 100}], "weight_gsm": 180}, } draft = tys.products.garments.create_draft("TEE-001", garment) print(draft["version"], draft["issues"]) ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/products/TEE-001/garments \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"category":"tshirt","fit_intent":"regular","sizes":["S","M","L"],"poms":{"S":{"chest_width":480},"M":{"chest_width":510},"L":{"chest_width":540}},"fabric":{"composition":[{"fiber":"cotton","pct":100}],"weight_gsm":180}}' ``` ## See also - [Catalog API](https://docs.tryyoursize.com/products/catalog-api.md) - [products.garments.createDraft (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createcataloggarmentdraft.md) --- # tys.products.garments.list() > Garment versions, newest first (a single page: has_more is always false). Source: https://docs.tryyoursize.com/reference/node/products-garments-list/ *Method* · `@tryyoursize/node` Garment versions, newest first (a single page: has_more is always false). ```ts list(idOrSku: string, options?: RequestOptions): PagePromise ``` ## Parameters - **`idOrSku`** (`string`, required): The product's TryYourSize ID or your SKU (URL-encoded for you, so `TEE/01` works). - **`options`** (`RequestOptions`, optional) ## Returns `PagePromise`: The product's garment versions, newest first (a single page). Await it for one page (`{ data, has_more, next_cursor }`), or `for await` over every item. See [PagePromise](https://docs.tryyoursize.com/reference/node/page-promise.md). ## Example **Node.js** ```ts const { data } = await tys.products.garments.list("TEE-001"); // newest first ``` **Python** ```python versions = tys.products.garments.list("TEE-001") # newest first ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/products/TEE-001/garments \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.garments.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listcataloggarments.md) --- # tys.products.list() > Newest first. Source: https://docs.tryyoursize.com/reference/node/products-list/ *Method* · `@tryyoursize/node` Newest first. Await for one page, or `for await` over every product. ```ts list(params: ProductListParams = {}, options?: RequestOptions): PagePromise ``` ## Parameters - **`params`** (`ProductListParams`, optional, default `{}`): `limit`, `cursor`, and `sku` to find one product by SKU. - **`options`** (`RequestOptions`, optional) ## Returns `PagePromise`: Products, newest first. Await it for one page (`{ data, has_more, next_cursor }`), or `for await` over every item. See [PagePromise](https://docs.tryyoursize.com/reference/node/page-promise.md). ## Example **Node.js** ```ts // One page const page = await tys.products.list({ limit: 50 }); // Every product, across pages for await (const p of tys.products.list()) { console.log(p.external_id, p.published_version); } // One product by SKU const { data } = await tys.products.list({ sku: "TEE-001" }); ``` **Python** ```python page = tys.products.list(limit=50) for product in tys.products.list().auto_paging_iter(): print(product["external_id"], product.get("published_version")) match = tys.products.list(sku="TEE-001") ``` **cURL** ```bash curl "https://api.tryyoursize.com/v1/products?limit=50" \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listcatalogproducts.md) --- # tys.products.retrieve() > tys.products.retrieve() Source: https://docs.tryyoursize.com/reference/node/products-retrieve/ *Method* · `@tryyoursize/node` ```ts retrieve(idOrSku: string, options?: RequestOptions): Promise ``` ## Parameters - **`idOrSku`** (`string`, required): The product's TryYourSize ID or your SKU (URL-encoded for you, so `TEE/01` works). - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The product. - **`id`** (`string`, required) - **`external_id`** (`string`, required): The brand's SKU or style ID (unique per brand). - **`name`** (`string`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`published_version`** (`integer`, optional): Garment version shoppers are fitted against, if any. - **`draft_version`** (`integer`, optional): Garment version being edited, if any. - **`created_at`** (`string`, required) - **`updated_at`** (`string`, required) - **`fit_group_id`** (`string`, optional): The fit group whose garment this product uses (absent until grouped). - **`brand_id`** (`string`, optional): The brand the product belongs to; every store of the brand sees it. - **`home_store_id`** (`string`, optional): The store the product was created in. - **`retired_at`** (`string`, optional): Set when the product is retired; shoppers no longer get a size for it. - **`available`** (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). ## Example **Node.js** ```ts const product = await tys.products.retrieve("TEE-001"); // TryYourSize ID or your SKU ``` **Python** ```python product = tys.products.retrieve("TEE-001") # TryYourSize ID or your SKU ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/products/TEE-001 \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcatalogproduct.md) --- # tys.products.sizeMap.retrieve() > tys.products.sizeMap.retrieve() Source: https://docs.tryyoursize.com/reference/node/products-size-map-retrieve/ *Method* · `@tryyoursize/node` ```ts retrieve(idOrSku: string, options?: RequestOptions): Promise ``` ## Parameters - **`idOrSku`** (`string`, required): The product's TryYourSize ID or your SKU (URL-encoded for you, so `TEE/01` works). - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The size map: Studio size → your size label. - **`entries`** (`object[]`, required) - **`unmapped_shop_values`** (`string[]`, optional): Variant size values of the product with no Studio size yet (read only). ## Example **Node.js** ```ts const map = await tys.products.sizeMap.retrieve("TEE-001"); console.log(map.entries, map.unmapped_shop_values); ``` **Python** ```python size_map = tys.products.size_map.retrieve("TEE-001") print(size_map["entries"], size_map.get("unmapped_shop_values")) ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/products/TEE-001/size_map \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.sizeMap.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcatalogsizemap.md) --- # tys.products.sizeMap.save() > Replaces the size map. Source: https://docs.tryyoursize.com/reference/node/products-size-map-save/ *Method* · `@tryyoursize/node` Replaces the size map. ```ts save(idOrSku: string, map: ProductSizeMapInput, options?: RequestOptions): Promise ``` ## Parameters - **`idOrSku`** (`string`, required): The product's TryYourSize ID or your SKU (URL-encoded for you, so `TEE/01` works). - **`map`** (`ProductSizeMapInput`, required): `{ entries: [{ studio_size, shop_value }] }`: replaces the whole map. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The saved size map. ## Example **Node.js** ```ts await tys.products.sizeMap.save("TEE-001", { entries: [ { studio_size: "S", shop_value: "Small" }, { studio_size: "M", shop_value: "Medium" }, ], }); ``` **Python** ```python tys.products.size_map.save("TEE-001", entries=[ {"studio_size": "S", "shop_value": "Small"}, {"studio_size": "M", "shop_value": "Medium"}, ]) ``` **cURL** ```bash curl -X PUT https://api.tryyoursize.com/v1/products/TEE-001/size_map \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"entries":[{"studio_size":"M","shop_value":"Medium"}]}' ``` ## See also - [products.sizeMap.save (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/savecatalogsizemap.md) --- # tys.products.update() > Replaces the SKU, name and category (all three required). Source: https://docs.tryyoursize.com/reference/node/products-update/ *Method* · `@tryyoursize/node` Replaces the SKU, name and category (all three required). ```ts update(idOrSku: string, input: ProductInput, options?: RequestOptions): Promise ``` ## Parameters - **`idOrSku`** (`string`, required): The product's TryYourSize ID or your SKU (URL-encoded for you, so `TEE/01` works). - **`input`** (`ProductInput`, required) - **`external_id`** (`string`, required) - **`name`** (`string`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: The updated product. ## Example **Node.js** ```ts await tys.products.update("TEE-001", { external_id: "TEE-001", name: "Basic tee (organic)", category: "tshirt" }); ``` **Python** ```python tys.products.update("TEE-001", external_id="TEE-001", name="Basic tee (organic)", category="tshirt") ``` **cURL** ```bash curl -X PATCH https://api.tryyoursize.com/v1/products/TEE-001 \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"external_id":"TEE-001","name":"Basic tee (organic)","category":"tshirt"}' ``` ## See also - [products.update (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/updatecatalogproduct.md) --- # tys.recommendations.create() > handle comes from the browser SDK (await tys.recommendationHandle()), products are your SKUs / style IDs. Source: https://docs.tryyoursize.com/reference/node/recommendations-create/ *Method* · `@tryyoursize/node` `handle` comes from the browser SDK (`await tys.recommendationHandle()`), `products` are your SKUs / style IDs. Key scope: `fit_checks:read`. ```ts create(params: RecommendationsRequest, options?: RequestOptions): Promise ``` ## Parameters - **`params`** (`RecommendationsRequest`, required) - **`handle`** (`string`, required): A recommendation handle from the browser SDK (`tys.recommendationHandle()`). - **`products`** (`string[]`, required): Product SKUs / style IDs (what you pass to the element as `product`). - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: `{ data: ServerRecommendation[] }`, one per product, in your size labels. Sizes only, never measurements. - **`product`** (`string`, required) - **`status`** (`"ready" | "computing" | "none" | "unavailable"`, required): `ready`: a size. `computing`: a simulation the shopper started is running (ask again later). `none`: simulated, no size can be worn. `unavailable`: no published garment for this product, not simulated on this body yet, or the store's plan has no fit checks left. A page view or this API never starts a simulation. - **`size`** (`string`, required): The recommended size in the store's label (the product's size map), or null. - **`studio_size`** (`string`, required): The same size as entered in TryYourSize Studio, or null. - **`wearable_sizes`** (`string[]`, required): Sizes that can be worn on this body, in the store's labels. - **`tryon_id`** (`string`, optional): The try-on that produced the size, when there is one. Send it back with orders for attribution. ## Example **Node.js** ```ts // handle: from the browser SDK, `await tys.recommendationHandle()`, sent to your server const { data } = await tys.recommendations.create({ handle, products: ["SKU-123", "SKU-456"], }); for (const r of data) { if (r.status === "ready") console.log(r.product, r.size, r.wearable_sizes); } ``` **Python** ```python answer = tys.recommendations.create(handle=handle, products=["SKU-123", "SKU-456"]) for rec in answer["data"]: if rec["status"] == "ready": print(rec["product"], rec["size"], rec["wearable_sizes"]) ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/recommendations \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"handle":"tys_rh_…","products":["SKU-123","SKU-456"]}' ``` ## See also - [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md) - [tys.recommendationHandle() (browser)](https://docs.tryyoursize.com/reference/web/client-recommendation-handle.md) - [recommendations.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createrecommendations.md) --- # RequestOptions > Options of one call: the last argument of every method. Source: https://docs.tryyoursize.com/reference/node/request-options/ *Type* · `@tryyoursize/node` Options of one call: the last argument of every method. ```ts interface RequestOptions ``` ## Properties - **`idempotencyKey`** (`string | undefined`, optional): Idempotency-Key for this POST (default: a random UUID, reused across this call's retries). - **`timeoutMs`** (`number | undefined`, optional): Overrides the client's timeout for this call. - **`maxRetries`** (`number | undefined`, optional): Overrides the client's maxRetries for this call. - **`apiVersion`** (`string | undefined`, optional): Overrides the client's TYS-Version for this call. - **`headers`** (`Record | undefined`, optional): Extra request headers. - **`signal`** (`AbortSignal | undefined`, optional): Aborts the call (not retried). ## Example **Node.js** ```ts await tys.orders.create(order, { idempotencyKey: `order-${order.id}`, timeoutMs: 10_000 }); ``` **Python** ```python tys.orders.create(order, idempotency_key=f"order-{order['id']}") ``` ## See also - [Idempotency and retries](https://docs.tryyoursize.com/guides/idempotency.md) --- # tys.returns.create() > tys.returns.create() Source: https://docs.tryyoursize.com/reference/node/returns-create/ *Method* · `@tryyoursize/node` ```ts create(ret: OutcomeReturnInput, options?: RequestOptions): Promise ``` ## Parameters - **`ret`** (`OutcomeReturnInput`, required) - **`id`** (`string`, required): Your return (or RMA) ID. - **`order_id`** (`string`, required) - **`line_id`** (`string`, required) - **`quantity`** (`integer`, required) - **`reason`** (`string`, optional): size_too_small, size_too_large, fit_other, style, quality, damaged_or_wrong, changed_mind, other; Shopify's ReturnReason codes (SIZE_TOO_SMALL…) and common returns-platform words are mapped. Empty: unspecified. - **`exchange`** (`boolean`, optional): The item was exchanged rather than refunded. - **`exchange_size`** (`string`, optional): The size sent in exchange (implies exchange). - **`returned_at`** (`string`, required) - **`options`** (`RequestOptions`, optional) ## Returns `Promise`: What was recorded. ## Example **Node.js** ```ts await tys.returns.create({ id: "RMA-7", order_id: "1001", line_id: "1", quantity: 1, reason: "size_too_small", returned_at: new Date().toISOString(), }); ``` **Python** ```python tys.returns.create({ "id": "RMA-7", "order_id": "1001", "line_id": "1", "quantity": 1, "reason": "size_too_small", "returned_at": datetime.now(timezone.utc).isoformat(), }) ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/returns \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"id":"RMA-7","order_id":"1001","line_id":"1","quantity":1,"reason":"size_too_small","returned_at":"2026-11-02T09:00:00Z"}' ``` ## See also - [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md) - [returns.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createoutcomereturn.md) --- # tys.webhooks.constructEvent() > Verifies a delivery's TYS-Signature header against the raw request body and returns the parsed event. Source: https://docs.tryyoursize.com/reference/node/webhooks-construct-event/ *Method* · `@tryyoursize/node` Verifies a delivery's `TYS-Signature` header against the raw request body and returns the parsed event. Also exported as a function: `import { constructEvent } from "@tryyoursize/node"`. ```ts constructEvent( rawBody: RawBody, signatureHeader: string | string[] | null | undefined, secret: string, opts?: ConstructEventOptions, ): WebhookEvent ``` ## Parameters - **`rawBody`** (`RawBody`, required): The raw request body (string or Buffer), not parsed JSON. With Express use `express.raw({ type: "application/json" })`. - **`signatureHeader`** (`string | string[] | null | undefined`, required): The `TYS-Signature` header. - **`secret`** (`string`, required): The endpoint's signing secret (`whsec_…`) from Studio → Developers → Webhooks. - **`opts`** (`ConstructEventOptions`, optional) - **`toleranceSeconds`** (`number | undefined`, optional): Maximum |now − t| in seconds (default 300). 0 or less disables the check. - **`now`** (`number | Date | undefined`, optional): The current time (unix seconds or a Date); for tests. ## Returns `WebhookEvent`: The event. Narrow on `type` to get typed `data`. Payloads never carry shopper identity or measurements. ## Errors `SignatureVerificationError` when no signature matches or the timestamp is outside the tolerance (5 minutes by default). ## Example **Node.js** ```ts app.post("/webhooks/tryyoursize", express.raw({ type: "application/json" }), (req, res) => { let event; try { event = tys.webhooks.constructEvent(req.body, req.headers["tys-signature"], process.env.TYS_WEBHOOK_SECRET!); } catch { return res.sendStatus(400); } if (event.type === "garment.published") console.log(event.data.product_skus); res.sendStatus(200); }); ``` **Python** ```python from flask import Flask, request @app.post("/webhooks/tryyoursize") def webhook(): try: event = tryyoursize.Webhook.construct_event( request.get_data(), request.headers.get("TYS-Signature", ""), os.environ["TYS_WEBHOOK_SECRET"] ) except tryyoursize.SignatureVerificationError: return "", 400 if event["type"] == "garment.published": print(event["data"]["product_skus"]) return "", 200 ``` ## See also - [Webhooks guide](https://docs.tryyoursize.com/guides/webhooks.md) --- # tys.webhooks.generateTestHeader() > Builds a valid TYS-Signature header for a payload, to test your webhook handler. Source: https://docs.tryyoursize.com/reference/node/webhooks-generate-test-header/ *Method* · `@tryyoursize/node` Builds a valid `TYS-Signature` header for a payload, to test your webhook handler. ```ts generateTestHeader(opts: Parameters[0]): string ``` ## Parameters - **`opts`** (`Parameters[0]`, required): `{ payload, secret, timestamp? }`: the body, the secret (or several, as during a rotation) and the unix time (default now). ## Returns `string`: The header value: `t=,v1=`. ## Example **Node.js** ```ts const payload = JSON.stringify({ id: "evt_test", object: "event", type: "webhook.test", data: {} }); const header = tys.webhooks.generateTestHeader({ payload, secret: "whsec_test" }); await fetch("http://localhost:3000/webhooks/tryyoursize", { method: "POST", headers: { "TYS-Signature": header, "Content-Type": "application/json" }, body: payload, }); ``` **Python** ```python payload = json.dumps({"id": "evt_test", "object": "event", "type": "webhook.test", "data": {}}) header = tryyoursize.Webhook.generate_test_header(payload, "whsec_test") ``` --- # tys.webhooks.verifySignature() > Checks the signature only (throws when it does not verify). Source: https://docs.tryyoursize.com/reference/node/webhooks-verify-signature/ *Method* · `@tryyoursize/node` Checks the signature only (throws when it does not verify). Use `constructEvent()` unless you parse the body yourself. ```ts verifySignature( rawBody: RawBody, signatureHeader: string | string[] | null | undefined, secret: string, opts?: ConstructEventOptions, ): void ``` ## Parameters - **`rawBody`** (`RawBody`, required): The raw request body. - **`signatureHeader`** (`string | string[] | null | undefined`, required): The `TYS-Signature` header. - **`secret`** (`string`, required): The signing secret (`whsec_…`). - **`opts`** (`ConstructEventOptions`, optional) - **`toleranceSeconds`** (`number | undefined`, optional): Maximum |now − t| in seconds (default 300). 0 or less disables the check. - **`now`** (`number | Date | undefined`, optional): The current time (unix seconds or a Date); for tests. ## Example **Node.js** ```ts tys.webhooks.verifySignature(rawBody, req.headers["tys-signature"], secret); // throws SignatureVerificationError ``` **Python** ```python tryyoursize.Webhook.verify_signature(payload, sig_header, secret) # True, or raises SignatureVerificationError ``` --- # Python reference (tryyoursize) > The server SDK for Python 3.9+, standard library only: recommendations, catalog, orders and returns, audit log, webhooks. Source: https://docs.tryyoursize.com/reference/python/ *SDK* · `tryyoursize` · v0.1.0 The official Python SDK for the TryYourSize server API: no third-party dependencies (standard library only), Python 3.9+, fully typed (`py.typed`). Use it **on your server** with a secret key (`sk_test_…` / `sk_live_…`) or a restricted key (`rk_…`); publishable keys (`pk_…`) are refused. Responses are plain dicts typed with `TypedDict`s generated from our OpenAPI spec. ## Install **pip** ```bash pip install tryyoursize ``` **npm** ```bash npm install @tryyoursize/node ``` ## Initialize **Python** ```python tys = tryyoursize.TryYourSize(os.environ["TRYYOURSIZE_SECRET_KEY"], max_retries=2, timeout=30.0) ``` **Node.js** ```ts const tys = new TryYourSize(process.env.TRYYOURSIZE_SECRET_KEY!, { maxRetries: 2, // network errors, timeouts, 429, 5xx timeoutMs: 30_000, }); ``` Every method maps to one HTTP operation of the [HTTP API reference](https://docs.tryyoursize.com/api/reference.md). Errors are typed: see [Errors](https://docs.tryyoursize.com/reference/python/errors.md) and the [API error codes](https://docs.tryyoursize.com/api/errors.md). ## Client | | | |---|---| | [`TryYourSize()`](https://docs.tryyoursize.com/reference/python/client.md) | Creates a client for one secret or restricted key. | | [`mode`](https://docs.tryyoursize.com/reference/python/mode.md) | "test" or "live", from the key prefix ("unknown" for other formats). | ## Recommendations | | | |---|---| | [`recommendations.create()`](https://docs.tryyoursize.com/reference/python/recommendations-create.md) | The shopper's size for up to 25 products (SKUs), from a browser SDK handle. | ## Products | | | |---|---| | [`products.list()`](https://docs.tryyoursize.com/reference/python/products-list.md) | The store's products, newest first. | | [`products.create()`](https://docs.tryyoursize.com/reference/python/products-create.md) | Create a product. | | [`products.retrieve()`](https://docs.tryyoursize.com/reference/python/products-retrieve.md) | A product by its TryYourSize ID or its SKU. | | [`products.update()`](https://docs.tryyoursize.com/reference/python/products-update.md) | Replace the SKU, name and category (all three required). | | [`products.size_map.retrieve()`](https://docs.tryyoursize.com/reference/python/products-size-map-retrieve.md) | A product's size map (Studio size → your size label). | | [`products.size_map.save()`](https://docs.tryyoursize.com/reference/python/products-size-map-save.md) | Replace the size map, e.g. | | [`products.garments.list()`](https://docs.tryyoursize.com/reference/python/products-garments-list.md) | Garment versions of a product, newest first (has_more is always false). | | [`products.garments.create_draft()`](https://docs.tryyoursize.com/reference/python/products-garments-create-draft.md) | Start a draft version: sizes, points of measure (mm) per size, grading, fabric. | ## Garments | | | |---|---| | [`garments.retrieve()`](https://docs.tryyoursize.com/reference/python/garments-retrieve.md) | A garment version. | | [`garments.update_draft()`](https://docs.tryyoursize.com/reference/python/garments-update-draft.md) | Replace a draft's content. | | [`garments.publish()`](https://docs.tryyoursize.com/reference/python/garments-publish.md) | Publish a complete draft (same checks as Studio; 422 lists what is missing). | ## Fit groups | | | |---|---| | [`fit_groups.list()`](https://docs.tryyoursize.com/reference/python/fit-groups-list.md) | Fit groups, cursor-paginated. | | [`fit_groups.create()`](https://docs.tryyoursize.com/reference/python/fit-groups-create.md) | Create a fit group: one garment shared by several products (colourways, prints). | | [`fit_groups.retrieve()`](https://docs.tryyoursize.com/reference/python/fit-groups-retrieve.md) | A fit group. | | [`fit_groups.link_products()`](https://docs.tryyoursize.com/reference/python/fit-groups-link-products.md) | Move products into the fit group; they then use its garment. | ## Outcomes | | | |---|---| | [`orders.create()`](https://docs.tryyoursize.com/reference/python/orders-create.md) | Record an order (idempotent by order id; re-sending updates it). | | [`returns.create()`](https://docs.tryyoursize.com/reference/python/returns-create.md) | Record a return of an order line (idempotent by return id and line). | ## Audit log | | | |---|---| | [`audit_events.list()`](https://docs.tryyoursize.com/reference/python/audit-events-list.md) | The audit log of the key's organization, newest first. | ## Webhooks | | | |---|---| | [`Webhook.construct_event()`](https://docs.tryyoursize.com/reference/python/webhook-construct-event.md) | Verify the signature, then return the parsed event envelope. | | [`Webhook.verify_signature()`](https://docs.tryyoursize.com/reference/python/webhook-verify-signature.md) | Return True or raise SignatureVerificationError. | | [`Webhook.generate_test_header()`](https://docs.tryyoursize.com/reference/python/webhook-generate-test-header.md) | A valid TYS-Signature value, for your own tests. | ## Pagination and errors | | | |---|---| | [`ListPage`](https://docs.tryyoursize.com/reference/python/list-page.md) | One page of a list. | | [`Errors`](https://docs.tryyoursize.com/reference/python/errors.md) | Every exception is a TryYourSizeError. | --- # tys.audit_events.list() > The audit log of the key's organization, newest first. Source: https://docs.tryyoursize.com/reference/python/audit-events-list/ *Method* · `tryyoursize` The audit log of the key's organization, newest first. Scope `audit:read`. Filters: `action` (a trailing `.` matches a prefix), `actor` (`actor_type:actor_id`), `store_id`, `target`, `from_` (or `**{"from": ...}`), `to` (`datetime` or RFC 3339 string), `limit` (≤ 200), `cursor`. ```python def list(**filters: Any) -> ListPage ``` ## Parameters - **`**filters`** (`Any`, required): `action` (a trailing `.` matches a prefix), `actor` (`actor_type:actor_id`), `store_id`, `target`, `from_` and `to` (datetime or RFC 3339), `limit` (≤ 200), `cursor`. Unknown filters raise `TypeError`. ## Returns `ListPage`: Audit events, newest first. A dict with `data`, `has_more`, `next_cursor`; `next_page()` and `auto_paging_iter()` walk the rest. See [ListPage](https://docs.tryyoursize.com/reference/python/list-page.md). ## Example **Python** ```python for event in tys.audit_events.list(action="api_key.").auto_paging_iter(): print(event["created_at"], event["actor_type"], event["action"]) ``` **Node.js** ```ts for await (const e of tys.auditEvents.list({ action: "api_key." })) { console.log(e.created_at, e.actor_type, e.action); } ``` **cURL** ```bash curl "https://api.tryyoursize.com/v1/audit_events?action=api_key." \ -H "Authorization: Bearer $TRYYOURSIZE_RESTRICTED_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [audit_events.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listauditeventswithkey.md) --- # TryYourSize() > Creates a client for one secret or restricted key. Source: https://docs.tryyoursize.com/reference/python/client/ *Constructor* · `tryyoursize` Creates a client for one secret or restricted key. Create it once and reuse it. ```python def __init__(api_key: str, *, api_version: Optional[str] = None, base_url: str = DEFAULT_BASE_URL, max_retries: int = 2, timeout: float = 30.0, transport: Optional[Transport] = None, sleep: Optional[Callable[[float], None]] = None) -> None ``` ## Parameters - **`api_key`** (`str`, required): A secret key (`sk_test_…` / `sk_live_…`) or a restricted key (`rk_…`). - **`api_version`** (`Optional[str]`, optional, default `None`): The `TYS-Version` to send (default: the version this release is built against, `API_VERSION`). - **`base_url`** (`str`, optional, default `DEFAULT_BASE_URL`): API origin; change only for local development. - **`max_retries`** (`int`, optional, default `2`): Retries of network errors, timeouts, 429, 5xx and `409 idempotency_key_in_use`. - **`timeout`** (`float`, optional, default `30.0`): Per-attempt timeout in seconds. - **`transport`** (`Optional[Transport]`, optional, default `None`): Replaces the HTTP call (tests). - **`sleep`** (`Optional[Callable[[float], None]]`, optional, default `None`): Replaces the wait between retries (tests). ## Errors `TryYourSizeError` for a missing key or a publishable key. ## Example **Python** ```python tys = tryyoursize.TryYourSize(os.environ["TRYYOURSIZE_SECRET_KEY"], max_retries=2, timeout=30.0) ``` **Node.js** ```ts const tys = new TryYourSize(process.env.TRYYOURSIZE_SECRET_KEY!, { maxRetries: 2, // network errors, timeouts, 429, 5xx timeoutMs: 30_000, }); ``` --- # Errors > Every exception is a TryYourSizeError. Source: https://docs.tryyoursize.com/reference/python/errors/ *Class* · `tryyoursize` Every exception is a `TryYourSizeError`. API answers raise an `APIError` subclass by status: `InvalidRequestError` (400, 422; `issues` for garments), `AuthenticationError` (401), `PermissionDeniedError` (403), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429, `retry_after`), `ServerError` (5xx). `APIConnectionError` when no answer came; `SignatureVerificationError` for webhooks. ```python class APIError(TryYourSizeError) ``` ## Attributes of APIError - **`status`** (`int`, required): The HTTP status. - **`code`** (`Optional[str]`, required): The stable error code ([API error codes](https://docs.tryyoursize.com/api/errors.md)). - **`doc_url`** (`Optional[str]`, required): Link to the code's explanation. - **`request_id`** (`Optional[str]`, required): Quote it to support. - **`body`** (`Dict[str, Any]`, required): The parsed error body. - **`issues`** (`list`, required): Garment issues of a 422; empty otherwise. ## Example **Python** ```python try: tys.products.retrieve("NO-SUCH-SKU") except tryyoursize.NotFoundError: pass except tryyoursize.APIError as err: print(err.status, err.code, err.request_id) ``` **Node.js** ```ts try { await tys.products.retrieve("NO-SUCH-SKU"); } catch (err) { if (err instanceof RateLimitError) await sleep((err.retryAfter ?? 1) * 1000); else if (err instanceof TryYourSizeError) console.error(err.status, err.code, err.requestId); } ``` --- # tys.fit_groups.create() > Create a fit group: one garment shared by several products (colourways, prints). Source: https://docs.tryyoursize.com/reference/python/fit-groups-create/ *Method* · `tryyoursize` Create a fit group: one garment shared by several products (colourways, prints). ```python def create(*, name: str, product_ids: Optional[Iterable[str]] = None, source_product_id: Optional[str] = None, idempotency_key: Optional[str] = None) -> FitGroup ``` ## Parameters - **`name`** (`str`, required): The group's name. - **`product_ids`** (`Optional[Iterable[str]]`, optional, default `None`): Products to put in the new group. - **`source_product_id`** (`Optional[str]`, optional, default `None`): The new group takes over this product's garment versions. - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `FitGroup`: The fit group. ## Example **Python** ```python group = tys.fit_groups.create(name="Basic tee", product_ids=[navy["id"], black["id"]]) ``` **Node.js** ```ts const group = await tys.fitGroups.create({ name: "Basic tee", product_ids: [navy.id, black.id], // colourways that share one garment }); ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/fit_groups \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"name":"Basic tee","product_ids":["",""]}' ``` ## See also - [fit_groups.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createcatalogfitgroup.md) --- # tys.fit_groups.link_products() > Move products into the fit group; they then use its garment. Source: https://docs.tryyoursize.com/reference/python/fit-groups-link-products/ *Method* · `tryyoursize` Move products into the fit group; they then use its garment. ```python def link_products(fit_group_id: str, *, product_ids: Iterable[str], idempotency_key: Optional[str] = None) -> FitGroup ``` ## Parameters - **`fit_group_id`** (`str`, required): The fit group ID. - **`product_ids`** (`Iterable[str]`, required): Products to move into the group. - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `FitGroup`: The fit group. ## Example **Python** ```python tys.fit_groups.link_products(group["id"], product_ids=[white["id"]]) ``` **Node.js** ```ts await tys.fitGroups.linkProducts(group.id, { product_ids: [white.id] }); ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/fit_groups//products \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"product_ids":[""]}' ``` ## See also - [fit_groups.link_products (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/linkcatalogfitgroupproducts.md) --- # tys.fit_groups.list() > Fit groups, cursor-paginated. Source: https://docs.tryyoursize.com/reference/python/fit-groups-list/ *Method* · `tryyoursize` Fit groups, cursor-paginated. ```python def list(*, limit: Optional[int] = None, cursor: Optional[str] = None) -> ListPage ``` ## Parameters - **`limit`** (`Optional[int]`, optional, default `None`): Page size. - **`cursor`** (`Optional[str]`, optional, default `None`): `next_cursor` of the previous page. ## Returns `ListPage`: Fit groups. A dict with `data`, `has_more`, `next_cursor`; `next_page()` and `auto_paging_iter()` walk the rest. See [ListPage](https://docs.tryyoursize.com/reference/python/list-page.md). ## Example **Python** ```python for group in tys.fit_groups.list().auto_paging_iter(): print(group["id"], group["name"]) ``` **Node.js** ```ts for await (const g of tys.fitGroups.list()) console.log(g.id, g.name); ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/fit_groups \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [fit_groups.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listcatalogfitgroups.md) --- # tys.fit_groups.retrieve() > A fit group. Source: https://docs.tryyoursize.com/reference/python/fit-groups-retrieve/ *Method* · `tryyoursize` A fit group. ```python def retrieve(fit_group_id: str) -> FitGroup ``` ## Parameters - **`fit_group_id`** (`str`, required): The fit group ID. ## Returns `FitGroup`: The fit group. ## Example **Python** ```python group = tys.fit_groups.retrieve(group_id) ``` **Node.js** ```ts const group = await tys.fitGroups.retrieve(groupId); ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/fit_groups/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [fit_groups.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcatalogfitgroup.md) --- # tys.garments.publish() > Publish a complete draft (same checks as Studio; 422 lists what is missing). Source: https://docs.tryyoursize.com/reference/python/garments-publish/ *Method* · `tryyoursize` Publish a complete draft (same checks as Studio; 422 lists what is missing). ```python def publish(garment_id: str, *, idempotency_key: Optional[str] = None) -> GarmentVersion ``` ## Parameters - **`garment_id`** (`str`, required): The draft's ID. - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `GarmentVersion`: The published version. ## Errors `InvalidRequestError` (422); `err.issues` lists what is missing. ## Example **Python** ```python try: tys.garments.publish(draft["id"]) except tryyoursize.InvalidRequestError as err: print(err.issues) # what is missing ``` **Node.js** ```ts try { await tys.garments.publish(draft.id); } catch (err) { if (err instanceof InvalidRequestError) console.log(err.issues); // what is missing } ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/garments//publish \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [garments.publish (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/publishcataloggarment.md) --- # tys.garments.retrieve() > A garment version. Source: https://docs.tryyoursize.com/reference/python/garments-retrieve/ *Method* · `tryyoursize` A garment version. ```python def retrieve(garment_id: str) -> GarmentVersion ``` ## Parameters - **`garment_id`** (`str`, required): The garment version ID. ## Returns `GarmentVersion`: The garment version. ## Example **Python** ```python version = tys.garments.retrieve(garment_id) ``` **Node.js** ```ts const version = await tys.garments.retrieve(garmentId); ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/garments/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [garments.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcataloggarment.md) --- # tys.garments.update_draft() > Replace a draft's content. Source: https://docs.tryyoursize.com/reference/python/garments-update-draft/ *Method* · `tryyoursize` Replace a draft's content. Published versions never change (409). ```python def update_draft(garment_id: str, garment: GarmentInput) -> GarmentVersion ``` ## Parameters - **`garment_id`** (`str`, required): The draft's ID. - **`garment`** (`GarmentInput`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`fit_intent`** (`FitIntent`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - **`sizes`** (`string[]`, required): Size labels, smallest to largest. - **`poms`** (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - **`grading`** (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - **`fabric`** (`Fabric`, required): What the tech pack / mill sheet says. - **`import_id`** (`string`, optional): Pattern import linked to this version (omit to unlink). - **`season`** (`string`, optional): Season tag, e.g. "SS27". - **`tolerances_mm`** (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - **`spec_source`** (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. ## Returns `GarmentVersion`: The updated draft. Published versions never change (409). ## Example **Python** ```python tys.garments.update_draft(draft["id"], {**garment, "season": "SS27"}) # replaces the draft ``` **Node.js** ```ts await tys.garments.updateDraft(draft.id, { ...garment, season: "SS27" }); // replaces the draft ``` **cURL** ```bash curl -X PUT https://api.tryyoursize.com/v1/garments/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"category":"tshirt","fit_intent":"regular","sizes":["S","M","L"],"poms":{"S":{"chest_width":480},"M":{"chest_width":510},"L":{"chest_width":540}},"fabric":{"composition":[{"fiber":"cotton","pct":100}],"weight_gsm":180}}' ``` ## See also - [garments.update_draft (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/updatecataloggarmentdraft.md) --- # ListPage > One page of a list. Source: https://docs.tryyoursize.com/reference/python/list-page/ *Class* · `tryyoursize` One page of a list. A `dict` with `data`, `has_more` and `next_cursor` (also as attributes). `auto_paging_iter()` yields every item, fetching the following pages lazily. ```python class ListPage(Dict[str, Any]) ``` ## Members - **`data`** (`list`, required): The items of this page. - **`has_more`** (`bool`, required): Whether another page follows. - **`next_cursor`** (`Optional[str]`, required): Cursor of the next page. - **`next_page()`** (`Optional[ListPage]`, required): The following page, or `None` at the end. - **`auto_paging_iter()`** (`Iterator[Any]`, required): Every item of this page and all following pages. ## Example **Python** ```python page = tys.products.list(limit=20) while page is not None: for product in page.data: print(product["external_id"]) page = page.next_page() ``` **Node.js** ```ts for await (const page of tys.products.list({ limit: 20 }).pages()) { for (const p of page.data) console.log(p.external_id); } ``` ## See also - [Pagination](https://docs.tryyoursize.com/guides/pagination.md) --- # tys.mode > "test" or "live", from the key prefix ("unknown" for other formats). Source: https://docs.tryyoursize.com/reference/python/mode/ *Property* · `tryyoursize` `"test"` or `"live"`, from the key prefix (`"unknown"` for other formats). ```python @property def mode() -> str ``` ## Example **Python** ```python if tys.mode == "live": enable_order_sync() ``` **Node.js** ```ts if (tys.mode === "live") enableOrderSync(); ``` --- # tys.orders.create() > Record an order (idempotent by order id; re-sending updates it). Source: https://docs.tryyoursize.com/reference/python/orders-create/ *Method* · `tryyoursize` Record an order (idempotent by order `id`; re-sending updates it). No customer data. ```python def create(order: OutcomeOrderInput, *, idempotency_key: Optional[str] = None) -> OutcomeOrderResult ``` ## Parameters - **`order`** (`OutcomeOrderInput`, required) - **`id`** (`string`, required): Your order ID. - **`ordered_at`** (`string`, required) - **`currency`** (`string`, optional): ISO 4217, e.g. EUR. - **`visitor_id`** (`string`, optional): The SDK's anonymous visitor ID (`(await tys.experiment())?.visitorId`), saved with the cart. TryYourSize recomputes the A/B arm from it and stores only its hash. Never a customer ID. - **`experiment_arm`** (`"treatment" | "holdout"`, optional): The arm, when you cannot send visitor_id. - **`lines`** (`OutcomeLineInput[]`, required) - **`id`** (`string`, required): Your order line ID (unique within the order). - **`product`** (`string`, required): Your product ID (the one you pass to the SDK). - **`variant`** (`string`, optional) - **`size`** (`string`, required): The size bought, in your labels. - **`quantity`** (`integer`, required) - **`unit_price_minor`** (`integer`, optional): Price of one unit in minor units (5995 = 59.95). Optional; used for the refund value estimate. - **`tys_shown`** (`boolean`, optional): A TryYourSize element was shown for this purchase. - **`recommended_size`** (`string`, optional): The size TryYourSize recommended (the SDK's recommendation event, or the _tys_size cart line property). - **`attribution`** (`"cart_attribute" | "sdk" | "api"`, optional): Where recommended_size came from. - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `OutcomeOrderResult`: What was recorded. - **`id`** (`string`, required) - **`external_id`** (`string`, required) - **`created`** (`boolean`, required): False when the order already existed and was updated. - **`experiment_arm`** (`"treatment" | "holdout"`, optional) - **`lines`** (`integer`, required) ## Example **Python** ```python from datetime import datetime, timezone tys.orders.create({ "id": "1001", "ordered_at": datetime.now(timezone.utc).isoformat(), "currency": "EUR", "visitor_id": visitor_id, "lines": [{"id": "1", "product": "TEE-001", "size": "M", "quantity": 1, "recommended_size": "M", "tys_shown": True}], }) ``` **Node.js** ```ts await tys.orders.create({ id: "1001", // your order ID: re-sending updates it ordered_at: new Date().toISOString(), currency: "EUR", visitor_id, // from tys.experiment() in the browser, saved with the cart lines: [ { id: "1", product: "TEE-001", size: "M", quantity: 1, recommended_size: "M", tys_shown: true }, ], }); ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/orders \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"id":"1001","ordered_at":"2026-10-26T10:00:00Z","lines":[{"id":"1","product":"TEE-001","size":"M","quantity":1}]}' ``` ## See also - [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md) - [orders.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createoutcomeorder.md) --- # tys.products.create() > Create a product. Source: https://docs.tryyoursize.com/reference/python/products-create/ *Method* · `tryyoursize` Create a product. `external_id` (SKU / style ID) is unique per store (409). ```python def create(*, external_id: str, name: str, category: str, idempotency_key: Optional[str] = None) -> Product ``` ## Parameters - **`external_id`** (`str`, required): Your SKU / style ID, unique per store (409 otherwise). - **`name`** (`str`, required): The product name. - **`category`** (`str`, required): The garment category, e.g. `tshirt`, `jeans`. - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `Product`: The product. - **`id`** (`string`, required) - **`external_id`** (`string`, required): The brand's SKU or style ID (unique per brand). - **`name`** (`string`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`published_version`** (`integer`, optional): Garment version shoppers are fitted against, if any. - **`draft_version`** (`integer`, optional): Garment version being edited, if any. - **`created_at`** (`string`, required) - **`updated_at`** (`string`, required) - **`fit_group_id`** (`string`, optional): The fit group whose garment this product uses (absent until grouped). - **`brand_id`** (`string`, optional): The brand the product belongs to; every store of the brand sees it. - **`home_store_id`** (`string`, optional): The store the product was created in. - **`retired_at`** (`string`, optional): Set when the product is retired; shoppers no longer get a size for it. - **`available`** (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). ## Example **Python** ```python product = tys.products.create(external_id="TEE-001", name="Basic tee", category="tshirt") ``` **Node.js** ```ts const product = await tys.products.create({ external_id: "TEE-001", // your SKU / style ID, unique per store name: "Basic tee", category: "tshirt", }); ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/products \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"external_id":"TEE-001","name":"Basic tee","category":"tshirt"}' ``` ## See also - [products.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createcatalogproduct.md) --- # tys.products.garments.create_draft() > Start a draft version: sizes, points of measure (mm) per size, grading, fabric. Source: https://docs.tryyoursize.com/reference/python/products-garments-create-draft/ *Method* · `tryyoursize` Start a draft version: sizes, points of measure (mm) per size, grading, fabric. A 422 raises `InvalidRequestError` with the issues in `err.body["issues"]`. ```python def create_draft(product: str, garment: GarmentInput, *, idempotency_key: Optional[str] = None) -> GarmentVersion ``` ## Parameters - **`product`** (`str`, required): The product's TryYourSize ID or your SKU (URL-encoded for you). - **`garment`** (`GarmentInput`, required): The garment: category, fit intent, sizes, points of measure per size (mm), grading and fabric. - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`fit_intent`** (`FitIntent`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - **`sizes`** (`string[]`, required): Size labels, smallest to largest. - **`poms`** (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - **`grading`** (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - **`fabric`** (`Fabric`, required): What the tech pack / mill sheet says. - **`import_id`** (`string`, optional): Pattern import linked to this version (omit to unlink). - **`season`** (`string`, optional): Season tag, e.g. "SS27". - **`tolerances_mm`** (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - **`spec_source`** (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `GarmentVersion`: The draft garment version, with `issues` that still block publishing. ## Example **Python** ```python garment = { "category": "tshirt", "fit_intent": "regular", "sizes": ["S", "M", "L"], "poms": {"S": {"chest_width": 480}, "M": {"chest_width": 510}, "L": {"chest_width": 540}}, # mm "fabric": {"composition": [{"fiber": "cotton", "pct": 100}], "weight_gsm": 180}, } draft = tys.products.garments.create_draft("TEE-001", garment) print(draft["version"], draft["issues"]) ``` **Node.js** ```ts const garment: GarmentInput = { category: "tshirt", fit_intent: "regular", sizes: ["S", "M", "L"], poms: { S: { chest_width: 480 }, M: { chest_width: 510 }, L: { chest_width: 540 } }, // mm fabric: { composition: [{ fiber: "cotton", pct: 100 }], weight_gsm: 180 }, }; const draft = await tys.products.garments.createDraft("TEE-001", garment); console.log(draft.version, draft.issues); // what still blocks publishing ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/products/TEE-001/garments \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"category":"tshirt","fit_intent":"regular","sizes":["S","M","L"],"poms":{"S":{"chest_width":480},"M":{"chest_width":510},"L":{"chest_width":540}},"fabric":{"composition":[{"fiber":"cotton","pct":100}],"weight_gsm":180}}' ``` ## See also - [products.garments.create_draft (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createcataloggarmentdraft.md) --- # tys.products.garments.list() > Garment versions of a product, newest first (has_more is always false). Source: https://docs.tryyoursize.com/reference/python/products-garments-list/ *Method* · `tryyoursize` Garment versions of a product, newest first (`has_more` is always false). ```python def list(product: str) -> ListPage ``` ## Parameters - **`product`** (`str`, required): The product's TryYourSize ID or your SKU (URL-encoded for you). ## Returns `ListPage`: Garment versions, newest first (a single page). A dict with `data`, `has_more`, `next_cursor`; `next_page()` and `auto_paging_iter()` walk the rest. See [ListPage](https://docs.tryyoursize.com/reference/python/list-page.md). ## Example **Python** ```python versions = tys.products.garments.list("TEE-001") # newest first ``` **Node.js** ```ts const { data } = await tys.products.garments.list("TEE-001"); // newest first ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/products/TEE-001/garments \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.garments.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listcataloggarments.md) --- # tys.products.list() > The store's products, newest first. Source: https://docs.tryyoursize.com/reference/python/products-list/ *Method* · `tryyoursize` The store's products, newest first. `sku` finds one product by SKU / style ID. ```python def list(*, sku: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> ListPage ``` ## Parameters - **`sku`** (`Optional[str]`, optional, default `None`): Find one product by SKU / style ID. - **`limit`** (`Optional[int]`, optional, default `None`): Page size. - **`cursor`** (`Optional[str]`, optional, default `None`): `next_cursor` of the previous page. ## Returns `ListPage`: Products, newest first. A dict with `data`, `has_more`, `next_cursor`; `next_page()` and `auto_paging_iter()` walk the rest. See [ListPage](https://docs.tryyoursize.com/reference/python/list-page.md). ## Example **Python** ```python page = tys.products.list(limit=50) for product in tys.products.list().auto_paging_iter(): print(product["external_id"], product.get("published_version")) match = tys.products.list(sku="TEE-001") ``` **Node.js** ```ts // One page const page = await tys.products.list({ limit: 50 }); // Every product, across pages for await (const p of tys.products.list()) { console.log(p.external_id, p.published_version); } // One product by SKU const { data } = await tys.products.list({ sku: "TEE-001" }); ``` **cURL** ```bash curl "https://api.tryyoursize.com/v1/products?limit=50" \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.list (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/listcatalogproducts.md) --- # tys.products.retrieve() > A product by its TryYourSize ID or its SKU. Source: https://docs.tryyoursize.com/reference/python/products-retrieve/ *Method* · `tryyoursize` A product by its TryYourSize ID or its SKU. ```python def retrieve(product: str) -> Product ``` ## Parameters - **`product`** (`str`, required): The product's TryYourSize ID or your SKU (URL-encoded for you). ## Returns `Product`: The product. - **`id`** (`string`, required) - **`external_id`** (`string`, required): The brand's SKU or style ID (unique per brand). - **`name`** (`string`, required) - **`category`** (`GarmentCategory`, required): The garment type; it sets which POMs are needed. - **`published_version`** (`integer`, optional): Garment version shoppers are fitted against, if any. - **`draft_version`** (`integer`, optional): Garment version being edited, if any. - **`created_at`** (`string`, required) - **`updated_at`** (`string`, required) - **`fit_group_id`** (`string`, optional): The fit group whose garment this product uses (absent until grouped). - **`brand_id`** (`string`, optional): The brand the product belongs to; every store of the brand sees it. - **`home_store_id`** (`string`, optional): The store the product was created in. - **`retired_at`** (`string`, optional): Set when the product is retired; shoppers no longer get a size for it. - **`available`** (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). ## Example **Python** ```python product = tys.products.retrieve("TEE-001") # TryYourSize ID or your SKU ``` **Node.js** ```ts const product = await tys.products.retrieve("TEE-001"); // TryYourSize ID or your SKU ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/products/TEE-001 \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcatalogproduct.md) --- # tys.products.size_map.retrieve() > A product's size map (Studio size → your size label). Source: https://docs.tryyoursize.com/reference/python/products-size-map-retrieve/ *Method* · `tryyoursize` A product's size map (Studio size → your size label). Product by ID or SKU. ```python def retrieve(product: str) -> ProductSizeMap ``` ## Parameters - **`product`** (`str`, required): The product's TryYourSize ID or your SKU (URL-encoded for you). ## Returns `ProductSizeMap`: The size map. - **`entries`** (`object[]`, required) - **`unmapped_shop_values`** (`string[]`, optional): Variant size values of the product with no Studio size yet (read only). ## Example **Python** ```python size_map = tys.products.size_map.retrieve("TEE-001") print(size_map["entries"], size_map.get("unmapped_shop_values")) ``` **Node.js** ```ts const map = await tys.products.sizeMap.retrieve("TEE-001"); console.log(map.entries, map.unmapped_shop_values); ``` **cURL** ```bash curl https://api.tryyoursize.com/v1/products/TEE-001/size_map \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## See also - [products.size_map.retrieve (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/getcatalogsizemap.md) --- # tys.products.size_map.save() > Replace the size map, e.g. Source: https://docs.tryyoursize.com/reference/python/products-size-map-save/ *Method* · `tryyoursize` Replace the size map, e.g. `entries=[{"studio_size": "M", "shop_value": "Medium"}]`. ```python def save(product: str, *, entries: List[ProductSizeMapEntriesItem]) -> ProductSizeMap ``` ## Parameters - **`product`** (`str`, required): The product's TryYourSize ID or your SKU (URL-encoded for you). - **`entries`** (`List[ProductSizeMapEntriesItem]`, required): `[{"studio_size": "M", "shop_value": "Medium"}, …]`: replaces the whole map. ## Returns `ProductSizeMap`: The saved size map. ## Example **Python** ```python tys.products.size_map.save("TEE-001", entries=[ {"studio_size": "S", "shop_value": "Small"}, {"studio_size": "M", "shop_value": "Medium"}, ]) ``` **Node.js** ```ts await tys.products.sizeMap.save("TEE-001", { entries: [ { studio_size: "S", shop_value: "Small" }, { studio_size: "M", shop_value: "Medium" }, ], }); ``` **cURL** ```bash curl -X PUT https://api.tryyoursize.com/v1/products/TEE-001/size_map \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"entries":[{"studio_size":"M","shop_value":"Medium"}]}' ``` ## See also - [products.size_map.save (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/savecatalogsizemap.md) --- # tys.products.update() > Replace the SKU, name and category (all three required). Source: https://docs.tryyoursize.com/reference/python/products-update/ *Method* · `tryyoursize` Replace the SKU, name and category (all three required). ```python def update(product: str, *, external_id: str, name: str, category: str) -> Product ``` ## Parameters - **`product`** (`str`, required): The product's TryYourSize ID or your SKU (URL-encoded for you). - **`external_id`** (`str`, required): The SKU (all three fields are required). - **`name`** (`str`, required): The name. - **`category`** (`str`, required): The category. ## Returns `Product`: The updated product. ## Example **Python** ```python tys.products.update("TEE-001", external_id="TEE-001", name="Basic tee (organic)", category="tshirt") ``` **Node.js** ```ts await tys.products.update("TEE-001", { external_id: "TEE-001", name: "Basic tee (organic)", category: "tshirt" }); ``` **cURL** ```bash curl -X PATCH https://api.tryyoursize.com/v1/products/TEE-001 \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"external_id":"TEE-001","name":"Basic tee (organic)","category":"tshirt"}' ``` ## See also - [products.update (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/updatecatalogproduct.md) --- # tys.recommendations.create() > The shopper's size for up to 25 products (SKUs), from a browser SDK handle. Source: https://docs.tryyoursize.com/reference/python/recommendations-create/ *Method* · `tryyoursize` The shopper's size for up to 25 products (SKUs), from a browser SDK handle. Sizes only, in your labels: ``data`i`` has `product`, `status`` (`ready`/`computing`/`none`/`unavailable`), `size`, `studio_size` and `wearable_sizes`. Never starts a simulation. Scope `fit_checks:read`. ```python def create(*, handle: str, products: Iterable[str], idempotency_key: Optional[str] = None) -> RecommendationList ``` ## Parameters - **`handle`** (`str`, required): The handle from the browser SDK (`await tys.recommendationHandle()`), sent to your server. - **`products`** (`Iterable[str]`, required): Up to 25 of your SKUs / style IDs. - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `RecommendationList`: `{"data": [...]}`, one recommendation per product, in your size labels. Sizes only. - **`product`** (`string`, required) - **`status`** (`"ready" | "computing" | "none" | "unavailable"`, required): `ready`: a size. `computing`: a simulation the shopper started is running (ask again later). `none`: simulated, no size can be worn. `unavailable`: no published garment for this product, not simulated on this body yet, or the store's plan has no fit checks left. A page view or this API never starts a simulation. - **`size`** (`string`, required): The recommended size in the store's label (the product's size map), or null. - **`studio_size`** (`string`, required): The same size as entered in TryYourSize Studio, or null. - **`wearable_sizes`** (`string[]`, required): Sizes that can be worn on this body, in the store's labels. - **`tryon_id`** (`string`, optional): The try-on that produced the size, when there is one. Send it back with orders for attribution. ## Example **Python** ```python answer = tys.recommendations.create(handle=handle, products=["SKU-123", "SKU-456"]) for rec in answer["data"]: if rec["status"] == "ready": print(rec["product"], rec["size"], rec["wearable_sizes"]) ``` **Node.js** ```ts // handle: from the browser SDK, `await tys.recommendationHandle()`, sent to your server const { data } = await tys.recommendations.create({ handle, products: ["SKU-123", "SKU-456"], }); for (const r of data) { if (r.status === "ready") console.log(r.product, r.size, r.wearable_sizes); } ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/recommendations \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"handle":"tys_rh_…","products":["SKU-123","SKU-456"]}' ``` ## See also - [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md) - [recommendations.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createrecommendations.md) --- # tys.returns.create() > Record a return of an order line (idempotent by return id and line). Source: https://docs.tryyoursize.com/reference/python/returns-create/ *Method* · `tryyoursize` Record a return of an order line (idempotent by return `id` and line). ```python def create(ret: OutcomeReturnInput, *, idempotency_key: Optional[str] = None) -> OutcomeReturnResult ``` ## Parameters - **`ret`** (`OutcomeReturnInput`, required) - **`id`** (`string`, required): Your return (or RMA) ID. - **`order_id`** (`string`, required) - **`line_id`** (`string`, required) - **`quantity`** (`integer`, required) - **`reason`** (`string`, optional): size_too_small, size_too_large, fit_other, style, quality, damaged_or_wrong, changed_mind, other; Shopify's ReturnReason codes (SIZE_TOO_SMALL…) and common returns-platform words are mapped. Empty: unspecified. - **`exchange`** (`boolean`, optional): The item was exchanged rather than refunded. - **`exchange_size`** (`string`, optional): The size sent in exchange (implies exchange). - **`returned_at`** (`string`, required) - **`idempotency_key`** (`Optional[str]`, optional, default `None`) ## Returns `OutcomeReturnResult`: What was recorded. ## Example **Python** ```python tys.returns.create({ "id": "RMA-7", "order_id": "1001", "line_id": "1", "quantity": 1, "reason": "size_too_small", "returned_at": datetime.now(timezone.utc).isoformat(), }) ``` **Node.js** ```ts await tys.returns.create({ id: "RMA-7", order_id: "1001", line_id: "1", quantity: 1, reason: "size_too_small", returned_at: new Date().toISOString(), }); ``` **cURL** ```bash curl -X POST https://api.tryyoursize.com/v1/returns \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d '{"id":"RMA-7","order_id":"1001","line_id":"1","quantity":1,"reason":"size_too_small","returned_at":"2026-11-02T09:00:00Z"}' ``` ## See also - [returns.create (HTTP API)](https://docs.tryyoursize.com/api/reference/operations/createoutcomereturn.md) --- # Webhook.construct_event() > Verify the signature, then return the parsed event envelope. Source: https://docs.tryyoursize.com/reference/python/webhook-construct-event/ *Method* · `tryyoursize` Verify the signature, then return the parsed event envelope. ```python @staticmethod def construct_event(payload: Union[bytes, str], sig_header: str, secret: str, tolerance: int = DEFAULT_TOLERANCE, now: Optional[int] = None) -> WebhookEvent ``` ## Parameters - **`payload`** (`Union[bytes, str]`, required): The raw request body (bytes or str), never re-serialized JSON. - **`sig_header`** (`str`, required): The `TYS-Signature` header. - **`secret`** (`str`, required): The endpoint's signing secret (`whsec_…`). - **`tolerance`** (`int`, optional, default `DEFAULT_TOLERANCE`): Accepted age in seconds (either direction); 0 disables the check. - **`now`** (`Optional[int]`, optional, default `None`): Overrides the clock (unix seconds), for tests. ## Returns `WebhookEvent`: The event envelope (`id`, `type`, `api_version`, `created`, `mode`, `store_id`, `data`). ## Errors `SignatureVerificationError`. ## Example **Python** ```python from flask import Flask, request @app.post("/webhooks/tryyoursize") def webhook(): try: event = tryyoursize.Webhook.construct_event( request.get_data(), request.headers.get("TYS-Signature", ""), os.environ["TYS_WEBHOOK_SECRET"] ) except tryyoursize.SignatureVerificationError: return "", 400 if event["type"] == "garment.published": print(event["data"]["product_skus"]) return "", 200 ``` **Node.js** ```ts app.post("/webhooks/tryyoursize", express.raw({ type: "application/json" }), (req, res) => { let event; try { event = tys.webhooks.constructEvent(req.body, req.headers["tys-signature"], process.env.TYS_WEBHOOK_SECRET!); } catch { return res.sendStatus(400); } if (event.type === "garment.published") console.log(event.data.product_skus); res.sendStatus(200); }); ``` ## See also - [Webhooks guide](https://docs.tryyoursize.com/guides/webhooks.md) --- # Webhook.generate_test_header() > A valid TYS-Signature value, for your own tests. Source: https://docs.tryyoursize.com/reference/python/webhook-generate-test-header/ *Method* · `tryyoursize` A valid `TYS-Signature` value, for your own tests. ```python @staticmethod def generate_test_header(payload: Union[bytes, str], secret: str, timestamp: Optional[int] = None) -> str ``` ## Parameters - **`payload`** (`Union[bytes, str]`, required): The body you will send. - **`secret`** (`str`, required): The signing secret. - **`timestamp`** (`Optional[int]`, optional, default `None`): Unix seconds (default now). ## Returns `str`: A valid `TYS-Signature` value. ## Example **Python** ```python payload = json.dumps({"id": "evt_test", "object": "event", "type": "webhook.test", "data": {}}) header = tryyoursize.Webhook.generate_test_header(payload, "whsec_test") ``` **Node.js** ```ts const payload = JSON.stringify({ id: "evt_test", object: "event", type: "webhook.test", data: {} }); const header = tys.webhooks.generateTestHeader({ payload, secret: "whsec_test" }); await fetch("http://localhost:3000/webhooks/tryyoursize", { method: "POST", headers: { "TYS-Signature": header, "Content-Type": "application/json" }, body: payload, }); ``` --- # Webhook.verify_signature() > Return True or raise SignatureVerificationError. Source: https://docs.tryyoursize.com/reference/python/webhook-verify-signature/ *Method* · `tryyoursize` Return `True` or raise `SignatureVerificationError`. `tolerance` is the accepted age in seconds (either direction); `0` or less disables the check. `now` overrides the clock (unix seconds), for tests. ```python @staticmethod def verify_signature(payload: Union[bytes, str], sig_header: str, secret: str, tolerance: int = DEFAULT_TOLERANCE, now: Optional[int] = None) -> bool ``` ## Parameters - **`payload`** (`Union[bytes, str]`, required): The raw request body. - **`sig_header`** (`str`, required): The `TYS-Signature` header. - **`secret`** (`str`, required): The signing secret. - **`tolerance`** (`int`, optional, default `DEFAULT_TOLERANCE`): Accepted age in seconds. - **`now`** (`Optional[int]`, optional, default `None`): Clock override, for tests. ## Returns `bool`: `True`, or raises `SignatureVerificationError`. ## Example **Python** ```python tryyoursize.Webhook.verify_signature(payload, sig_header, secret) # True, or raises SignatureVerificationError ``` **Node.js** ```ts tys.webhooks.verifySignature(rawBody, req.headers["tys-signature"], secret); // throws SignatureVerificationError ``` --- # React Native reference (@tryyoursize/react-native) > The native Size guide button and the shopper's recommended size in React Native, over the iOS and Android SDKs. Source: https://docs.tryyoursize.com/reference/react-native/ *SDK* · `@tryyoursize/react-native` · v0.1.0 A thin wrapper over the native [iOS](https://docs.tryyoursize.com/reference/ios.md) and [Android](https://docs.tryyoursize.com/reference/android.md) SDKs: the button is the native one, the window is our window in a native sheet, and only sizes reach your JavaScript. React Native 0.73+; Expo Go is not supported (use a development build). Register `ios-app://` and `android-app://` for your key in Studio. ## Install **npm** ```bash npm install @tryyoursize/react-native cd ios && pod install ``` ## Initialize **React Native** ```tsx await configure("pk_test_…"); // once, at startup ``` ## Functions | | | |---|---| | [`configure()`](https://docs.tryyoursize.com/reference/react-native/configure.md) | Call once at startup with your publishable key. | | [`getRecommendation()`](https://docs.tryyoursize.com/reference/react-native/get-recommendation.md) | The shopper's recommended size for a product, in your size labels. | | [`addRecommendationListener()`](https://docs.tryyoursize.com/reference/react-native/add-recommendation-listener.md) | Calls listener now (once known) and on every change. | | [`addEventListener()`](https://docs.tryyoursize.com/reference/react-native/add-event-listener.md) | Window events: ready, open, close, size (preselect it), loaderror. | | [`present()`](https://docs.tryyoursize.com/reference/react-native/present.md) | Opens the measuring / try-on window for a product. | | [`setAppearance()`](https://docs.tryyoursize.com/reference/react-native/set-appearance.md) | Live restyle (e.g. | ## Components | | | |---|---| | [`SizeGuide`](https://docs.tryyoursize.com/reference/react-native/size-guide.md) | The native "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". | ## Types | | | |---|---| | [`Recommendation`](https://docs.tryyoursize.com/reference/react-native/recommendation.md) | Recommendation | | [`Appearance`](https://docs.tryyoursize.com/reference/react-native/appearance.md) | Appearance | | [`TysEvent`](https://docs.tryyoursize.com/reference/react-native/tys-event.md) | A window event, by type. | --- # addEventListener() > Window events: ready, open, close, size (preselect it), loaderror. Source: https://docs.tryyoursize.com/reference/react-native/add-event-listener/ *Function* · `@tryyoursize/react-native` Window events: ready, open, close, size (preselect it), loaderror. ```ts function addEventListener(listener: (e: TysEvent) => void): { remove(): void } ``` ## Parameters - **`listener`** (`(e: TysEvent) => void`, required): Called with each window event: `ready`, `open`, `close`, `size` (preselect it), `loaderror`. ## Returns `{ remove(): void }`: `{ remove() }`. ## Example **React Native** ```tsx const sub = addEventListener((e) => { if (e.type === "size") setSize(e.selection.size); if (e.type === "loaderror") console.warn(e.code); }); ``` **Swift** ```swift let sub = tys.on { event in switch event { case .size(let selection): selectedSize = selection.size // "Use this size" case .loadError(let error): print(error.code) default: break } } ``` **Kotlin** ```kotlin val sub = tys.on { event -> when (event) { is TryYourSize.Event.Size -> selected = event.selection.size is TryYourSize.Event.LoadError -> Log.w("tys", event.error.code.wire) else -> Unit } } ``` **Flutter** ```dart tys.events.listen((e) { if (e is SizeSelected) setState(() => size = e.selection.size); if (e is LoadError) debugPrint(e.error.code); }); ``` --- # addRecommendationListener() > Calls listener now (once known) and on every change. Source: https://docs.tryyoursize.com/reference/react-native/add-recommendation-listener/ *Function* · `@tryyoursize/react-native` Calls `listener` now (once known) and on every change. Returns `{ remove }`. ```ts function addRecommendationListener(product: string, listener: (r: Recommendation) => void): { remove(): void } ``` ## Parameters - **`product`** (`string`, required): Your product ID or SKU. - **`listener`** (`(r: Recommendation) => void`, required): Called now (once known) and on every change. ## Returns `{ remove(): void }`: `{ remove() }`. ## Example **React Native** ```tsx useEffect(() => { const sub = addRecommendationListener("SKU-123", (rec) => { if (rec.status === "ready" && !touched) setSize(rec.size); }); return () => sub.remove(); }, []); ``` **Swift** ```swift let sub = tys.onRecommendation(for: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } // keep `sub`; sub.cancel() when the screen goes away ``` **Kotlin** ```kotlin val sub = tys.onRecommendation("SKU-123") { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } // sub.cancel() in onDestroy ``` **Flutter** ```dart final sub = tys.onRecommendation('SKU-123').listen((rec) { if (rec.status == RecommendationStatus.ready && !touched) setState(() => size = rec.size); }); // sub.cancel() in dispose() ``` --- # Appearance > Appearance Source: https://docs.tryyoursize.com/reference/react-native/appearance/ *Type* · `@tryyoursize/react-native` ```ts interface Appearance ``` ## Properties - **`theme`** (`"inherit" | "tryyoursize"`, optional): "inherit" (default): your app's own colours and font. "tryyoursize": the light beige brand. - **`variables`** (`{ /** Hex: "#rrggbb" or "#rrggbbaa". */ colorPrimary?: string; colorBackground?: string; colorText?: string; /** A CSS font-family for the window. */ fontFamily?: string; /** Corner radius, 0–32. */ borderRadius?: number; }`, optional) - **`fonts`** (`{ cssSrc: string }[]`, optional): Google Fonts stylesheets for the window. ## Example **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # configure() > Call once at startup with your publishable key. Source: https://docs.tryyoursize.com/reference/react-native/configure/ *Function* · `@tryyoursize/react-native` Call once at startup with your publishable key. Register your app for the key in Studio: `ios-app://` and `android-app://` (test mode today). ```ts function configure(publishableKey: string, options: ConfigureOptions = {}): Promise ``` ## Parameters - **`publishableKey`** (`string`, required): Your publishable key (`pk_test_…` / `pk_live_…`). - **`options`** (`ConfigureOptions`, optional, default `{}`) - **`appearance`** (`Appearance`, optional) - **`locale`** (`string`, optional): BCP 47; default: the device language. - **`embedOrigin`** (`string`, optional): Our window's origin, for staging or local development only. ## Errors An `Error` for a secret key or a malformed key, before anything reaches the native side. ## Example **React Native** ```tsx await configure("pk_test_…", { appearance: { variables: { borderRadius: 10 } }, locale: "es" }); ``` **Swift** ```swift let tys = try TryYourSize(publishableKey: "pk_test_…", configuration: .init( appearance: Appearance(borderRadius: 10), locale: "es")) ``` **Kotlin** ```kotlin val tys = TryYourSize(context, "pk_test_…", TryYourSize.Configuration( appearance = Appearance(borderRadius = 10f), locale = "es", )) ``` **Flutter** ```dart final tys = await TryYourSize.create('pk_test_…', appearance: const Appearance(borderRadius: 10), locale: 'es'); ``` --- # getRecommendation() > The shopper's recommended size for a product, in your size labels. Source: https://docs.tryyoursize.com/reference/react-native/get-recommendation/ *Function* · `@tryyoursize/react-native` The shopper's recommended size for a product, in your size labels. Never starts a simulation. ```ts async function getRecommendation(product: string): Promise ``` ## Parameters - **`product`** (`string`, required): Your product ID or SKU. ## Returns `Promise`: The shopper's recommended size. - **`product`** (`string`, required) - **`status`** (`RecommendationStatus`, required): `none`: simulated, no size can be worn. Same values as the web SDK. - **`size`** (`string | null`, required): The recommended size in your size labels, or null. - **`studioSize`** (`string | null`, required): The same size as entered in TryYourSize Studio, or null. - **`wearableSizes`** (`string[]`, optional): Sizes that can be worn on this body, in your labels (when simulated). ## Example **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` --- # present() > Opens the measuring / try-on window for a product. Source: https://docs.tryyoursize.com/reference/react-native/present/ *Function* · `@tryyoursize/react-native` Opens the measuring / try-on window for a product. ```ts function present(product: string, step?: OpenStep): void ``` ## Parameters - **`product`** (`string`, required): Your product ID or SKU. - **`step`** (`OpenStep`, optional): `"create"`, `"improve"` or `"view"`. ## Example **React Native** ```tsx present("SKU-123"); // or present("SKU-123", "view") ``` **Swift** ```swift tys.present(product: "SKU-123") // or step: .view, from: viewController ``` **Kotlin** ```kotlin tys.present(activity, "SKU-123") // or step = OpenStep.VIEW ``` **Flutter** ```dart await tys.present('SKU-123'); // or step: OpenStep.view ``` --- # Recommendation > Recommendation Source: https://docs.tryyoursize.com/reference/react-native/recommendation/ *Type* · `@tryyoursize/react-native` ```ts interface Recommendation ``` ## Properties - **`product`** (`string`, required) - **`status`** (`RecommendationStatus`, required): `none`: simulated, no size can be worn. Same values as the web SDK. - **`size`** (`string | null`, required): The recommended size in your size labels, or null. - **`studioSize`** (`string | null`, required): The same size as entered in TryYourSize Studio, or null. - **`wearableSizes`** (`string[]`, optional): Sizes that can be worn on this body, in your labels (when simulated). ## Example **React Native** ```tsx const rec = await getRecommendation("SKU-123"); if (rec.status === "ready") setSize(rec.size); ``` **Swift** ```swift let rec = try await tys.recommendation(for: "SKU-123") if rec.status == .ready, let size = rec.size { selectedSize = size } ``` **Kotlin** ```kotlin lifecycleScope.launch { val rec = tys.recommendation("SKU-123") if (rec.status == Recommendation.Status.READY) selected = rec.size } ``` **Flutter** ```dart final rec = await tys.recommendation('SKU-123'); if (rec.status == RecommendationStatus.ready) setState(() => size = rec.size); ``` --- # setAppearance() > Live restyle (e.g. Source: https://docs.tryyoursize.com/reference/react-native/set-appearance/ *Function* · `@tryyoursize/react-native` Live restyle (e.g. your dark mode); an open window updates at once. ```ts function setAppearance(appearance: Appearance): void ``` ## Parameters - **`appearance`** (`Appearance`, required) - **`theme`** (`"inherit" | "tryyoursize"`, optional): "inherit" (default): your app's own colours and font. "tryyoursize": the light beige brand. - **`variables`** (`{ /** Hex: "#rrggbb" or "#rrggbbaa". */ colorPrimary?: string; colorBackground?: string; colorText?: string; /** A CSS font-family for the window. */ fontFamily?: string; /** Corner radius, 0–32. */ borderRadius?: number; }`, optional) - **`fonts`** (`{ cssSrc: string }[]`, optional): Google Fonts stylesheets for the window. ## Example **React Native** ```tsx setAppearance({ variables: { colorPrimary: "#3b2f26" } }); ``` **Swift** ```swift tys.appearance.colorPrimary = TYSColor(hex: "#3b2f26") // an open window restyles at once ``` **Kotlin** ```kotlin tys.appearance = tys.appearance.copy(colorPrimary = 0xFF3B2F26.toInt()) ``` **Flutter** ```dart await tys.setAppearance(const Appearance(colorPrimary: Color(0xFF3B2F26))); ``` --- # > The native "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". Source: https://docs.tryyoursize.com/reference/react-native/size-guide/ *Component* · `@tryyoursize/react-native` The native "Size guide" button: "Size guide" → "Calculating your size…" → "Your size: M". ```ts function SizeGuide({ product, variant = "link", label, style, onRecommendation }: SizeGuideProps) ``` ## Props - **`product`** (`string`, required) - **`variant`** (`"link" | "button"`, optional): "link" (default): link-sized text with the mark. "button": a small bordered button. - **`label`** (`string`, optional): Your own words instead of "Size guide". - **`style`** (`StyleProp`, optional) - **`onRecommendation`** (`(r: Recommendation) => void`, optional): The shopper's recommended size, on show and on every change (sizes only). ## Example **React Native** ```tsx rec.status === "ready" && !touched && setSize(rec.size)} /> ``` **Swift** ```swift // SwiftUI, where your "Size guide" link was SizeGuide(client: tys, product: "SKU-123") { rec in if rec.status == .ready, !touched { selectedSize = rec.size } } ``` **Kotlin** ```kotlin // Compose, where your "Size guide" link was SizeGuide(tys, "SKU-123", textStyle = MaterialTheme.typography.bodyMedium) { rec -> if (rec.status == Recommendation.Status.READY && !touched) selected = rec.size } ``` **Flutter** ```dart const SizeGuide(product: 'SKU-123') // listen to tys.onRecommendation to preselect ``` --- # TysEvent > A window event, by type. Source: https://docs.tryyoursize.com/reference/react-native/tys-event/ *Type* · `@tryyoursize/react-native` A window event, by `type`. ```ts type TysEvent = | { type: "ready"; product: string } | { type: "open"; product: string } | { type: "close"; product: string } | { type: "size"; selection: SizeSelection } | { type: "loaderror"; code: string; message: string }; ``` ## Example **React Native** ```tsx const sub = addEventListener((e) => { if (e.type === "size") setSize(e.selection.size); if (e.type === "loaderror") console.warn(e.code); }); ``` **Swift** ```swift let sub = tys.on { event in switch event { case .size(let selection): selectedSize = selection.size // "Use this size" case .loadError(let error): print(error.code) default: break } } ``` **Kotlin** ```kotlin val sub = tys.on { event -> when (event) { is TryYourSize.Event.Size -> selected = event.selection.size is TryYourSize.Event.LoadError -> Log.w("tys", event.error.code.wire) else -> Unit } } ``` **Flutter** ```dart tys.events.listen((e) { if (e is SizeSelected) setState(() => size = e.selection.size); if (e is LoadError) debugPrint(e.error.code); }); ``` --- # React reference (@tryyoursize/react) > A provider, hooks and element components for TryYourSize.js, shaped like @stripe/react-stripe-js. Source: https://docs.tryyoursize.com/reference/react/ *SDK* · `@tryyoursize/react` · v0.1.0 React components for [TryYourSize.js](https://docs.tryyoursize.com/reference/web.md): wrap your app (or product page) in `TryYourSizeProvider`, then render `SizeGuide`, `FitButton` or `FitElement` where shoppers choose a size. Components mount on commit and are destroyed on unmount (StrictMode safe); changing `product` updates the element in place; changing a callback never reloads anything. Nothing touches `window` during render, so server-side rendering works. ## Install **npm** ```bash npm install @tryyoursize/react @tryyoursize/sdk ``` ## Quick example **React** ```tsx const tysPromise = loadTryYourSize("pk_test_…"); const [size, setSize] = useState(); return ( r.status === "ready" && setSize(r.size ?? undefined)} /> ); } ``` ## Provider and hooks | | | |---|---| | [`TryYourSizeProvider`](https://docs.tryyoursize.com/reference/react/tryyoursize-provider.md) | Creates (or adopts) the TryYourSize client and its elements group for the components inside it. | | [`useTryYourSize()`](https://docs.tryyoursize.com/reference/react/use-tryyoursize.md) | The TryYourSize client, or null until it has loaded. | | [`useElements()`](https://docs.tryyoursize.com/reference/react/use-elements.md) | The provider's Elements group, or null until the client has loaded. | ## Components | | | |---|---| | [`SizeGuide`](https://docs.tryyoursize.com/reference/react/size-guide.md) | A link-sized "Size guide" with the TryYourSize mark, for the spot next to your size picker. | | [`FitButton`](https://docs.tryyoursize.com/reference/react/fit-button.md) | A compact "Find my size" button that opens the measuring window. | | [`FitElement`](https://docs.tryyoursize.com/reference/react/fit-element.md) | The inline element (sign-in and "your body is ready" in your page). | | [`ElementHandle`](https://docs.tryyoursize.com/reference/react/element-handle.md) | What a ref on , or gives you. | --- # ElementHandle > What a ref on , or gives you. Source: https://docs.tryyoursize.com/reference/react/element-handle/ *Type* · `@tryyoursize/react` What a `ref` on ``, `` or `` gives you. ```ts interface ElementHandle ``` ## Methods - **`open`** (`void`, required): Opens the measuring window. - **`close`** (`void`, required) - **`getElement`** (`TysElement | null`, required): The underlying SDK element (null before mount). - **`getRecommendation`** (`Promise`, required): The shopper's recommended size once known. ## Example **React** ```tsx const guide = useRef(null); ``` --- # > A compact "Find my size" button that opens the measuring window. Source: https://docs.tryyoursize.com/reference/react/fit-button/ *Component* · `@tryyoursize/react` A compact "Find my size" button that opens the measuring window. Pass a `ref` to get an [`ElementHandle`](https://docs.tryyoursize.com/reference/react/element-handle.md) (`open()`, `close()`, `getRecommendation()`, `getElement()`). ```ts FitButton: ForwardRefExoticComponent> ``` ## Props - **`product`** (`string`, required): Your product identifier, as synced to TryYourSize. Changing it updates the element in place. - **`id`** (`string`, optional) - **`className`** (`string`, optional) - **`style`** (`CSSProperties`, optional) - **`onReady`** (`(e: ElementEvents["ready"]) => void`, optional): The element is shown and connected. - **`onSize`** (`(e: SizeEvent) => void`, optional): The shopper got a size for this product: preselect the variant. - **`onOpen`** (`(e: ElementEvents["open"]) => void`, optional) - **`onClose`** (`(e: ElementEvents["close"]) => void`, optional) - **`onLoadError`** (`(e: ElementEvents["loaderror"]) => void`, optional): Our window could not be reached (network, CSP). - **`onRecommendation`** (`(r: Recommendation) => void`, optional): The shopper's recommended size in your size labels: on mount when they are already connected, and on every change. Sizes only. Preselect your picker. ## Example **React** ```tsx setSize(size)} /> ``` **JavaScript** ```js elements.create("button", { product: "SKU-123" }).mount("#find-my-size"); ``` --- # > The inline element (sign-in and "your body is ready" in your page). Source: https://docs.tryyoursize.com/reference/react/fit-element/ *Component* · `@tryyoursize/react` The inline element (sign-in and "your body is ready" in your page). Recommended. Pass a `ref` to get an [`ElementHandle`](https://docs.tryyoursize.com/reference/react/element-handle.md) (`open()`, `close()`, `getRecommendation()`, `getElement()`). ```ts FitElement: ForwardRefExoticComponent> ``` ## Props - **`product`** (`string`, required): Your product identifier, as synced to TryYourSize. Changing it updates the element in place. - **`id`** (`string`, optional) - **`className`** (`string`, optional) - **`style`** (`CSSProperties`, optional) - **`onReady`** (`(e: ElementEvents["ready"]) => void`, optional): The element is shown and connected. - **`onSize`** (`(e: SizeEvent) => void`, optional): The shopper got a size for this product: preselect the variant. - **`onOpen`** (`(e: ElementEvents["open"]) => void`, optional) - **`onClose`** (`(e: ElementEvents["close"]) => void`, optional) - **`onLoadError`** (`(e: ElementEvents["loaderror"]) => void`, optional): Our window could not be reached (network, CSP). - **`onRecommendation`** (`(r: Recommendation) => void`, optional): The shopper's recommended size in your size labels: on mount when they are already connected, and on every change. Sizes only. Preselect your picker. - **`demo`** (`DemoOptions`, optional): only, test keys only: demo mode on a named sample body, no sign-in (landing pages). Changing `demo.body` switches the body in place. - **`body`** (`string`, required): A sample body id, e.g. `sample-female-165`. - **`height`** (`number`, optional): Height of the element in px (default 640). ## Example **React** ```tsx selectVariant(size)} /> ``` **JavaScript** ```js elements.create("fit", { product: "STRAIGHT-JEANS" }).mount("#tys-fit"); ``` --- # > A link-sized "Size guide" with the TryYourSize mark, for the spot next to your size picker. Source: https://docs.tryyoursize.com/reference/react/size-guide/ *Component* · `@tryyoursize/react` A link-sized "Size guide" with the TryYourSize mark, for the spot next to your size picker. Shows "Your size: M" when the shopper is connected. Pass a `ref` to get an [`ElementHandle`](https://docs.tryyoursize.com/reference/react/element-handle.md) (`open()`, `close()`, `getRecommendation()`, `getElement()`). ```ts SizeGuide: ForwardRefExoticComponent> ``` ## Props - **`variant`** (`"link" | "button"`, optional): A text link (default) or an outlined button. - **`label`** (`string`, optional): Your own words for "Size guide" (otherwise localized). ## Example **React** ```tsx r.status === "ready" && !touched && setSize(r.size!)} /> ``` **JavaScript** ```js elements.create("sizeGuide", { product: "SKU-123", variant: "link" }).mount("#size-guide"); ``` **HTML** ```html ``` --- # > Creates (or adopts) the TryYourSize client and its elements group for the components inside it. Source: https://docs.tryyoursize.com/reference/react/tryyoursize-provider/ *Component* · `@tryyoursize/react` Creates (or adopts) the TryYourSize client and its elements group for the components inside it. Pass either `publishableKey` or `client`; it cannot change once set. ```ts function TryYourSizeProvider(props: TryYourSizeProviderProps): ReactNode ``` ## Props - **`children`** (`ReactNode`, optional) - **`options`** (`TryYourSizeProviderOptions`, optional) - **`embedOrigin`** (`string`, optional): Development override of our windows' origin (e.g. http://localhost:5173). Used with `publishableKey`. - **`apiOrigin`** (`string`, optional): Development override of the API origin (e.g. http://localhost:8000): the A/B holdout config. - **`locale`** (`Locale`, optional): Language of our windows; "auto" follows the browser. - **`appearance`** (`Appearance`, optional): Look and feel. Changes are applied live with `elements.update()`. - **`publishableKey`** (`string`, optional): Either this or `client`. Your publishable key (`pk_test_…` / `pk_live_…`). The provider calls `TryYourSize()`. - **`client`** (`TryYourSizeClient | Promise | null`, optional): Either this or `publishableKey`. A client, a promise of one (for example `loadTryYourSize(pk)`), or `null` while you don't have one yet. ## Example **React** ```tsx // With a key // With the CDN loader (SSR-safe) const tysPromise = loadTryYourSize("pk_test_…"); ``` **JavaScript** ```js // Without React const tys = TryYourSize("pk_test_…", { locale: "es" }); const elements = tys.elements(); ``` --- # useElements() > The provider's Elements group, or null until the client has loaded. Source: https://docs.tryyoursize.com/reference/react/use-elements/ *Hook* · `@tryyoursize/react` The provider's Elements group, or null until the client has loaded. ```ts function useElements(): Elements | null ``` ## Returns `Elements | null`: The provider's elements group, or `null` until the client has loaded. Throws outside a provider. ## Example **React** ```tsx const elements = useElements(); const openGuide = () => elements?.getElement("sizeGuide")?.open(); ``` --- # useTryYourSize() > The TryYourSize client, or null until it has loaded. Source: https://docs.tryyoursize.com/reference/react/use-tryyoursize/ *Hook* · `@tryyoursize/react` The TryYourSize client, or null until it has loaded. ```ts function useTryYourSize(): TryYourSizeClient | null ``` ## Returns `TryYourSizeClient | null`: The client, or `null` until it has loaded. Throws outside a provider. ## Example **React** ```tsx function SizeBadge({ sku }: { sku: string }) { const tys = useTryYourSize(); const [size, setSize] = useState(null); useEffect(() => { tys?.recommendation(sku).then((r) => setSize(r.status === "ready" ? r.size : null)); }, [tys, sku]); return size ? Your size: {size} : null; } ``` --- # Web SDK reference (TryYourSize.js) > TryYourSize.js for any storefront: the client, elements, events and web components. Source: https://docs.tryyoursize.com/reference/web/ *SDK* · `@tryyoursize/sdk` · v0.2.0 TryYourSize.js puts the TryYourSize elements in your storefront: a **Size guide** link next to your size picker, a **Find my size** button, or the inline **fit** element. Shoppers measure once with a tape measure; your page receives their size in your own size labels, never their measurements. Same shape as Stripe.js: a client per publishable key, an elements group, elements you mount, events you listen to. ## Install **Script tag** ```html ``` **npm** ```bash npm install @tryyoursize/sdk ``` ## Initialize **JavaScript** ```js const tys = TryYourSize("pk_test_…"); // publishable key only const elements = tys.elements(); // inherits your page's font and colours const guide = elements.create("sizeGuide", { product: "SKU-123" }); guide.mount("#size-guide"); guide.on("recommendation", (r) => { if (r.status === "ready") preselect(r.size); // your size label, e.g. "M" }); ``` **React** ```tsx return ( r.status === "ready" && preselect(r.size)} /> ); } ``` **HTML** ```html ``` Allow our origins in your Content Security Policy: `script-src https://js.tryyoursize.com` (CDN build) and `frame-src https://embed.tryyoursize.com`. Error codes are listed in [Web SDK errors](https://docs.tryyoursize.com/sdk/errors.md). ## Client | | | |---|---| | [`TryYourSize()`](https://docs.tryyoursize.com/reference/web/tryyoursize.md) | Creates a client for your publishable key. | | [`loadTryYourSize()`](https://docs.tryyoursize.com/reference/web/load-tryyoursize.md) | Loads TryYourSize.js from our CDN once (the latest compatible v1 build) and creates a client, like loadStripe(). | | [`elements()`](https://docs.tryyoursize.com/reference/web/client-elements.md) | Creates an elements group: the appearance and language shared by the elements you create from it. | | [`recommendation()`](https://docs.tryyoursize.com/reference/web/client-recommendation.md) | The shopper's recommended size for a product, in your size labels, without mounting anything (a hidden frame on our origin answers). | | [`recommendationHandle()`](https://docs.tryyoursize.com/reference/web/client-recommendation-handle.md) | A sealed handle for your server's POST /v1/recommendations (headless or server-rendered pages). | | [`experiment()`](https://docs.tryyoursize.com/reference/web/client-experiment.md) | The visitor's A/B holdout assignment when the store runs one (null otherwise). | | [`version`](https://docs.tryyoursize.com/reference/web/client-version.md) | The SDK version (semver) of the loaded TryYourSize.js. | ## Elements group | | | |---|---| | [`create()`](https://docs.tryyoursize.com/reference/web/elements-create.md) | Creates an element for one product. | | [`getElement()`](https://docs.tryyoursize.com/reference/web/elements-get-element.md) | The most recently created element of a type that is still alive, or null. | | [`update()`](https://docs.tryyoursize.com/reference/web/elements-update.md) | Live update, e.g. | ## Element | | | |---|---| | [`mount()`](https://docs.tryyoursize.com/reference/web/element-mount.md) | Shows the element inside a container of your page. | | [`unmount()`](https://docs.tryyoursize.com/reference/web/element-unmount.md) | Removes the element from the page and closes its window. | | [`destroy()`](https://docs.tryyoursize.com/reference/web/element-destroy.md) | Unmounts the element and drops its handlers. | | [`update()`](https://docs.tryyoursize.com/reference/web/element-update.md) | Changes the element's options in place. | | [`open()`](https://docs.tryyoursize.com/reference/web/element-open.md) | Opens the TryYourSize window for this element's product, as the element itself does on click. | | [`close()`](https://docs.tryyoursize.com/reference/web/element-close.md) | Closes the window if it is open. | | [`getRecommendation()`](https://docs.tryyoursize.com/reference/web/element-get-recommendation.md) | The shopper's recommended size for this product once known (see the recommendation event). | | [`on()`](https://docs.tryyoursize.com/reference/web/element-on.md) | Listens to an element event. | | [`once()`](https://docs.tryyoursize.com/reference/web/element-once.md) | Like on(), for the next event only. | | [`off()`](https://docs.tryyoursize.com/reference/web/element-off.md) | Removes a handler, or every handler of the event when you pass none. | | [`elementType`](https://docs.tryyoursize.com/reference/web/element-element-type.md) | The element's type: "fit", "button" or "sizeGuide". | ## Events Listen with `element.on(name, handler)`, with the React `on…` props, or as `tys:` DOM events on the web components. | | | |---|---| | [`"ready"`](https://docs.tryyoursize.com/reference/web/event-ready.md) | The element is on the page (the size guide and button at once; the inline element once our window is connected). | | [`"size"`](https://docs.tryyoursize.com/reference/web/event-size.md) | The shopper chose a size in our window ("Use this size"): preselect that variant. | | [`"open"`](https://docs.tryyoursize.com/reference/web/event-open.md) | Our window opened. | | [`"close"`](https://docs.tryyoursize.com/reference/web/event-close.md) | Our window closed. | | [`"loaderror"`](https://docs.tryyoursize.com/reference/web/event-loaderror.md) | Our window could not be loaded or reached (network, Content Security Policy, an origin not registered for the key). | | [`"recommendation"`](https://docs.tryyoursize.com/reference/web/event-recommendation.md) | The shopper's recommended size for this product, in your size labels: on mount when the shopper is already connected, and on every change. | ## Web components For no-code platforms and CMS blocks: load `tys.js` (or import `@tryyoursize/sdk/elements`) and use the tags. Each forwards the element events as bubbling `tys:` DOM events with the payload in `event.detail`. | | | |---|---| | [``](https://docs.tryyoursize.com/reference/web/tys-size-guide.md) | The size guide element as a tag: "Size guide" with the TryYourSize mark, then "Your size: M" once the shopper is connected. | | [``](https://docs.tryyoursize.com/reference/web/tys-fit.md) | The inline element as a tag: sign-in and "your body is ready" in your page. | | [``](https://docs.tryyoursize.com/reference/web/tys-fit-button.md) | The compact "Find my size" button as a tag. | ## Types and utilities | | | |---|---| | [`Appearance`](https://docs.tryyoursize.com/reference/web/appearance.md) | Look and feel of the elements and our windows, modelled on Stripe's Appearance API: a theme, a few variables, and web fonts. | | [`Recommendation`](https://docs.tryyoursize.com/reference/web/recommendation.md) | Recommendation | | [`TryYourSizeError`](https://docs.tryyoursize.com/reference/web/tryyoursize-error.md) | Every error the SDK throws or emits: a stable type and code to branch on, a message, and a link to its explanation. | | [`isPublishableKey()`](https://docs.tryyoursize.com/reference/web/is-publishable-key.md) | A publishable key is the only key allowed in a browser. | | [`resolveAppearance()`](https://docs.tryyoursize.com/reference/web/resolve-appearance.md) | Resolves what the windows receive: for "inherit", the font, text colour and background where the element is mounted (buttons use the text colour, the way most stores style their primary button); expli | | [`VERSION`](https://docs.tryyoursize.com/reference/web/version.md) | The SDK version (semver), also sent to our windows. | --- # Appearance > Look and feel of the elements and our windows, modelled on Stripe's Appearance API: a theme, a few variables, and web fonts. Source: https://docs.tryyoursize.com/reference/web/appearance/ *Type* · `@tryyoursize/sdk` Look and feel of the elements and our windows, modelled on Stripe's Appearance API: a theme, a few variables, and web fonts. The default theme, `inherit`, reads your page where the element is mounted, so it looks native to your store. ```ts interface Appearance ``` ## Properties - **`theme`** (`Theme`, optional): `"inherit"` (default): your page's font, text colour and background. `"tryyoursize"`: our light beige brand. - **`variables`** (`AppearanceVariables`, optional) - **`colorPrimary`** (`string`, optional): Buttons and the tape line on the body. - **`colorBackground`** (`string`, optional) - **`colorText`** (`string`, optional) - **`fontFamily`** (`string`, optional) - **`borderRadius`** (`number`, optional): Corner radius in px for cards, inputs and buttons. - **`fonts`** (`{ cssSrc: string }[]`, optional): Stylesheets with @font-face rules (Google Fonts only), like Stripe's cssSrc. ## Example **JavaScript** ```js tys.elements({ appearance: { theme: "inherit", variables: { colorPrimary: "#3b2f26", fontFamily: "Inter, sans-serif", borderRadius: 8 }, fonts: [{ cssSrc: "https://fonts.googleapis.com/css2?family=Inter:wght@400;600" }], }, }); ``` **Swift** ```swift let config = TryYourSize.Configuration(appearance: Appearance( colorPrimary: TYSColor(hex: "#3b2f26"), fontFamily: "Inter, -apple-system", borderRadius: 8)) ``` **Kotlin** ```kotlin val config = TryYourSize.Configuration( appearance = Appearance(colorPrimary = 0xFF3B2F26.toInt(), borderRadius = 8f), ) ``` ## See also - [Appearance and theming](https://docs.tryyoursize.com/guides/appearance.md) --- # tys.elements() > Creates an elements group: the appearance and language shared by the elements you create from it. Source: https://docs.tryyoursize.com/reference/web/client-elements/ *Method* · `@tryyoursize/sdk` Creates an elements group: the appearance and language shared by the elements you create from it. ```ts elements(options?: ElementsOptions): Elements; ``` ## Parameters - **`options`** (`ElementsOptions`, optional) - **`appearance`** (`Appearance`, optional): Look and feel. By default (`theme: "inherit"`) elements take your page's font and colours. See `Appearance`(/reference/web/appearance/). - **`locale`** (`Locale`, optional) ## Returns `Elements`: An `Elements` group: `create()`, `getElement()`, `update()`. ## Example **JavaScript** ```js const elements = tys.elements({ appearance: { variables: { colorPrimary: "#3b2f26", borderRadius: 10 } }, locale: "auto", }); ``` **React** ```tsx ``` --- # tys.experiment() > The visitor's A/B holdout assignment when the store runs one (null otherwise). Source: https://docs.tryyoursize.com/reference/web/client-experiment/ *Method* · `@tryyoursize/sdk` The visitor's A/B holdout assignment when the store runs one (null otherwise). Save `visitorId` with the order (POST /v1/orders `visitor_id`) so returns are attributed to the right arm. ```ts experiment(): Promise; ``` ## Returns `Promise`: The assignment, or `null` when the store runs no A/B holdout (or the API cannot be reached: then everyone sees TryYourSize). - **`experimentId`** (`string`, required) - **`arm`** (`ExperimentArm`, required) - **`visitorId`** (`string`, required): The anonymous visitor ID: save it with the order (`visitor_id` in POST /v1/orders). ## Example **JavaScript** ```js const a = await tys.experiment(); // Keep it with the cart; your server sends it with the order. if (a) cart.attributes.tys_visitor_id = a.visitorId; ``` **Node.js** ```ts await tys.orders.create({ id: "order_1042", visitor_id: cart.attributes.tys_visitor_id, // from tys.experiment() in the browser // …lines, currency }); ``` ## See also - [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md) - [Orders and returns](https://docs.tryyoursize.com/guides/outcomes.md) --- # tys.recommendationHandle() > A sealed handle for your server's POST /v1/recommendations (headless or server-rendered pages). Source: https://docs.tryyoursize.com/reference/web/client-recommendation-handle/ *Method* · `@tryyoursize/sdk` A sealed handle for your server's POST /v1/recommendations (headless or server-rendered pages). Send `handle` to your server; it gets sizes only. Null when the shopper is not signed in to TryYourSize on your site, has no body yet, or is in the A/B holdout. Expires after an hour. ```ts recommendationHandle(): Promise; ``` ## Returns `Promise`: `{ handle, expiresAt }`, or `null`. - **`handle`** (`string`, required): Opaque, starts with `tys_rh_`; send it to your server as is. - **`expiresAt`** (`string`, required): ISO date-time; ask for a new one after it (an hour). ## Examples ### Browser: get a handle, send it to your server **JavaScript** ```js const h = await tys.recommendationHandle(); const res = await fetch("/api/sizes", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ handle: h?.handle, products: ["SKU-123", "SKU-456"] }), }); ``` ### Server: exchange it for sizes **Node.js** ```ts const { data } = await tys.recommendations.create({ handle, products: ["SKU-123", "SKU-456"] }); ``` **Python** ```python answer = tys.recommendations.create(handle=handle, products=["SKU-123", "SKU-456"]) ``` ## See also - [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md) - [recommendations.create() (Node.js)](https://docs.tryyoursize.com/reference/node/recommendations-create.md) --- # tys.recommendation() > The shopper's recommended size for a product, in your size labels, without mounting anything (a hidden frame on our origin answers). Source: https://docs.tryyoursize.com/reference/web/client-recommendation/ *Method* · `@tryyoursize/sdk` The shopper's recommended size for a product, in your size labels, without mounting anything (a hidden frame on our origin answers). Sizes only. A visitor in the [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md) always gets `unavailable`, so you show them no size either. The call never starts a simulation. ```ts recommendation(product: string): Promise; ``` ## Parameters - **`product`** (`string`, required): Your product ID or SKU, as synced to TryYourSize. ## Returns `Promise`: The shopper's recommended size for the product. - **`product`** (`string`, required) - **`status`** (`RecommendationStatus`, required): `ready` (`size` is set), `computing` (a try-on the shopper started is still running), `none` (no size of this garment can be worn on this body), `signed_out` (no TryYourSize session on your site) or `unavailable` (no size for this product yet). - **`size`** (`string | null`, required): The recommended size in the store's own label (its size map), or null. - **`studioSize`** (`string | null`, required): The same size as entered in TryYourSize Studio, or null. - **`wearableSizes`** (`string[]`, optional): Sizes that can be worn on this body, in the store's labels (when simulated). ## Example **JavaScript** ```js const rec = await tys.recommendation("SKU-123"); if (rec.status === "ready") { sizePicker.value = rec.size; // "M", in your own labels } ``` **React** ```tsx function usePreselect(product: string, select: (size: string) => void) { const tys = useTryYourSize(); useEffect(() => { tys?.recommendation(product).then((r) => r.status === "ready" && r.size && select(r.size)); }, [tys, product]); } ``` ## See also - [Preselect the recommended size](https://docs.tryyoursize.com/guides/recommendation-event.md) - ["recommendation" event](https://docs.tryyoursize.com/reference/web/event-recommendation.md) --- # tys.version > The SDK version (semver) of the loaded TryYourSize.js. Source: https://docs.tryyoursize.com/reference/web/client-version/ *Property* · `@tryyoursize/sdk` The SDK version (semver) of the loaded TryYourSize.js. Include it in support requests. ```ts readonly version: string; ``` ## Example **JavaScript** ```js console.log(tys.version); // "0.2.0" ``` --- # element.close() > Closes the window if it is open. Source: https://docs.tryyoursize.com/reference/web/element-close/ *Method* · `@tryyoursize/sdk` Closes the window if it is open. ```ts close(): void; ``` ## Example **JavaScript** ```js guide.close(); ``` --- # element.destroy() > Unmounts the element and drops its handlers. Source: https://docs.tryyoursize.com/reference/web/element-destroy/ *Method* · `@tryyoursize/sdk` Unmounts the element and drops its handlers. Create a new element to show it again. ```ts destroy(): void; ``` ## Example **JavaScript** ```js // Single-page apps: when the product view goes away guide.destroy(); ``` **React** ```tsx // React components destroy their element on unmount. ``` --- # element.elementType > The element's type: "fit", "button" or "sizeGuide". Source: https://docs.tryyoursize.com/reference/web/element-element-type/ *Property* · `@tryyoursize/sdk` The element's type: `"fit"`, `"button"` or `"sizeGuide"`. ```ts readonly elementType: ElementType; ``` ## Example **JavaScript** ```js if (el.elementType === "sizeGuide") el.update({ variant: "button" }); ``` --- # element.getRecommendation() > The shopper's recommended size for this product once known (see the recommendation event). Source: https://docs.tryyoursize.com/reference/web/element-get-recommendation/ *Method* · `@tryyoursize/sdk` The shopper's recommended size for this product once known (see the `recommendation` event). ```ts getRecommendation(): Promise; ``` ## Returns `Promise`: Resolves with the first recommendation once known (see `tys.recommendation()` for the fields). ## Example **JavaScript** ```js const r = await guide.getRecommendation(); if (r.status === "ready") preselect(r.size); ``` **React** ```tsx const ref = useRef(null); const r = await ref.current?.getRecommendation(); ``` --- # element.mount() > Shows the element inside a container of your page. Source: https://docs.tryyoursize.com/reference/web/element-mount/ *Method* · `@tryyoursize/sdk` Shows the element inside a container of your page. A visitor in the A/B holdout gets nothing at all. ```ts mount(target: HTMLElement | string): void; ``` ## Parameters - **`target`** (`HTMLElement | string`, required): A CSS selector or an element. Put it where the shopper chooses a size. ## Errors `TryYourSizeError` with code `mount_not_found`, `already_mounted` or `element_destroyed`. ## Example **JavaScript** ```js const guide = elements.create("sizeGuide", { product: "SKU-123" }); guide.mount("#size-guide"); // or guide.mount(document.querySelector(".size-row")) ``` **React** ```tsx // React components mount for you on commit: ``` --- # element.off() > Removes a handler, or every handler of the event when you pass none. Source: https://docs.tryyoursize.com/reference/web/element-off/ *Method* · `@tryyoursize/sdk` Removes a handler, or every handler of the event when you pass none. ```ts off(event: K, handler?: (e: ElementEvents[K]) => void): void; ``` ## Parameters - **`event`** (`keyof ElementEvents`, required): The event name. - **`handler`** (`(e: ElementEvents[K]) => void`, optional): The handler passed to `on()`. Omit it to remove all. ## Example **JavaScript** ```js const onSize = ({ size }) => preselect(size); guide.on("size", onSize); // later guide.off("size", onSize); ``` --- # element.on() > Listens to an element event. Source: https://docs.tryyoursize.com/reference/web/element-on/ *Method* · `@tryyoursize/sdk` Listens to an element event. A handler that throws never breaks the SDK or the other handlers. ```ts on(event: K, handler: (e: ElementEvents[K]) => void): void; ``` ## Parameters - **`event`** (`keyof ElementEvents`, required): `"ready"`, `"size"`, `"open"`, `"close"`, `"loaderror"` or `"recommendation"`. See the event pages. - **`handler`** (`(e: ElementEvents[K]) => void`, required): Called with the event payload. ## Example **JavaScript** ```js guide.on("recommendation", (r) => r.status === "ready" && preselect(r.size)); guide.on("size", ({ size }) => preselect(size)); // "Use this size" in the window guide.on("loaderror", ({ error }) => console.warn(error.code)); ``` **React** ```tsx r.status === "ready" && preselect(r.size)} onSize={({ size }) => preselect(size)} onLoadError={({ error }) => console.warn(error.code)} /> ``` **HTML** ```html const el = document.querySelector("tys-size-guide"); el.addEventListener("tys:recommendation", (e) => e.detail.status === "ready" && preselect(e.detail.size)); el.addEventListener("tys:size", (e) => preselect(e.detail.size)); ``` --- # element.once() > Like on(), for the next event only. Source: https://docs.tryyoursize.com/reference/web/element-once/ *Method* · `@tryyoursize/sdk` Like `on()`, for the next event only. ```ts once(event: K, handler: (e: ElementEvents[K]) => void): void; ``` ## Parameters - **`event`** (`keyof ElementEvents`, required): The event name. - **`handler`** (`(e: ElementEvents[K]) => void`, required): Called once with the event payload. ## Example **JavaScript** ```js guide.once("ready", () => performance.mark("tys-ready")); ``` --- # element.open() > Opens the TryYourSize window for this element's product, as the element itself does on click. Source: https://docs.tryyoursize.com/reference/web/element-open/ *Method* · `@tryyoursize/sdk` Opens the TryYourSize window for this element's product, as the element itself does on click. Use it from your own button or link. ```ts open(): void; ``` ## Example **JavaScript** ```js document.querySelector("#my-size-link").addEventListener("click", (e) => { e.preventDefault(); guide.open(); }); ``` **React** ```tsx const ref = useRef(null); ``` --- # element.unmount() > Removes the element from the page and closes its window. Source: https://docs.tryyoursize.com/reference/web/element-unmount/ *Method* · `@tryyoursize/sdk` Removes the element from the page and closes its window. Handlers are kept: you can `mount()` it again. ```ts unmount(): void; ``` ## Example **JavaScript** ```js guide.unmount(); guide.mount("#another-place"); ``` --- # element.update() > Changes the element's options in place. Source: https://docs.tryyoursize.com/reference/web/element-update/ *Method* · `@tryyoursize/sdk` Changes the element's options in place. A new `product` (or demo body) reloads the element; `variant` and `label` restyle it. ```ts update(options: Partial): void; ``` ## Parameters - **`options`** (`Partial`, required) - **`product`** (`string`, required): Your product ID or SKU, as synced to TryYourSize. - **`variant`** (`"link" | "button"`, optional): "sizeGuide" only: a text link (default, like most stores' "Size guide") or an outlined button. - **`label`** (`string`, optional): "sizeGuide" only: your own words for "Size guide" (otherwise localized from `locale`). - **`demo`** (`DemoOptions`, optional): `"fit"` only, test keys only: demo mode. The element shows the garment on a named sample body (synthetic size-chart tape values, never a real person's), with no sign-in: size chips, objective numbers in cm and kPa, a turning body and a sample-body picker. For landing pages and sales demos. - **`body`** (`string`, required): A sample body id: `sample-female-158`, `sample-female-165`, `sample-female-172`, `sample-male-172`, `sample-male-178` or `sample-male-186`. - **`height`** (`number`, optional): Height of the element in px (default 640). ## Examples ### A variant page that switches product **JavaScript** ```js colorSelect.addEventListener("change", () => guide.update({ product: colorSelect.value })); ``` **React** ```tsx // Changing the prop calls update() for you: ``` --- # elements.create() > Creates an element for one product. Source: https://docs.tryyoursize.com/reference/web/elements-create/ *Method* · `@tryyoursize/sdk` Creates an element for one product. Mount it with `mount()`. ```ts create(type: ElementType, options: ElementOptions): TysElement; ``` ## Parameters - **`type`** (`"fit" | "button" | "sizeGuide"`, required): `"sizeGuide"`: a link-sized "Size guide" with the TryYourSize mark, for the spot next to your size picker. `"button"`: a compact "Find my size" button. `"fit"`: the inline element, with sign-in and "your body is ready" in your page. - **`options`** (`ElementOptions`, required) - **`product`** (`string`, required): Your product ID or SKU, as synced to TryYourSize. - **`variant`** (`"link" | "button"`, optional): "sizeGuide" only: a text link (default, like most stores' "Size guide") or an outlined button. - **`label`** (`string`, optional): "sizeGuide" only: your own words for "Size guide" (otherwise localized from `locale`). - **`demo`** (`DemoOptions`, optional): `"fit"` only, test keys only: demo mode. The element shows the garment on a named sample body (synthetic size-chart tape values, never a real person's), with no sign-in: size chips, objective numbers in cm and kPa, a turning body and a sample-body picker. For landing pages and sales demos. - **`body`** (`string`, required): A sample body id: `sample-female-158`, `sample-female-165`, `sample-female-172`, `sample-male-172`, `sample-male-178` or `sample-male-186`. - **`height`** (`number`, optional): Height of the element in px (default 640). ## Returns `TysElement`: The element. Several of the same type are allowed (for example a product grid). ## Errors `TryYourSizeError` with code `invalid_option` (missing `product`, bad `variant`, `label` or `demo`) or `unknown_element_type`. ## Examples ### Size guide next to the size picker **JavaScript** ```js const guide = tys.elements().create("sizeGuide", { product: "SKU-123", variant: "link", // or "button" label: "Find your size", // optional; otherwise localized }); guide.mount("#size-guide"); ``` **React** ```tsx ``` **HTML** ```html ``` ### Demo mode (test keys only) **JavaScript** ```js const demo = tys.elements().create("fit", { product: "CREW-TSHIRT", demo: { body: "sample-female-165", height: 640 }, }); demo.mount("#demo"); ``` **React** ```tsx ``` **HTML** ```html ``` ## See also - [Size guide quickstart](https://docs.tryyoursize.com/quickstarts/size-guide.md) --- # elements.getElement() > The most recently created element of a type that is still alive, or null. Source: https://docs.tryyoursize.com/reference/web/elements-get-element/ *Method* · `@tryyoursize/sdk` The most recently created element of a type that is still alive, or `null`. ```ts getElement(type: ElementType): TysElement | null; ``` ## Parameters - **`type`** (`"fit" | "button" | "sizeGuide"`, required): The element type. ## Returns `TysElement | null`: The element, or `null`. ## Example **JavaScript** ```js elements.getElement("sizeGuide")?.open(); ``` --- # elements.update() > Live update, e.g. Source: https://docs.tryyoursize.com/reference/web/elements-update/ *Method* · `@tryyoursize/sdk` Live update, e.g. when the shop switches to dark mode. Mounted elements and an open window restyle at once; nothing reloads. ```ts update(options: ElementsOptions): void; ``` ## Parameters - **`options`** (`ElementsOptions`, required) - **`appearance`** (`Appearance`, optional): Look and feel; by default the elements inherit the page's font and colours. - **`locale`** (`Locale`, optional) ## Examples ### Follow your dark mode **JavaScript** ```js const dark = matchMedia("(prefers-color-scheme: dark)"); dark.addEventListener("change", () => elements.update({ appearance: { theme: "inherit" } }), // re-reads your page's colours ); ``` **React** ```tsx // The provider calls elements.update() when options.appearance changes. ``` ## See also - [Appearance and theming](https://docs.tryyoursize.com/guides/appearance.md) --- # "close" event > Our window closed. Source: https://docs.tryyoursize.com/reference/web/event-close/ *Event* · `@tryyoursize/sdk` Our window closed. With unsaved measurements, the window asks the shopper first. ```ts close: { elementType: ElementType }; ``` ## Payload - **`elementType`** (`"fit" | "button" | "sizeGuide"`, required): Which element. ## Example **JavaScript** ```js guide.on("close", () => sizePicker.focus()); ``` **React** ```tsx pickerRef.current?.focus()} /> ``` --- # "loaderror" event > Our window could not be loaded or reached (network, Content Security Policy, an origin not registered for the key). Source: https://docs.tryyoursize.com/reference/web/event-loaderror/ *Event* · `@tryyoursize/sdk` Our window could not be loaded or reached (network, Content Security Policy, an origin not registered for the key). ```ts loaderror: { elementType: ElementType; error: TryYourSizeError }; ``` ## Payload - **`elementType`** (`"fit" | "button" | "sizeGuide"`, required): Which element. - **`error`** (`TryYourSizeError`, required): Code `window_unreachable`. See [Web SDK errors](https://docs.tryyoursize.com/sdk/errors.md#window_unreachable). ## Example **JavaScript** ```js guide.on("loaderror", ({ error }) => { console.warn(error.code, error.docUrl); guide.unmount(); // fall back to your own size chart }); ``` **React** ```tsx report(error.code)} /> ``` --- # "open" event > Our window opened. Source: https://docs.tryyoursize.com/reference/web/event-open/ *Event* · `@tryyoursize/sdk` Our window opened. ```ts open: { elementType: ElementType }; ``` ## Payload - **`elementType`** (`"fit" | "button" | "sizeGuide"`, required): Which element opened it. ## Example **JavaScript** ```js guide.on("open", () => analytics.track("size_guide_open")); ``` **React** ```tsx track("size_guide_open")} /> ``` **HTML** ```html el.addEventListener("tys:open", () => track("size_guide_open")); ``` --- # "ready" event > The element is on the page (the size guide and button at once; the inline element once our window is connected). Source: https://docs.tryyoursize.com/reference/web/event-ready/ *Event* · `@tryyoursize/sdk` The element is on the page (the size guide and button at once; the inline element once our window is connected). Fires once per mount. ```ts ready: { elementType: ElementType }; ``` ## Payload - **`elementType`** (`"fit" | "button" | "sizeGuide"`, required): Which element. ## Example **JavaScript** ```js guide.on("ready", ({ elementType }) => console.log(elementType, "ready")); ``` **React** ```tsx setReady(true)} /> ``` **HTML** ```html el.addEventListener("tys:ready", (e) => console.log(e.detail.elementType)); ``` --- # "recommendation" event > The shopper's recommended size for this product, in your size labels: on mount when the shopper is already connected, and on every change. Source: https://docs.tryyoursize.com/reference/web/event-recommendation/ *Event* · `@tryyoursize/sdk` The shopper's recommended size for this product, in your size labels: on mount when the shopper is already connected, and on every change. Sizes only, never measurements. The size guide element always listens; for other elements, adding a handler starts it. Preselect your picker, but never override a size the shopper picked themselves. ```ts recommendation: Recommendation; ``` ## Payload - **`product`** (`string`, required) - **`status`** (`RecommendationStatus`, required): `ready`, `computing`, `none`, `signed_out` or `unavailable`. See [tys.recommendation()](https://docs.tryyoursize.com/reference/web/client-recommendation.md). - **`size`** (`string | null`, required): The recommended size in the store's own label (its size map), or null. - **`studioSize`** (`string | null`, required): The same size as entered in TryYourSize Studio, or null. - **`wearableSizes`** (`string[]`, optional): Sizes that can be worn on this body, in the store's labels (when simulated). ## Example **JavaScript** ```js let touched = false; sizePicker.addEventListener("change", () => (touched = true)); guide.on("recommendation", (r) => { if (r.status === "ready" && !touched) sizePicker.value = r.size; }); ``` **React** ```tsx { if (r.status === "ready" && !touched) setSize(r.size); }} /> ``` **HTML** ```html el.addEventListener("tys:recommendation", (e) => { if (e.detail.status === "ready" && !touched) sizePicker.value = e.detail.size; }); ``` ## See also - [Preselect the recommended size](https://docs.tryyoursize.com/guides/recommendation-event.md) --- # "size" event > The shopper chose a size in our window ("Use this size"): preselect that variant. Source: https://docs.tryyoursize.com/reference/web/event-size/ *Event* · `@tryyoursize/sdk` The shopper chose a size in our window ("Use this size"): preselect that variant. ```ts size: SizeEvent; ``` ## Payload - **`product`** (`string`, required) - **`size`** (`string`, required) - **`verified`** (`boolean`, required): True when the size comes from a completed try-on on the shopper's body. ## Example **JavaScript** ```js guide.on("size", ({ product, size }) => { if (product === currentSku) sizePicker.value = size; }); ``` **React** ```tsx setSize(size)} /> ``` **HTML** ```html el.addEventListener("tys:size", (e) => (sizePicker.value = e.detail.size)); ``` --- # isPublishableKey() > A publishable key is the only key allowed in a browser. Source: https://docs.tryyoursize.com/reference/web/is-publishable-key/ *Function* · `@tryyoursize/sdk` A publishable key is the only key allowed in a browser. ```ts function isPublishableKey(key: string): boolean ``` ## Parameters - **`key`** (`string`, required): Any string. ## Returns `boolean`: True for `pk_test_…` and `pk_live_…` keys. ## Example **JavaScript** ```js if (!isPublishableKey(settings.tysKey)) throw new Error("Use the publishable key"); ``` --- # loadTryYourSize() > Loads TryYourSize.js from our CDN once (the latest compatible v1 build) and creates a client, like loadStripe(). Source: https://docs.tryyoursize.com/reference/web/load-tryyoursize/ *Function* · `@tryyoursize/sdk/loader` Loads TryYourSize.js from our CDN once (the latest compatible v1 build) and creates a client, like `loadStripe()`. Resolves to `null` during server-side rendering, so it is safe to call at module level in Next.js, Remix or Astro. ```ts async function loadTryYourSize( publishableKey: string, options: TryYourSizeOptions & { scriptUrl?: string } = {}, ): Promise ``` ## Parameters - **`publishableKey`** (`string`, required): Your publishable key (`pk_test_…` or `pk_live_…`). - **`options`** (`TryYourSizeOptions & { scriptUrl?: string }`, optional, default `{}`): The options of `TryYourSize()`, plus `scriptUrl` to load the script from another URL (development only). - **`embedOrigin`** (`string`, optional): Override for development (e.g. http://localhost:5173). - **`apiOrigin`** (`string`, optional): Override of the API origin for development (e.g. http://localhost:8000): the A/B holdout config. - **`locale`** (`Locale`, optional): Language of our windows; "auto" follows the browser. ## Returns `Promise`: A promise of the client, or `null` on the server. ## Errors Rejects with `TryYourSizeError` code `script_load_failed` when the script cannot load (network, CSP). Call it again to retry. ## Example **JavaScript** ```js const tys = await loadTryYourSize("pk_test_…"); if (tys) tys.elements().create("sizeGuide", { product: "SKU-123" }).mount("#size-guide"); ``` **React** ```tsx const tysPromise = loadTryYourSize("pk_test_…"); // module level, once return ; } ``` --- # Recommendation > Recommendation Source: https://docs.tryyoursize.com/reference/web/recommendation/ *Type* · `@tryyoursize/sdk` ```ts interface Recommendation ``` ## Properties - **`product`** (`string`, required) - **`status`** (`RecommendationStatus`, required) - **`size`** (`string | null`, required): The recommended size in the store's own label (its size map), or null. - **`studioSize`** (`string | null`, required): The same size as entered in TryYourSize Studio, or null. - **`wearableSizes`** (`string[]`, optional): Sizes that can be worn on this body, in the store's labels (when simulated). ## Example **JavaScript** ```js { "product": "SKU-123", "status": "ready", "size": "M", "studioSize": "M", "wearableSizes": ["M", "L"] } ``` ## Notes `status` is one of: - `ready`: `size` is the recommended size. - `computing`: a try-on the shopper started is running; you get another recommendation when it finishes. - `none`: no size of this garment can be worn on this body. - `signed_out`: no TryYourSize session on your site. - `unavailable`: signed in, but no size for this product yet (no garment data, no body yet, or not tried on). Page views never start a simulation; the shopper does, with "See it on you". --- # resolveAppearance() > Resolves what the windows receive: for "inherit", the font, text colour and background where the element is mounted (buttons use the text colour, the way most stores style their primary button); expli Source: https://docs.tryyoursize.com/reference/web/resolve-appearance/ *Function* · `@tryyoursize/sdk` Resolves what the windows receive: for "inherit", the font, text colour and background where the element is mounted (buttons use the text colour, the way most stores style their primary button); explicit variables always win. ```ts function resolveAppearance(mount: Element, input: Appearance = {}): Appearance ``` ## Parameters - **`mount`** (`Element`, required): The element whose computed style is read. - **`input`** (`Appearance`, optional, default `{}`): Your appearance; explicit variables win. ## Returns `Appearance`: The appearance our windows receive. ## Example **JavaScript** ```js console.log(resolveAppearance(document.querySelector("#size-guide"))); ``` --- # TryYourSizeError > Every error the SDK throws or emits: a stable type and code to branch on, a message, and a link to its explanation. Source: https://docs.tryyoursize.com/reference/web/tryyoursize-error/ *Class* · `@tryyoursize/sdk` Every error the SDK throws or emits: a stable `type` and `code` to branch on, a message, and a link to its explanation. ```ts class TryYourSizeError extends Error ``` ## Properties - **`type`** (`ErrorType`, required): `"integration_error"` (fix your code) or `"load_error"` (network, CSP). - **`code`** (`ErrorCode`, required): A stable code. All codes: [Web SDK errors](https://docs.tryyoursize.com/sdk/errors.md). - **`docUrl`** (`string`, required): Link to the code's explanation. ## Example **JavaScript** ```js try { TryYourSize(key); } catch (e) { if (e instanceof TryYourSizeError && e.code === "invalid_key") showConfigError(); } ``` --- # TryYourSize() > Creates a client for your publishable key. Source: https://docs.tryyoursize.com/reference/web/tryyoursize/ *Function* · `@tryyoursize/sdk` Creates a client for your publishable key. Every element and call goes through it. ```ts function TryYourSize(publishableKey: string, options: TryYourSizeOptions = {}): TryYourSizeClient ``` ## Parameters - **`publishableKey`** (`string`, required): Your publishable key (`pk_test_…` or `pk_live_…`) from Studio → Developers → API keys. It works only on the origins registered for it. A secret key (`sk_…`) throws `secret_key_in_browser`. - **`options`** (`TryYourSizeOptions`, optional, default `{}`) - **`embedOrigin`** (`string`, optional): Override for development (e.g. http://localhost:5173). - **`apiOrigin`** (`string`, optional): Override of the API origin for development (e.g. http://localhost:8000): the A/B holdout config. - **`locale`** (`Locale`, optional): Language of our windows; "auto" follows the browser. ## Returns `TryYourSizeClient`: The client: `elements()`, `recommendation()`, `recommendationHandle()`, `experiment()` and `version`. ## Errors `TryYourSizeError` with code `secret_key_in_browser` or `invalid_key`. See [Web SDK errors](https://docs.tryyoursize.com/sdk/errors.md). ## Example **JavaScript** ```js const tys = TryYourSize("pk_test_…", { locale: "es" }); // windows in Spanish ``` **HTML** ```html ``` **React** ```tsx // The provider calls TryYourSize() for you. ``` ## See also - [loadTryYourSize()](https://docs.tryyoursize.com/reference/web/load-tryyoursize.md) - [Keys and modes](https://docs.tryyoursize.com/get-started/keys-and-modes.md) --- # > The compact "Find my size" button as a tag. Source: https://docs.tryyoursize.com/reference/web/tys-fit-button/ *Web component* · `@tryyoursize/sdk/elements` The compact "Find my size" button as a tag. ## Attributes - **`publishable-key`** (`string`, required): Your publishable key. - **`product`** (`string`, required): Your product ID or SKU. - **`locale`** (`string`, optional): BCP 47 language. ## Example **HTML** ```html ``` --- # > The inline element as a tag: sign-in and "your body is ready" in your page. Source: https://docs.tryyoursize.com/reference/web/tys-fit/ *Web component* · `@tryyoursize/sdk/elements` The inline element as a tag: sign-in and "your body is ready" in your page. ## Attributes - **`publishable-key`** (`string`, required): Your publishable key. - **`product`** (`string`, required): Your product ID or SKU. - **`locale`** (`string`, optional): BCP 47 language. - **`demo-body`** (`string`, optional): Test keys only: demo mode on a sample body, e.g. `sample-female-165`. ## Example **HTML** ```html ``` --- # > The size guide element as a tag: "Size guide" with the TryYourSize mark, then "Your size: M" once the shopper is connected. Source: https://docs.tryyoursize.com/reference/web/tys-size-guide/ *Web component* · `@tryyoursize/sdk/elements` The size guide element as a tag: "Size guide" with the TryYourSize mark, then "Your size: M" once the shopper is connected. ## Attributes - **`publishable-key`** (`string`, required): Your publishable key (`pk_test_…` / `pk_live_…`). - **`product`** (`string`, required): Your product ID or SKU. - **`variant`** (`"link" | "button"`, optional): A text link (default) or an outlined button. - **`label`** (`string`, optional): Your own words for "Size guide" (otherwise localized). - **`locale`** (`string`, optional): BCP 47 language; default: your page's ``, then the browser. ## Example **HTML** ```html ``` ## See also - [Shopify (no code)](https://docs.tryyoursize.com/quickstarts/shopify.md) - [Size guide quickstart](https://docs.tryyoursize.com/quickstarts/size-guide.md) --- # VERSION > The SDK version (semver), also sent to our windows. Source: https://docs.tryyoursize.com/reference/web/version/ *Constant* · `@tryyoursize/sdk` The SDK version (semver), also sent to our windows. ```ts VERSION ``` ## Example **JavaScript** ```js ``` --- # Web SDK errors > Every TryYourSizeError code of TryYourSize.js and @tryyoursize/react, what causes it and how to fix it. Source: https://docs.tryyoursize.com/sdk/errors/ TryYourSize.js throws (or emits with the `loaderror` event) a [`TryYourSizeError`](https://docs.tryyoursize.com/reference/web/tryyoursize-error.md) with a stable `type` and `code`, a `message`, and a `docUrl` that points at the matching section below. | Type | Meaning | |---|---| | `integration_error` | Your code called the SDK wrongly. Thrown synchronously; fix the call. | | `load_error` | Our window or script could not be loaded or reached: network, Content Security Policy, an origin not registered for the key. Emitted with `loaderror`, or a rejected promise. | ```js try { const tys = TryYourSize(window.TYS_KEY); } catch (e) { console.error(e.code, e.docUrl); } ``` ## secret_key_in_browser You passed a secret key (`sk_…`) to `TryYourSize()`. Secret keys must never reach a browser: anyone could read them. Use your **publishable** key (`pk_test_…` / `pk_live_…`) and [roll the secret key](https://docs.tryyoursize.com/get-started/keys-and-modes.md) if it was deployed. ## invalid_key The key is not a publishable key (`pk_test_…` or `pk_live_…` followed by at least 8 characters). Copy it again from Studio → Developers → API keys. ## mount_not_found `element.mount(target)` found nothing: the selector matched no element, or it ran before the element was in the page. Mount after the container exists (for example after `DOMContentLoaded`, or in your framework's mounted hook). ## already_mounted The element is already mounted. Call `unmount()` first, or create a second element (several of one type are allowed). ## element_destroyed The element was destroyed with `destroy()`. Create a new one with `elements.create()`. ## unknown_element_type `elements.create(type)` got a type that does not exist. Use `"sizeGuide"`, `"button"` or `"fit"`. ## invalid_option An option is missing or invalid: `product` is required; `variant` must be `"link"` or `"button"`; `label` is a string of at most 60 characters; `demo` needs a test key and `{ body: "sample-female-165" }`; `recommendation()` needs a product. The message names the option. ## window_unreachable Our window did not load or connect within 15 seconds. Emitted with `loaderror`. Check, in this order: 1. Your Content Security Policy allows `frame-src https://embed.tryyoursize.com` (and `script-src https://js.tryyoursize.com` for the CDN build). 2. The page's origin is registered for the key's mode in Studio (Settings → Stores → Allowed sites). `http://localhost` works in test mode without registering. 3. The network: an ad blocker or a corporate proxy blocking `tryyoursize.com`. See [Troubleshooting](https://docs.tryyoursize.com/guides/troubleshooting.md#the-element-does-not-appear). ## script_load_failed `loadTryYourSize()` could not load `https://js.tryyoursize.com/v1/tys.js` (network, CSP `script-src`). The promise rejects; call `loadTryYourSize()` again to retry. --- # TryYourSize API Source: https://docs.tryyoursize.com/api/reference/ Server API conventions: authenticate with `Authorization: Bearer sk_…` (or a restricted `rk_…` key holding the operation's key scopes). Pin the version with the `TYS-Version` header (a date, e.g. `2026-10-25`; without it the latest version answers and the response's `TYS-Version` says which). Errors are `{error, status, code, doc_url, request_id}`. POSTs accept an `Idempotency-Key` (responses stored 24 h). Lists are cursor-paginated (`limit`, `cursor` → `data`, `has_more`, `next_cursor`). Rate limits answer `429` with `Retry-After`; every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. Base URL: `https://api.tryyoursize.com`. Version header: `TYS-Version`. Spec: https://docs.tryyoursize.com/openapi.json ## Authentication - **PublishableKey**: The store's publishable key (pk_test_… / pk_live_…). Safe in browsers; works only from the origins registered for the key. - **SecretKey**: A secret key (sk_test_… / sk_live_…) or a restricted key (rk_test_… / rk_live_…) from your server: `Authorization: Bearer sk_…`. A secret key has every scope; a restricted key only the scopes it was created with. Each operation lists its key scopes. ## Embed Called by TryYourSize.js and the measuring window from your page (publishable key). You do not call these yourself; they are listed so you can read your request logs and CSP reports. - [Bootstrap the measurement window](https://docs.tryyoursize.com/api/reference/operations/initembed.md): `POST /v1/embed/init` - [Count a shopper funnel step (measuring window)](https://docs.tryyoursize.com/api/reference/operations/recordfunnelevent.md): `POST /v1/funnel_events` ## TryOn The try-on window's routes (publishable key, from our window's origin). Shown for reference. - [Is "See it on you" available for a product?](https://docs.tryyoursize.com/api/reference/operations/getproducttryon.md): `GET /v1/products/{product}/tryon` - [See a product on the shopper's body](https://docs.tryyoursize.com/api/reference/operations/starttryon.md): `POST /v1/tryon` - [A try-on's status, progress and result](https://docs.tryyoursize.com/api/reference/operations/gettryon.md): `GET /v1/tryon/{tryon_id}` - [The colourways the window draws on the simulated garment](https://docs.tryyoursize.com/api/reference/operations/getpublicproductappearance.md): `GET /v1/products/{product}/appearance` ## Audit Your organization's audit log, read with a restricted key holding `audit:read`. - [Audit log of the key's organization (server API)](https://docs.tryyoursize.com/api/reference/operations/listauditeventswithkey.md): `GET /v1/audit_events` ## Outcomes Orders and returns from your server, for the A/B results and size-related return rates. No customer data is accepted. - [Record an order (idempotent by order ID)](https://docs.tryyoursize.com/api/reference/operations/createoutcomeorder.md): `POST /v1/orders` - [Record a return of an order line (idempotent by return ID and line)](https://docs.tryyoursize.com/api/reference/operations/createoutcomereturn.md): `POST /v1/returns` ## Recommendations The shopper's recommended size for your server or a headless storefront, from a handle the browser SDK issues. Sizes only, never measurements. - [Seal the signed-in shopper's body into a handle for the brand's server](https://docs.tryyoursize.com/api/reference/operations/createrecommendationhandle.md): `POST /v1/recommendation_handles` - [The shopper's recommended size for up to 25 products](https://docs.tryyoursize.com/api/reference/operations/createrecommendations.md): `POST /v1/recommendations` ## Catalog API Products, size maps, fit groups and garment drafts, with a secret key or a restricted key holding the listed scopes. The catalog is shared by test and live mode: test answers drafts, live answers published versions. - [List the store's products](https://docs.tryyoursize.com/api/reference/operations/listcatalogproducts.md): `GET /v1/products` - [Create a product](https://docs.tryyoursize.com/api/reference/operations/createcatalogproduct.md): `POST /v1/products` - [A product](https://docs.tryyoursize.com/api/reference/operations/getcatalogproduct.md): `GET /v1/products/{product}` - [Update a product](https://docs.tryyoursize.com/api/reference/operations/updatecatalogproduct.md): `PATCH /v1/products/{product}` - [A product's size map (Studio size → your size label)](https://docs.tryyoursize.com/api/reference/operations/getcatalogsizemap.md): `GET /v1/products/{product}/size_map` - [Replace a product's size map](https://docs.tryyoursize.com/api/reference/operations/savecatalogsizemap.md): `PUT /v1/products/{product}/size_map` - [Garment versions of a product, newest first](https://docs.tryyoursize.com/api/reference/operations/listcataloggarments.md): `GET /v1/products/{product}/garments` - [Start a new draft garment version](https://docs.tryyoursize.com/api/reference/operations/createcataloggarmentdraft.md): `POST /v1/products/{product}/garments` - [A garment version](https://docs.tryyoursize.com/api/reference/operations/getcataloggarment.md): `GET /v1/garments/{garment_id}` - [Replace a draft's content](https://docs.tryyoursize.com/api/reference/operations/updatecataloggarmentdraft.md): `PUT /v1/garments/{garment_id}` - [Publish a complete draft](https://docs.tryyoursize.com/api/reference/operations/publishcataloggarment.md): `POST /v1/garments/{garment_id}/publish` - [List fit groups](https://docs.tryyoursize.com/api/reference/operations/listcatalogfitgroups.md): `GET /v1/fit_groups` - [Create a fit group](https://docs.tryyoursize.com/api/reference/operations/createcatalogfitgroup.md): `POST /v1/fit_groups` - [A fit group](https://docs.tryyoursize.com/api/reference/operations/getcatalogfitgroup.md): `GET /v1/fit_groups/{fit_group_id}` - [Move products into a fit group](https://docs.tryyoursize.com/api/reference/operations/linkcatalogfitgroupproducts.md): `POST /v1/fit_groups/{fit_group_id}/products` --- # Create a fit group ```http POST /v1/fit_groups ``` Source: https://docs.tryyoursize.com/api/reference/operations/createcatalogfitgroup/ **Key scopes:** `products:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. One garment shared by several products (colourways, prints, reorders). Send an `Idempotency-Key` to retry safely. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `Idempotency-Key` (header, `string`, optional): A unique value per logical request (a UUID v4 is ideal), 1-255 printable ASCII characters. With a secret or restricted key, the first response is stored for 24 hours per store and mode: a retry with the same key and the same body gets the stored response back (header `Idempotent-Replayed: true`); the same key with a different body is `422 idempotency_key_reused`; a retry while the first request still runs is `409 idempotency_key_in_use`. Responses with a 5xx status are not stored, so a retry runs again. ## Request body `application/json`: `FitGroupInput` - `name` (`string`, required, max length 200) - `product_ids` (`string (uuid)[]`, optional): Products to put in the new group (create only). - `source_product_id` (`string (uuid)`, optional): "Use the garment of…": the new group takes over this product's garment versions (create only). ## Responses ### 201 Created (`FitGroup`) - `id` (`string (uuid)`, required) - `name` (`string`, required) - `product_ids` (`string (uuid)[]`, required) - `published_version_id` (`string (uuid)`, optional) - `draft_version_id` (`string (uuid)`, optional) - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ### 409 Conflicts with the current state (duplicate SKU, editing a published version) (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/fit_groups \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.fitGroups.create()`](https://docs.tryyoursize.com/reference/node/fit-groups-create.md) - Python: [`tys.fit_groups.create()`](https://docs.tryyoursize.com/reference/python/fit-groups-create.md) --- # Start a new draft garment version ```http POST /v1/products/{product}/garments ``` Source: https://docs.tryyoursize.com/api/reference/operations/createcataloggarmentdraft/ **Key scopes:** `garments:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. Sizes, points of measure (mm) per size, grading and fabric, as in Studio. A product has at most one draft (409). Send an `Idempotency-Key` to retry safely. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `product` (path, `string`, required): The product: its TryYourSize ID (UUID) or the brand's SKU / style ID (`external_id`). An ID is looked up first. - `Idempotency-Key` (header, `string`, optional): A unique value per logical request (a UUID v4 is ideal), 1-255 printable ASCII characters. With a secret or restricted key, the first response is stored for 24 hours per store and mode: a retry with the same key and the same body gets the stored response back (header `Idempotent-Replayed: true`); the same key with a different body is `422 idempotency_key_reused`; a retry while the first request still runs is `409 idempotency_key_in_use`. Responses with a 5xx status are not stored, so a retry runs again. ## Request body `application/json`: `GarmentInput` - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `fit_intent` (`"skinny" | "slim" | "regular" | "relaxed" | "oversized"`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - `sizes` (`string[]`, required): Size labels, smallest to largest. - `poms` (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - `grading` (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - `base_size` (`string`, required) - `increments_mm` (`object`, required) - `fabric` (`Fabric`, required): What the tech pack / mill sheet says. - `name` (`string`, optional) - `construction` (`"jersey" | "rib" | "interlock" | "fleece" | "pique" | "plain_weave" | "twill" | "denim" | "satin" | "other"`, optional) - `composition` (`FiberShare[]`, required) - `fiber` (`"cotton" | "polyester" | "elastane" | "viscose" | "modal" | "lyocell" | "wool" | "linen" | "polyamide" | "silk" | "acrylic" | "other"`, required) - `pct` (`number (double)`, required) - `weight_gsm` (`number (double)`, optional): Areal density, g/m². - `stretch_warp_pct` (`number (double)`, optional): Stretch along the warp (lengthwise), percent. - `stretch_weft_pct` (`number (double)`, optional): Stretch along the weft (crosswise), percent. - `notes` (`string`, optional) - `import_id` (`string (uuid)`, optional): Pattern import linked to this version (omit to unlink). - `season` (`string`, optional, max length 40): Season tag, e.g. "SS27". - `tolerances_mm` (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - `spec_source` (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - `kind` (`"manual" | "paste" | "xlsx" | "csv" | "pdf" | "workbook" | "clo" | "browzwear"`, required) - `filename` (`string`, optional, max length 255) ## Responses ### 201 Draft created (`GarmentVersion`) - `id` (`string (uuid)`, required) - `product_id` (`string (uuid)`, required) - `version` (`integer`, required) - `status` (`"draft" | "published" | "retired"`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `fit_intent` (`"skinny" | "slim" | "regular" | "relaxed" | "oversized"`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - `sizes` (`string[]`, required) - `poms` (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - `grading` (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - `base_size` (`string`, required) - `increments_mm` (`object`, required) - `fabric` (`Fabric`, required): What the tech pack / mill sheet says. - `name` (`string`, optional) - `construction` (`"jersey" | "rib" | "interlock" | "fleece" | "pique" | "plain_weave" | "twill" | "denim" | "satin" | "other"`, optional) - `composition` (`FiberShare[]`, required) - `fiber` (`"cotton" | "polyester" | "elastane" | "viscose" | "modal" | "lyocell" | "wool" | "linen" | "polyamide" | "silk" | "acrylic" | "other"`, required) - `pct` (`number (double)`, required) - `weight_gsm` (`number (double)`, optional): Areal density, g/m². - `stretch_warp_pct` (`number (double)`, optional): Stretch along the warp (lengthwise), percent. - `stretch_weft_pct` (`number (double)`, optional): Stretch along the weft (crosswise), percent. - `notes` (`string`, optional) - `import_id` (`string (uuid)`, optional) - `issues` (`GarmentIssue[]`, required): What still blocks publishing (errors) or deserves a look (warnings). - `severity` (`"error" | "warning"`, required) - `code` (`string`, required): e.g. out_of_range, not_graded, missing_pom, composition_total - `field` (`string`, required): category | fit_intent | sizes | poms | grading | import | fabric. - `size` (`string`, optional) - `pom` (`string`, optional) - `params` (`IssueParams`, optional): The values in `message`, for clients that word the finding in their own language (keys depend on `code`, e.g. value_mm, min_mm, max_mm, prev_size, fiber, max). Numbers are plain decimals ("." separator); lengths in mm. - `message` (`string`, required): English text; clients in other languages can word it from code, size, pom and params. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `published_at` (`string (date-time)`, optional) - `season` (`string`, optional) - `tolerances_mm` (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - `spec_source` (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - `kind` (`"manual" | "paste" | "xlsx" | "csv" | "pdf" | "workbook" | "clo" | "browzwear"`, required) - `filename` (`string`, optional, max length 255) - `fit_group_id` (`string (uuid)`, optional): The fit group (garments row) this version belongs to (absent until grouped). ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ### 409 Conflicts with the current state (duplicate SKU, editing a published version) (`Error`) ### 422 Values to fix (each issue points at the field, size and POM) (`GarmentRejected`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/products//garments \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.products.garments.createDraft()`](https://docs.tryyoursize.com/reference/node/products-garments-create-draft.md) - Python: [`tys.products.garments.create_draft()`](https://docs.tryyoursize.com/reference/python/products-garments-create-draft.md) --- # Create a product ```http POST /v1/products ``` Source: https://docs.tryyoursize.com/api/reference/operations/createcatalogproduct/ **Key scopes:** `products:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. The SKU / style ID (`external_id`) is unique per store (409). Send an `Idempotency-Key` to retry safely. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `Idempotency-Key` (header, `string`, optional): A unique value per logical request (a UUID v4 is ideal), 1-255 printable ASCII characters. With a secret or restricted key, the first response is stored for 24 hours per store and mode: a retry with the same key and the same body gets the stored response back (header `Idempotent-Replayed: true`); the same key with a different body is `422 idempotency_key_reused`; a retry while the first request still runs is `409 idempotency_key_in_use`. Responses with a 5xx status are not stored, so a retry runs again. ## Request body `application/json`: `ProductInput` - `external_id` (`string`, required) - `name` (`string`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. ## Responses ### 201 Created (`Product`) - `id` (`string (uuid)`, required) - `external_id` (`string`, required): The brand's SKU or style ID (unique per brand). - `name` (`string`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `published_version` (`integer`, optional): Garment version shoppers are fitted against, if any. - `draft_version` (`integer`, optional): Garment version being edited, if any. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `fit_group_id` (`string (uuid)`, optional): The fit group whose garment this product uses (absent until grouped). - `brand_id` (`string (uuid)`, optional): The brand the product belongs to; every store of the brand sees it. - `home_store_id` (`string (uuid)`, optional): The store the product was created in. - `retired_at` (`string (date-time)`, optional): Set when the product is retired; shoppers no longer get a size for it. - `available` (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 409 Conflicts with the current state (duplicate SKU, editing a published version) (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/products \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.products.create()`](https://docs.tryyoursize.com/reference/node/products-create.md) - Python: [`tys.products.create()`](https://docs.tryyoursize.com/reference/python/products-create.md) --- # Record an order (idempotent by order ID) ```http POST /v1/orders ``` Source: https://docs.tryyoursize.com/api/reference/operations/createoutcomeorder/ **Key scopes:** `orders:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. From the brand's server with a secret key or a restricted key with `orders:write`. Re-sending the same `id` updates the order (200); a new one is 201. The A/B arm comes from `visitor_id` (recomputed by TryYourSize) or `experiment_arm`. No customer data is accepted. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Request body `application/json`: `OutcomeOrderInput` An order from the brand's server. Re-sending the same id updates it (idempotent). No customer data: there is no customer, email, name or address field, and unknown fields are refused. - `id` (`string`, required, max length 200): Your order ID. - `ordered_at` (`string (date-time)`, required) - `currency` (`string`, optional): ISO 4217, e.g. EUR. - `visitor_id` (`string`, optional, max length 64): The SDK's anonymous visitor ID (`(await tys.experiment())?.visitorId`), saved with the cart. TryYourSize recomputes the A/B arm from it and stores only its hash. Never a customer ID. - `experiment_arm` (`"treatment" | "holdout"`, optional): The arm, when you cannot send visitor_id. - `lines` (`OutcomeLineInput[]`, required, max 200 items) - `id` (`string`, required, max length 200): Your order line ID (unique within the order). - `product` (`string`, required, max length 200): Your product ID (the one you pass to the SDK). - `variant` (`string`, optional, max length 200) - `size` (`string`, required, max length 40): The size bought, in your labels. - `quantity` (`integer`, required) - `unit_price_minor` (`integer (int64)`, optional): Price of one unit in minor units (5995 = 59.95). Optional; used for the refund value estimate. - `tys_shown` (`boolean`, optional): A TryYourSize element was shown for this purchase. - `recommended_size` (`string`, optional, max length 40): The size TryYourSize recommended (the SDK's recommendation event, or the _tys_size cart line property). - `attribution` (`"cart_attribute" | "sdk" | "api"`, optional): Where recommended_size came from. ## Responses ### 200 Updated (`OutcomeOrderResult`) - `id` (`string (uuid)`, required) - `external_id` (`string`, required) - `created` (`boolean`, required): False when the order already existed and was updated. - `experiment_arm` (`"treatment" | "holdout"`, optional) - `lines` (`integer`, required) ### 201 Created (`OutcomeOrderResult`) - `id` (`string (uuid)`, required) - `external_id` (`string`, required) - `created` (`boolean`, required): False when the order already existed and was updated. - `experiment_arm` (`"treatment" | "holdout"`, optional) - `lines` (`integer`, required) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/orders \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.orders.create()`](https://docs.tryyoursize.com/reference/node/orders-create.md) - Python: [`tys.orders.create()`](https://docs.tryyoursize.com/reference/python/orders-create.md) --- # Record a return of an order line (idempotent by return ID and line) ```http POST /v1/returns ``` Source: https://docs.tryyoursize.com/api/reference/operations/createoutcomereturn/ **Key scopes:** `returns:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. Secret key, or a restricted key with `returns:write` (e.g. a returns platform). The order must have been sent first (404 otherwise). Reasons are normalised to TryYourSize's taxonomy. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Request body `application/json`: `OutcomeReturnInput` A return of (part of) an order line. Idempotent by (line, id). Free-text notes are not accepted. - `id` (`string`, required, max length 200): Your return (or RMA) ID. - `order_id` (`string`, required, max length 200) - `line_id` (`string`, required, max length 200) - `quantity` (`integer`, required) - `reason` (`string`, optional, max length 60): size_too_small, size_too_large, fit_other, style, quality, damaged_or_wrong, changed_mind, other; Shopify's ReturnReason codes (SIZE_TOO_SMALL…) and common returns-platform words are mapped. Empty: unspecified. - `exchange` (`boolean`, optional): The item was exchanged rather than refunded. - `exchange_size` (`string`, optional, max length 40): The size sent in exchange (implies exchange). - `returned_at` (`string (date-time)`, required) ## Responses ### 200 Updated (`OutcomeReturnResult`) - `created` (`boolean`, required) - `reason` (`string`, required): The reason as normalised to TryYourSize's taxonomy. ### 201 Created (`OutcomeReturnResult`) - `created` (`boolean`, required) - `reason` (`string`, required): The reason as normalised to TryYourSize's taxonomy. ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/returns \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.returns.create()`](https://docs.tryyoursize.com/reference/node/returns-create.md) - Python: [`tys.returns.create()`](https://docs.tryyoursize.com/reference/python/returns-create.md) --- # Seal the signed-in shopper's body into a handle for the brand's server ```http POST /v1/recommendation_handles ``` Source: https://docs.tryyoursize.com/api/reference/operations/createrecommendationhandle/ **Key:** publishable (`pk_test_…` / `pk_live_…`), from an origin registered for the key. Called by the TryYourSize window (the SDK's invisible status frame), never by a brand's code: it needs the shopper's session (`Authorization: Bearer tys_sess_…`), which only our origin can read, plus the store's publishable key. The answer is an encrypted handle bound to the store, the mode and the shopper; the page passes it to the brand's server for POST /v1/recommendations. Nothing is stored: the handle itself is the sealed body, and neither the page nor the brand's server can read it. Expires after an hour. ## Authentication Publishable key (`pk_test_…` / `pk_live_…`), from an origin registered for the key. ## Request body `application/json`: `RecommendationHandleRequest` The signed-in shopper's body as the TryYourSize window holds it (the same fields as POST /v1/tryon, without the product). Sent only by our window (status frame) or SDKs, never by a brand's page or server. - `basics` (`BodyBasics`, required) - `sex` (`"female" | "male"`, required) - `age_years` (`integer`, required) - `height_mm` (`number (double)`, required) - `weight_kg` (`number (double)`, required) - `measurements` (`Measurement[]`, optional, max 64 items) - `code` (`"neck_girth" | "shoulder_width" | "arm_length" | "armscye_girth" | "upper_arm_girth" | "wrist_girth" | "chest_girth" | "underbust_girth" | "bust_point_to_bust_point" | "shoulder_to_bust_point" | "back_length" | "waist_girth" | "front_waist_arc" | "high_hip_girth" | "abdomen_girth" | "hip_girth" | "front_hip_arc" | "thigh_girth" | "knee_girth" | "calf_girth" | "ankle_girth" | "inseam" | "outseam" | "crotch_length"`, required): Body measurement from set v1. - `value_mm` (`number (double)`, required): Measured value in millimetres. - `source` (`"tape_self" | "tape_helper" | "flat_garment" | "estimated"`, required) - `body_state` (`number (double)[]`, required, max 64 items) ## Responses ### 201 Created (`RecommendationHandle`) - `handle` (`string`, required): Starts with `tys_rh_`. Up to 4 KB. - `expires_at` (`string (date-time)`, required) - `mode` (`"test" | "live"`, required) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 422 The content is rejected (e.g. a body that cannot be rebuilt) (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/recommendation_handles \ -H "Authorization: Bearer pk_test_…" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` --- # The shopper's recommended size for up to 25 products ```http POST /v1/recommendations ``` Source: https://docs.tryyoursize.com/api/reference/operations/createrecommendations/ **Key scopes:** `fit_checks:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. From the brand's server (or a headless storefront's backend) with a secret key, or a restricted key with `fit_checks:read`. Send the handle the browser SDK gave your page (`tys.recommendationHandle()`); the answer has sizes only, in your size labels, never measurements. Like a page view, it never starts a simulation: a body already simulated with the garment answers with the existing result, or with a size estimate when one is certain for this body; otherwise the status is `unavailable` until the shopper asks for the try-on. A delivered size counts as a fit check, like in the browser. The handle's mode must match the key's (a test handle with a live key is 403). A handle whose shopper deleted their account or disconnected the store is refused (401 `handle_invalid`). ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `Idempotency-Key` (header, `string`, optional): A unique value per logical request (a UUID v4 is ideal), 1-255 printable ASCII characters. With a secret or restricted key, the first response is stored for 24 hours per store and mode: a retry with the same key and the same body gets the stored response back (header `Idempotent-Replayed: true`); the same key with a different body is `422 idempotency_key_reused`; a retry while the first request still runs is `409 idempotency_key_in_use`. Responses with a 5xx status are not stored, so a retry runs again. ## Request body `application/json`: `RecommendationsRequest` - `handle` (`string`, required, max length 8192): A recommendation handle from the browser SDK (`tys.recommendationHandle()`). - `products` (`string[]`, required, max 25 items): Product SKUs / style IDs (what you pass to the element as `product`). ## Responses ### 200 One answer per product, in the order asked (`RecommendationList`) - `data` (`ServerRecommendation[]`, required) - `product` (`string`, required) - `status` (`"ready" | "computing" | "none" | "unavailable"`, required): `ready`: a size. `computing`: a simulation the shopper started is running (ask again later). `none`: simulated, no size can be worn. `unavailable`: no published garment for this product, not simulated on this body yet, or the store's plan has no fit checks left. A page view or this API never starts a simulation. - `size` (`string`, required): The recommended size in the store's label (the product's size map), or null. - `studio_size` (`string`, required): The same size as entered in TryYourSize Studio, or null. - `wearable_sizes` (`string[]`, required): Sizes that can be worn on this body, in the store's labels. - `method` (`"estimate" | "simulated"`, optional): How the size was determined: `estimate` (a size estimate certain for this body, no try-on of it yet) or `simulated` (from a try-on of this body). A coarse informational label: do not build logic on its values. - `tryon_id` (`string (uuid)`, optional): The try-on that produced the size, when there is one. Send it back with orders for attribution. - `mode` (`"test" | "live"`, required) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 429 Too many requests; retry later (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/recommendations \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.recommendations.create()`](https://docs.tryyoursize.com/reference/node/recommendations-create.md) - Python: [`tys.recommendations.create()`](https://docs.tryyoursize.com/reference/python/recommendations-create.md) --- # A fit group ```http GET /v1/fit_groups/{fit_group_id} ``` Source: https://docs.tryyoursize.com/api/reference/operations/getcatalogfitgroup/ **Key scopes:** `products:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `fit_group_id` (path, `string (uuid)`, required): A fit group. ## Responses ### 200 OK (`FitGroup`) - `id` (`string (uuid)`, required) - `name` (`string`, required) - `product_ids` (`string (uuid)[]`, required) - `published_version_id` (`string (uuid)`, optional) - `draft_version_id` (`string (uuid)`, optional) - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/fit_groups/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.fitGroups.retrieve()`](https://docs.tryyoursize.com/reference/node/fit-groups-retrieve.md) - Python: [`tys.fit_groups.retrieve()`](https://docs.tryyoursize.com/reference/python/fit-groups-retrieve.md) --- # A garment version ```http GET /v1/garments/{garment_id} ``` Source: https://docs.tryyoursize.com/api/reference/operations/getcataloggarment/ **Key scopes:** `products:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `garment_id` (path, `string (uuid)`, required): A garment *version* (garment_versions.id). A fit group (garments table) is `fit_group_id`. ## Responses ### 200 OK (`GarmentVersion`) - `id` (`string (uuid)`, required) - `product_id` (`string (uuid)`, required) - `version` (`integer`, required) - `status` (`"draft" | "published" | "retired"`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `fit_intent` (`"skinny" | "slim" | "regular" | "relaxed" | "oversized"`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - `sizes` (`string[]`, required) - `poms` (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - `grading` (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - `base_size` (`string`, required) - `increments_mm` (`object`, required) - `fabric` (`Fabric`, required): What the tech pack / mill sheet says. - `name` (`string`, optional) - `construction` (`"jersey" | "rib" | "interlock" | "fleece" | "pique" | "plain_weave" | "twill" | "denim" | "satin" | "other"`, optional) - `composition` (`FiberShare[]`, required) - `fiber` (`"cotton" | "polyester" | "elastane" | "viscose" | "modal" | "lyocell" | "wool" | "linen" | "polyamide" | "silk" | "acrylic" | "other"`, required) - `pct` (`number (double)`, required) - `weight_gsm` (`number (double)`, optional): Areal density, g/m². - `stretch_warp_pct` (`number (double)`, optional): Stretch along the warp (lengthwise), percent. - `stretch_weft_pct` (`number (double)`, optional): Stretch along the weft (crosswise), percent. - `notes` (`string`, optional) - `import_id` (`string (uuid)`, optional) - `issues` (`GarmentIssue[]`, required): What still blocks publishing (errors) or deserves a look (warnings). - `severity` (`"error" | "warning"`, required) - `code` (`string`, required): e.g. out_of_range, not_graded, missing_pom, composition_total - `field` (`string`, required): category | fit_intent | sizes | poms | grading | import | fabric. - `size` (`string`, optional) - `pom` (`string`, optional) - `params` (`IssueParams`, optional): The values in `message`, for clients that word the finding in their own language (keys depend on `code`, e.g. value_mm, min_mm, max_mm, prev_size, fiber, max). Numbers are plain decimals ("." separator); lengths in mm. - `message` (`string`, required): English text; clients in other languages can word it from code, size, pom and params. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `published_at` (`string (date-time)`, optional) - `season` (`string`, optional) - `tolerances_mm` (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - `spec_source` (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - `kind` (`"manual" | "paste" | "xlsx" | "csv" | "pdf" | "workbook" | "clo" | "browzwear"`, required) - `filename` (`string`, optional, max length 255) - `fit_group_id` (`string (uuid)`, optional): The fit group (garments row) this version belongs to (absent until grouped). ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/garments/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.garments.retrieve()`](https://docs.tryyoursize.com/reference/node/garments-retrieve.md) - Python: [`tys.garments.retrieve()`](https://docs.tryyoursize.com/reference/python/garments-retrieve.md) --- # A product ```http GET /v1/products/{product} ``` Source: https://docs.tryyoursize.com/api/reference/operations/getcatalogproduct/ **Key scopes:** `products:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. By ID or SKU. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `product` (path, `string`, required): The product: its TryYourSize ID (UUID) or the brand's SKU / style ID (`external_id`). An ID is looked up first. ## Responses ### 200 OK (`Product`) - `id` (`string (uuid)`, required) - `external_id` (`string`, required): The brand's SKU or style ID (unique per brand). - `name` (`string`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `published_version` (`integer`, optional): Garment version shoppers are fitted against, if any. - `draft_version` (`integer`, optional): Garment version being edited, if any. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `fit_group_id` (`string (uuid)`, optional): The fit group whose garment this product uses (absent until grouped). - `brand_id` (`string (uuid)`, optional): The brand the product belongs to; every store of the brand sees it. - `home_store_id` (`string (uuid)`, optional): The store the product was created in. - `retired_at` (`string (date-time)`, optional): Set when the product is retired; shoppers no longer get a size for it. - `available` (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/products/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.products.retrieve()`](https://docs.tryyoursize.com/reference/node/products-retrieve.md) - Python: [`tys.products.retrieve()`](https://docs.tryyoursize.com/reference/python/products-retrieve.md) --- # A product's size map (Studio size → your size label) ```http GET /v1/products/{product}/size_map ``` Source: https://docs.tryyoursize.com/api/reference/operations/getcatalogsizemap/ **Key scopes:** `products:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `product` (path, `string`, required): The product: its TryYourSize ID (UUID) or the brand's SKU / style ID (`external_id`). An ID is looked up first. ## Responses ### 200 OK (`ProductSizeMap`) - `entries` (`object[]`, required) - `studio_size` (`string`, required) - `shop_value` (`string`, required) - `unmapped_shop_values` (`string[]`, optional): Variant size values of the product with no Studio size yet (read only). ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/products//size_map \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.products.sizeMap.retrieve()`](https://docs.tryyoursize.com/reference/node/products-size-map-retrieve.md) - Python: [`tys.products.size_map.retrieve()`](https://docs.tryyoursize.com/reference/python/products-size-map-retrieve.md) --- # Is "See it on you" available for a product? ```http GET /v1/products/{product}/tryon ``` Source: https://docs.tryyoursize.com/api/reference/operations/getproducttryon/ **Key:** publishable (`pk_test_…` / `pk_live_…`), from an origin registered for the key. Used by the inline element, the size-guide button and the window to decide whether to offer the try-on. Called from the brand's page (an allowed origin of the store) or from the window. `sizes` are the garment's sizes as entered in Studio; `shop_sizes` are the store's own labels for them (the product's size map, e.g. "M" → "Medium"), so the page can preselect its own variant. ## Authentication Publishable key (`pk_test_…` / `pk_live_…`), from an origin registered for the key. ## Parameters - `product` (path, `string`, required): The product's SKU / style ID in the store. ## Responses ### 200 Availability (never 404 for an unknown product, `available` is false) (`ProductTryOn`) - `available` (`boolean`, required) - `product` (`string`, optional): The product's SKU / style ID, as given. - `garment_name` (`string`, optional) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, optional): The garment type; it sets which POMs are needed. - `sizes` (`string[]`, optional): The garment's size labels as entered in Studio, smallest to largest. Try-on results and `summary.recommended_size` use these. - `shop_sizes` (`string[]`, optional): The store's own label for each entry of `sizes`, same order (the product's size map, e.g. "M" → "Medium"; the Studio label where the map has no entry). - `garment_version_id` (`string (uuid)`, optional) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/products//tryon \ -H "Authorization: Bearer pk_test_…" \ -H "TYS-Version: 2026-10-25" ``` --- # The colourways the window draws on the simulated garment ```http GET /v1/products/{product}/appearance ``` Source: https://docs.tryyoursize.com/api/reference/operations/getpublicproductappearance/ **Key:** publishable (`pk_test_…` / `pk_live_…`), from an origin registered for the key. Called by the window (or the brand's page) with the publishable key, from an allowed origin of the store or the window's own domain. Never 404: an unknown product, or one without appearance, has no colourways. Cached by the browser for a minute. ## Authentication Publishable key (`pk_test_…` / `pk_live_…`), from an origin registered for the key. ## Parameters - `product` (path, `string`, required): The product's SKU / style ID in the store. - `colour` (query, `string`, optional): The shop's selected colour option value; picks `selected` (case-insensitive key match). ## Responses ### 200 Appearance (`PublicProductAppearance`) - `product` (`string`, required) - `colourways` (`AppearanceColourway[]`, required) - `key` (`string`, required, max length 80): The shop's colour option value ("Rinse indigo"), or "" for the product itself. - `name` (`string`, optional, max length 80): Display name of the colour. - `base_color` (`string`, required): The fabric's colour, sRGB hex. - `finish` (`"matte_jersey" | "denim_twill" | "rib" | "satin"`, required): Fabric finish preset. The viewer draws its surface detail (normal and roughness) from small procedural maps at true scale: `matte_jersey` (knit loops, soft sheen), `denim_twill` (diagonal 2/1 twill), `rib` (wales along the grain), `satin` (smooth, low roughness, sheen). - `print` (`AppearanceRepeatPrint`, optional): An all-over print: one tile of the brand's artwork repeated at its real size, placed in each pattern piece's own frame (u across the grain, v along it), like printed fabric under the cutter. The print is continuous inside a piece and breaks at seams, as on a real garment. - `asset_id` (`string (uuid)`, required) - `width_cm` (`number (double)`, required): Width of one repeat on the fabric, cm (across the grain before rotation). - `height_cm` (`number (double)`, required): Height of one repeat on the fabric, cm (along the grain before rotation). - `offset_x_cm` (`number (double)`, optional): Shift of the tile origin across the grain, cm. - `offset_y_cm` (`number (double)`, optional): Shift of the tile origin along the grain, cm. - `rotation_deg` (`number (double)`, optional): Angle of the tile's vertical to the grainline, degrees counter-clockwise (0 = upright on the grain). - `panels` (`AppearancePanelOverride[]`, optional, max 16 items) - `panel` (`string`, required) - `hide_repeat` (`boolean`, optional): Leave this piece in the base colour (no all-over print). - `placement` (`AppearancePlacementPrint`, optional): Artwork printed once on a pattern piece (a chest print), positioned in cm. - `asset_id` (`string (uuid)`, required) - `width_cm` (`number (double)`, required): Printed width, cm. The height follows the artwork's aspect ratio. - `x_cm` (`number (double)`, required): Centre of the artwork from the piece's centre line, cm (+ toward the piece's +u side). - `y_cm` (`number (double)`, required): Centre of the artwork down from the piece's highest point (the high point shoulder on a bodice), cm. - `rotation_deg` (`number (double)`, optional) - `selected` (`integer`, optional): Index into `colourways` of the one matching `?colour=` (else 0, the default). Absent when there are none. - `assets` (`AppearanceAsset[]`, required) - `id` (`string (uuid)`, required) - `filename` (`string`, required) - `content_type` (`"image/png" | "image/jpeg" | "image/svg+xml"`, required) - `bytes` (`integer (int64)`, required) - `width_px` (`integer`, required): Pixel width (SVG, its viewBox width). - `height_px` (`integer`, required) - `url` (`string`, required): Path of the file on this API (`/v1/appearance_assets/{id}`), relative to the API base URL. - `created_at` (`string (date-time)`, required) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/products//appearance \ -H "Authorization: Bearer pk_test_…" \ -H "TYS-Version: 2026-10-25" ``` --- # A try-on's status, progress and result ```http GET /v1/tryon/{tryon_id} ``` Source: https://docs.tryyoursize.com/api/reference/operations/gettryon/ **Key:** publishable (`pk_test_…` / `pk_live_…`), from an origin registered for the key. Readable only by the viewer the try-on was issued to: send the `viewer_token` from POST /v1/tryon as `Authorization: Bearer tys_view_…`, or the session of the signed-in shopper who started it. Anyone else (another session, no token) gets 404. ## Authentication Publishable key (`pk_test_…` / `pk_live_…`), from an origin registered for the key. ## Parameters - `tryon_id` (path, `string (uuid)`, required) - `view` (query, `"full" | "summary"`, optional): `full` (default) answers with the whole `result` (meshes for every size, MBs). `summary` leaves `result` out and answers `summary` instead (the recommended size and the sizes that can be worn, a few bytes): for the size-guide button and the store's recommended-size event, which never need the meshes. ## Responses ### 200 The try-on (`TryOnJob`) - `id` (`string`, required): The try-on's id (uuid). Empty only for a size estimate (POST ?start=false&view=summary answered with no try-on of this body); there is nothing to poll. - `viewer_token` (`string`, optional): Only in the answer to POST /v1/tryon: the window's proof that it may read this try-on. Send it as `Authorization: Bearer tys_view_…` to GET /v1/tryon/{tryon_id}. Stored hashed; never sent again. - `status` (`"queued" | "running" | "done" | "failed"`, required) - `progress` (`object`, required) - `sizes_done` (`integer`, required) - `sizes_total` (`integer`, required) - `result` (`object`, optional): Present when `status` is `done`, and while it runs: the sizes computed so far, each with `method` (simulated) and `state` (preview: shown early, may still change; settled; refining: still being computed, no mesh yet; simulated). The try-on result for the window: body mesh and body_state (no basics, no tape measurements), garment with its fabric and estimated POMs, recommended size and per-size meshes, pressure kPa, ease mm and zones. Binary arrays are base64 little-endian; per-vertex values are rounded to display precision. - `summary` (`object`, optional): Present when `status` is `done` and the request asked for `view=summary`: what a store may receive (sizes only, never measurements). Size labels are the garment's (Studio) sizes; map them with `shop_sizes` from GET /v1/products/{product}/tryon. - `recommended_size` (`string`, optional): The recommended size; absent when no size can be worn. - `no_size_fits` (`boolean`, required) - `wearable_sizes` (`string[]`, required): Sizes that can be worn on this body (judged on this body), smallest to largest. - `method` (`"estimate" | "simulated"`, optional): How the size was determined: `estimate` (a size estimate, no try-on of this body yet) or `simulated` (from the try-on of this body). A coarse informational label: do not build logic on its values. - `recommendation` (`object`, optional): The size answer with its method. On the first answer of a try-on it may already be there (`method: estimate`) while the 3D view is computed; once done it is the try-on's own size. An estimate is final (`ready` or `none`) only when it is certain for this body; otherwise the status is `computing`, no size is named, and the try-on's 3D view gives it. - `recommended_size` (`string`, optional) - `method` (`"estimate" | "simulated"`, required): Same as `summary.method`. A coarse informational label; do not build logic on its values. - `status` (`"ready" | "none" | "computing" | "unavailable"`, required) - `wearable_sizes` (`string[]`, optional) - `error` (`object`, optional): Present when `status` is `failed`. - `code` (`string`, required): INVALID_BODY, UNSUPPORTED_CATEGORY, MISSING_POM, INVALID_POM, INVALID_FABRIC, NO_SIZES, UNAVAILABLE or ENGINE_ERROR. - `message` (`string`, required) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/tryon/ \ -H "Authorization: Bearer pk_test_…" \ -H "TYS-Version: 2026-10-25" ``` --- # Bootstrap the measurement window ```http POST /v1/embed/init ``` Source: https://docs.tryyoursize.com/api/reference/operations/initembed/ **Key:** publishable (`pk_test_…` / `pk_live_…`), from an origin registered for the key. Called when the measurement window opens. Checks the publishable key and that the brand page embedding the window (parent_origin) is allowed for the store. ## Authentication Publishable key (`pk_test_…` / `pk_live_…`), from an origin registered for the key. ## Request body `application/json`: `InitEmbedRequest` - `parent_origin` (`string`, required): Origin of the brand page that opened the window, e.g. https://shop.example ## Responses ### 200 The store the window works for (`EmbedStore`) - `store_name` (`string`, required) - `mode` (`"test" | "live"`, required) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/embed/init \ -H "Authorization: Bearer pk_test_…" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` --- # Move products into a fit group ```http POST /v1/fit_groups/{fit_group_id}/products ``` Source: https://docs.tryyoursize.com/api/reference/operations/linkcatalogfitgroupproducts/ **Key scopes:** `products:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. The products then use this group's garment. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `fit_group_id` (path, `string (uuid)`, required): A fit group. - `Idempotency-Key` (header, `string`, optional): A unique value per logical request (a UUID v4 is ideal), 1-255 printable ASCII characters. With a secret or restricted key, the first response is stored for 24 hours per store and mode: a retry with the same key and the same body gets the stored response back (header `Idempotent-Replayed: true`); the same key with a different body is `422 idempotency_key_reused`; a retry while the first request still runs is `409 idempotency_key_in_use`. Responses with a 5xx status are not stored, so a retry runs again. ## Request body `application/json`: `LinkProductsRequest` - `product_ids` (`string (uuid)[]`, required, max 500 items) ## Responses ### 200 OK (`FitGroup`) - `id` (`string (uuid)`, required) - `name` (`string`, required) - `product_ids` (`string (uuid)[]`, required) - `published_version_id` (`string (uuid)`, optional) - `draft_version_id` (`string (uuid)`, optional) - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ### 409 Conflicts with the current state (duplicate SKU, editing a published version) (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/fit_groups//products \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.fitGroups.linkProducts()`](https://docs.tryyoursize.com/reference/node/fit-groups-link-products.md) - Python: [`tys.fit_groups.link_products()`](https://docs.tryyoursize.com/reference/python/fit-groups-link-products.md) --- # Audit log of the key's organization (server API) ```http GET /v1/audit_events ``` Source: https://docs.tryyoursize.com/api/reference/operations/listauditeventswithkey/ **Key scopes:** `audit:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. The audit log of the secret key's organization, newest first. Same filters as the Studio listing (no CSV). Secret keys (sk_) and restricted keys (rk_) with the `audit:read` scope. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `cursor` (query, `string`, optional) - `action` (query, `string`, optional) - `actor` (query, `string`, optional): actor_type:actor_id - `store_id` (query, `string (uuid)`, optional): Only events of this store - `target` (query, `string`, optional): target_type or target_type:target_id, e.g. api_key: - `from` (query, `string (date-time)`, optional): Events at or after this time - `to` (query, `string (date-time)`, optional): Events before this time - `limit` (query, `integer`, optional): Page size (default 50, at most 200; ignored by the CSV export) ## Responses ### 200 OK (`AuditEventPage`) - `events` (`AuditEvent[]`, required) - `id` (`string (uuid)`, required) - `organization_id` (`string (uuid)`, optional) - `store_id` (`string (uuid)`, optional) - `actor_type` (`"user" | "api_key" | "partner_user" | "staff" | "system" | "shopify"`, required) - `actor_id` (`string`, required) - `actor_label` (`string`, optional) - `on_behalf_of` (`string (uuid)`, optional) - `action` (`string`, required) - `target_type` (`string`, optional) - `target_id` (`string`, optional) - `metadata` (`object`, optional) - `ip` (`string`, optional) - `user_agent` (`string`, optional) - `request_id` (`string`, optional) - `created_at` (`string (date-time)`, required) - `next_cursor` (`string`, optional) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/audit_events \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.auditEvents.list()`](https://docs.tryyoursize.com/reference/node/audit-events-list.md) - Python: [`tys.audit_events.list()`](https://docs.tryyoursize.com/reference/python/audit-events-list.md) --- # List fit groups ```http GET /v1/fit_groups ``` Source: https://docs.tryyoursize.com/api/reference/operations/listcatalogfitgroups/ **Key scopes:** `products:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. Cursor-paginated. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `limit` (query, `integer`, optional): Page size (list convention). Default 20, at most 100. - `cursor` (query, `string`, optional): Opaque cursor from the previous page's `next_cursor` (list convention). Omit it for the first page. ## Responses ### 200 OK (`FitGroupPage`) - `data` (`FitGroup[]`, required) - `id` (`string (uuid)`, required) - `name` (`string`, required) - `product_ids` (`string (uuid)[]`, required) - `published_version_id` (`string (uuid)`, optional) - `draft_version_id` (`string (uuid)`, optional) - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `has_more` (`boolean`, required) - `next_cursor` (`string`, optional) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/fit_groups \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.fitGroups.list()`](https://docs.tryyoursize.com/reference/node/fit-groups-list.md) - Python: [`tys.fit_groups.list()`](https://docs.tryyoursize.com/reference/python/fit-groups-list.md) --- # Garment versions of a product, newest first ```http GET /v1/products/{product}/garments ``` Source: https://docs.tryyoursize.com/api/reference/operations/listcataloggarments/ **Key scopes:** `products:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. `has_more` is always false (a product has few versions). ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `product` (path, `string`, required): The product: its TryYourSize ID (UUID) or the brand's SKU / style ID (`external_id`). An ID is looked up first. ## Responses ### 200 OK (`GarmentVersionPage`) - `data` (`GarmentVersion[]`, required) - `id` (`string (uuid)`, required) - `product_id` (`string (uuid)`, required) - `version` (`integer`, required) - `status` (`"draft" | "published" | "retired"`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `fit_intent` (`"skinny" | "slim" | "regular" | "relaxed" | "oversized"`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - `sizes` (`string[]`, required) - `poms` (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - `grading` (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - `base_size` (`string`, required) - `increments_mm` (`object`, required) - `fabric` (`Fabric`, required): What the tech pack / mill sheet says. - `name` (`string`, optional) - `construction` (`"jersey" | "rib" | "interlock" | "fleece" | "pique" | "plain_weave" | "twill" | "denim" | "satin" | "other"`, optional) - `composition` (`FiberShare[]`, required) - `fiber` (`"cotton" | "polyester" | "elastane" | "viscose" | "modal" | "lyocell" | "wool" | "linen" | "polyamide" | "silk" | "acrylic" | "other"`, required) - `pct` (`number (double)`, required) - `weight_gsm` (`number (double)`, optional): Areal density, g/m². - `stretch_warp_pct` (`number (double)`, optional): Stretch along the warp (lengthwise), percent. - `stretch_weft_pct` (`number (double)`, optional): Stretch along the weft (crosswise), percent. - `notes` (`string`, optional) - `import_id` (`string (uuid)`, optional) - `issues` (`GarmentIssue[]`, required): What still blocks publishing (errors) or deserves a look (warnings). - `severity` (`"error" | "warning"`, required) - `code` (`string`, required): e.g. out_of_range, not_graded, missing_pom, composition_total - `field` (`string`, required): category | fit_intent | sizes | poms | grading | import | fabric. - `size` (`string`, optional) - `pom` (`string`, optional) - `params` (`IssueParams`, optional): The values in `message`, for clients that word the finding in their own language (keys depend on `code`, e.g. value_mm, min_mm, max_mm, prev_size, fiber, max). Numbers are plain decimals ("." separator); lengths in mm. - `message` (`string`, required): English text; clients in other languages can word it from code, size, pom and params. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `published_at` (`string (date-time)`, optional) - `season` (`string`, optional) - `tolerances_mm` (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - `spec_source` (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - `kind` (`"manual" | "paste" | "xlsx" | "csv" | "pdf" | "workbook" | "clo" | "browzwear"`, required) - `filename` (`string`, optional, max length 255) - `fit_group_id` (`string (uuid)`, optional): The fit group (garments row) this version belongs to (absent until grouped). - `has_more` (`boolean`, required) - `next_cursor` (`string`, optional) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/products//garments \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.products.garments.list()`](https://docs.tryyoursize.com/reference/node/products-garments-list.md) - Python: [`tys.products.garments.list()`](https://docs.tryyoursize.com/reference/python/products-garments-list.md) --- # List the store's products ```http GET /v1/products ``` Source: https://docs.tryyoursize.com/api/reference/operations/listcatalogproducts/ **Key scopes:** `products:read`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. Newest first, cursor-paginated. `sku` finds one product by its SKU / style ID. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `sku` (query, `string`, optional): Only the product with this SKU / style ID. - `limit` (query, `integer`, optional): Page size (list convention). Default 20, at most 100. - `cursor` (query, `string`, optional): Opaque cursor from the previous page's `next_cursor` (list convention). Omit it for the first page. ## Responses ### 200 OK (`ProductPage`) - `data` (`Product[]`, required) - `id` (`string (uuid)`, required) - `external_id` (`string`, required): The brand's SKU or style ID (unique per brand). - `name` (`string`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `published_version` (`integer`, optional): Garment version shoppers are fitted against, if any. - `draft_version` (`integer`, optional): Garment version being edited, if any. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `fit_group_id` (`string (uuid)`, optional): The fit group whose garment this product uses (absent until grouped). - `brand_id` (`string (uuid)`, optional): The brand the product belongs to; every store of the brand sees it. - `home_store_id` (`string (uuid)`, optional): The store the product was created in. - `retired_at` (`string (date-time)`, optional): Set when the product is retired; shoppers no longer get a size for it. - `available` (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). - `has_more` (`boolean`, required) - `next_cursor` (`string`, optional) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ## Example ```bash curl -X GET https://api.tryyoursize.com/v1/products \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.products.list()`](https://docs.tryyoursize.com/reference/node/products-list.md) - Python: [`tys.products.list()`](https://docs.tryyoursize.com/reference/python/products-list.md) --- # Publish a complete draft ```http POST /v1/garments/{garment_id}/publish ``` Source: https://docs.tryyoursize.com/api/reference/operations/publishcataloggarment/ **Key scopes:** `garments:publish`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. The version shoppers are fitted against from now on, for every product of its fit group. The same checks as Studio: every required point of measure for every size, fabric, the fit approval when the store requires it, every shop size mapped, and the plan's styles (402). Test answers drafts, live answers published versions only. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `garment_id` (path, `string (uuid)`, required): A garment *version* (garment_versions.id). A fit group (garments table) is `fit_group_id`. - `Idempotency-Key` (header, `string`, optional): A unique value per logical request (a UUID v4 is ideal), 1-255 printable ASCII characters. With a secret or restricted key, the first response is stored for 24 hours per store and mode: a retry with the same key and the same body gets the stored response back (header `Idempotent-Replayed: true`); the same key with a different body is `422 idempotency_key_reused`; a retry while the first request still runs is `409 idempotency_key_in_use`. Responses with a 5xx status are not stored, so a retry runs again. ## Responses ### 200 Published (`GarmentVersion`) - `id` (`string (uuid)`, required) - `product_id` (`string (uuid)`, required) - `version` (`integer`, required) - `status` (`"draft" | "published" | "retired"`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `fit_intent` (`"skinny" | "slim" | "regular" | "relaxed" | "oversized"`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - `sizes` (`string[]`, required) - `poms` (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - `grading` (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - `base_size` (`string`, required) - `increments_mm` (`object`, required) - `fabric` (`Fabric`, required): What the tech pack / mill sheet says. - `name` (`string`, optional) - `construction` (`"jersey" | "rib" | "interlock" | "fleece" | "pique" | "plain_weave" | "twill" | "denim" | "satin" | "other"`, optional) - `composition` (`FiberShare[]`, required) - `fiber` (`"cotton" | "polyester" | "elastane" | "viscose" | "modal" | "lyocell" | "wool" | "linen" | "polyamide" | "silk" | "acrylic" | "other"`, required) - `pct` (`number (double)`, required) - `weight_gsm` (`number (double)`, optional): Areal density, g/m². - `stretch_warp_pct` (`number (double)`, optional): Stretch along the warp (lengthwise), percent. - `stretch_weft_pct` (`number (double)`, optional): Stretch along the weft (crosswise), percent. - `notes` (`string`, optional) - `import_id` (`string (uuid)`, optional) - `issues` (`GarmentIssue[]`, required): What still blocks publishing (errors) or deserves a look (warnings). - `severity` (`"error" | "warning"`, required) - `code` (`string`, required): e.g. out_of_range, not_graded, missing_pom, composition_total - `field` (`string`, required): category | fit_intent | sizes | poms | grading | import | fabric. - `size` (`string`, optional) - `pom` (`string`, optional) - `params` (`IssueParams`, optional): The values in `message`, for clients that word the finding in their own language (keys depend on `code`, e.g. value_mm, min_mm, max_mm, prev_size, fiber, max). Numbers are plain decimals ("." separator); lengths in mm. - `message` (`string`, required): English text; clients in other languages can word it from code, size, pom and params. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `published_at` (`string (date-time)`, optional) - `season` (`string`, optional) - `tolerances_mm` (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - `spec_source` (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - `kind` (`"manual" | "paste" | "xlsx" | "csv" | "pdf" | "workbook" | "clo" | "browzwear"`, required) - `filename` (`string`, optional, max length 255) - `fit_group_id` (`string (uuid)`, optional): The fit group (garments row) this version belongs to (absent until grouped). ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ### 409 Conflicts with the current state (duplicate SKU, editing a published version) (`Error`) ### 422 Values to fix (each issue points at the field, size and POM) (`GarmentRejected`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/garments//publish \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" ``` ## In the SDKs - Node.js: [`tys.garments.publish()`](https://docs.tryyoursize.com/reference/node/garments-publish.md) - Python: [`tys.garments.publish()`](https://docs.tryyoursize.com/reference/python/garments-publish.md) --- # Count a shopper funnel step (measuring window) ```http POST /v1/funnel_events ``` Source: https://docs.tryyoursize.com/api/reference/operations/recordfunnelevent/ **Key:** publishable (`pk_test_…` / `pk_live_…`), from an origin registered for the key. The measuring window reports the one funnel step no other request shows: the shopper chose a size ("Use this size"). The Platform counts it server-side, cookieless and aggregated: no identifier is sent or stored, no person profile is made; the event carries only the store, the product category, the language, the device class and the surface. The product is used to find its category and is never stored. Always 204 (also when analytics are off). ## Authentication Publishable key (`pk_test_…` / `pk_live_…`), from an origin registered for the key. ## Request body `application/json`: `FunnelEvent` A shopper funnel step reported by the measuring window. No identifier, body data or size: only which step. - `event` (`"size_used"`, required) - `product` (`string`, optional, max length 255): The product (SKU) the size is for; only used to find its category, never stored. ## Responses ### 204 Counted (or dropped when analytics are off) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/funnel_events \ -H "Authorization: Bearer pk_test_…" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` --- # Replace a product's size map ```http PUT /v1/products/{product}/size_map ``` Source: https://docs.tryyoursize.com/api/reference/operations/savecatalogsizemap/ **Key scopes:** `products:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. Recommendations are returned in your labels through this map ("M" → "Medium"). Publishing needs every shop size mapped. ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `product` (path, `string`, required): The product: its TryYourSize ID (UUID) or the brand's SKU / style ID (`external_id`). An ID is looked up first. ## Request body `application/json`: `ProductSizeMap` Studio size label → the shop's variant option value ("M" → "Medium"). Must cover every variant to publish. - `entries` (`object[]`, required) - `studio_size` (`string`, required) - `shop_value` (`string`, required) - `unmapped_shop_values` (`string[]`, optional): Variant size values of the product with no Studio size yet (read only). ## Responses ### 200 OK (`ProductSizeMap`) - `entries` (`object[]`, required) - `studio_size` (`string`, required) - `shop_value` (`string`, required) - `unmapped_shop_values` (`string[]`, optional): Variant size values of the product with no Studio size yet (read only). ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ### 422 Values to fix (each issue points at the field, size and POM) (`GarmentRejected`) ## Example ```bash curl -X PUT https://api.tryyoursize.com/v1/products//size_map \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.products.sizeMap.save()`](https://docs.tryyoursize.com/reference/node/products-size-map-save.md) - Python: [`tys.products.size_map.save()`](https://docs.tryyoursize.com/reference/python/products-size-map-save.md) --- # See a product on the shopper's body ```http POST /v1/tryon ``` Source: https://docs.tryyoursize.com/api/reference/operations/starttryon/ **Key:** publishable (`pk_test_…` / `pk_live_…`), from an origin registered for the key. Shows every size of the product's published garment on the body and recommends a size (from a few seconds to about 2 minutes). A body already shown with this garment version answers 200 at once; otherwise 202 and a job to poll. Every answer carries a new `viewer_token`, the only way (with the shopper's session) to read the job. With a shopper session (`Authorization: Bearer tys_sess_…`) the try-on is linked to the shopper and, in live mode, delivering a done result records the store connection and the size the store receives. ## Authentication Publishable key (`pk_test_…` / `pk_live_…`), from an origin registered for the key. ## Parameters - `view` (query, `"full" | "summary"`, optional): `full` (default) answers with the whole `result` (meshes for every size, MBs). `summary` leaves `result` out and answers `summary` instead (the recommended size and the sizes that can be worn, a few bytes): for the size-guide button and the store's recommended-size event, which never need the meshes. - `start` (query, `boolean`, optional): `false` answers only with an existing result and never queues a simulation: a done or running try-on of this body, or 404 ("not simulated yet"). With `view=summary`, a miss may answer 200 with a size estimate instead (`status: done`, empty `id`, `summary.method: estimate`). Page views (the size-guide and inline elements) use it; a full simulation starts only on an explicit shopper action ("See it on you"). ## Request body `application/json`: `TryOnRequest` The product and the shopper's body as on screen: the same body as `/v1/bodies/preview`, with the preview's `body_state` (required; opaque). - `product` (`string`, required, max length 120): The product's SKU / style ID in the store (what the shop passes to the element). - `basics` (`BodyBasics`, required) - `sex` (`"female" | "male"`, required) - `age_years` (`integer`, required) - `height_mm` (`number (double)`, required) - `weight_kg` (`number (double)`, required) - `measurements` (`Measurement[]`, optional, max 64 items) - `code` (`"neck_girth" | "shoulder_width" | "arm_length" | "armscye_girth" | "upper_arm_girth" | "wrist_girth" | "chest_girth" | "underbust_girth" | "bust_point_to_bust_point" | "shoulder_to_bust_point" | "back_length" | "waist_girth" | "front_waist_arc" | "high_hip_girth" | "abdomen_girth" | "hip_girth" | "front_hip_arc" | "thigh_girth" | "knee_girth" | "calf_girth" | "ankle_girth" | "inseam" | "outseam" | "crotch_length"`, required): Body measurement from set v1. - `value_mm` (`number (double)`, required): Measured value in millimetres. - `source` (`"tape_self" | "tape_helper" | "flat_garment" | "estimated"`, required) - `body_state` (`number (double)[]`, required, max 64 items): The `body_state` of the body preview on screen. ## Responses ### 200 Done (an existing result) (`TryOnJob`) - `id` (`string`, required): The try-on's id (uuid). Empty only for a size estimate (POST ?start=false&view=summary answered with no try-on of this body); there is nothing to poll. - `viewer_token` (`string`, optional): Only in the answer to POST /v1/tryon: the window's proof that it may read this try-on. Send it as `Authorization: Bearer tys_view_…` to GET /v1/tryon/{tryon_id}. Stored hashed; never sent again. - `status` (`"queued" | "running" | "done" | "failed"`, required) - `progress` (`object`, required) - `sizes_done` (`integer`, required) - `sizes_total` (`integer`, required) - `result` (`object`, optional): Present when `status` is `done`, and while it runs: the sizes computed so far, each with `method` (simulated) and `state` (preview: shown early, may still change; settled; refining: still being computed, no mesh yet; simulated). The try-on result for the window: body mesh and body_state (no basics, no tape measurements), garment with its fabric and estimated POMs, recommended size and per-size meshes, pressure kPa, ease mm and zones. Binary arrays are base64 little-endian; per-vertex values are rounded to display precision. - `summary` (`object`, optional): Present when `status` is `done` and the request asked for `view=summary`: what a store may receive (sizes only, never measurements). Size labels are the garment's (Studio) sizes; map them with `shop_sizes` from GET /v1/products/{product}/tryon. - `recommended_size` (`string`, optional): The recommended size; absent when no size can be worn. - `no_size_fits` (`boolean`, required) - `wearable_sizes` (`string[]`, required): Sizes that can be worn on this body (judged on this body), smallest to largest. - `method` (`"estimate" | "simulated"`, optional): How the size was determined: `estimate` (a size estimate, no try-on of this body yet) or `simulated` (from the try-on of this body). A coarse informational label: do not build logic on its values. - `recommendation` (`object`, optional): The size answer with its method. On the first answer of a try-on it may already be there (`method: estimate`) while the 3D view is computed; once done it is the try-on's own size. An estimate is final (`ready` or `none`) only when it is certain for this body; otherwise the status is `computing`, no size is named, and the try-on's 3D view gives it. - `recommended_size` (`string`, optional) - `method` (`"estimate" | "simulated"`, required): Same as `summary.method`. A coarse informational label; do not build logic on its values. - `status` (`"ready" | "none" | "computing" | "unavailable"`, required) - `wearable_sizes` (`string[]`, optional) - `error` (`object`, optional): Present when `status` is `failed`. - `code` (`string`, required): INVALID_BODY, UNSUPPORTED_CATEGORY, MISSING_POM, INVALID_POM, INVALID_FABRIC, NO_SIZES, UNAVAILABLE or ENGINE_ERROR. - `message` (`string`, required) ### 202 Queued or running; poll GET /v1/tryon/{tryon_id} (`TryOnJob`) - `id` (`string`, required): The try-on's id (uuid). Empty only for a size estimate (POST ?start=false&view=summary answered with no try-on of this body); there is nothing to poll. - `viewer_token` (`string`, optional): Only in the answer to POST /v1/tryon: the window's proof that it may read this try-on. Send it as `Authorization: Bearer tys_view_…` to GET /v1/tryon/{tryon_id}. Stored hashed; never sent again. - `status` (`"queued" | "running" | "done" | "failed"`, required) - `progress` (`object`, required) - `sizes_done` (`integer`, required) - `sizes_total` (`integer`, required) - `result` (`object`, optional): Present when `status` is `done`, and while it runs: the sizes computed so far, each with `method` (simulated) and `state` (preview: shown early, may still change; settled; refining: still being computed, no mesh yet; simulated). The try-on result for the window: body mesh and body_state (no basics, no tape measurements), garment with its fabric and estimated POMs, recommended size and per-size meshes, pressure kPa, ease mm and zones. Binary arrays are base64 little-endian; per-vertex values are rounded to display precision. - `summary` (`object`, optional): Present when `status` is `done` and the request asked for `view=summary`: what a store may receive (sizes only, never measurements). Size labels are the garment's (Studio) sizes; map them with `shop_sizes` from GET /v1/products/{product}/tryon. - `recommended_size` (`string`, optional): The recommended size; absent when no size can be worn. - `no_size_fits` (`boolean`, required) - `wearable_sizes` (`string[]`, required): Sizes that can be worn on this body (judged on this body), smallest to largest. - `method` (`"estimate" | "simulated"`, optional): How the size was determined: `estimate` (a size estimate, no try-on of this body yet) or `simulated` (from the try-on of this body). A coarse informational label: do not build logic on its values. - `recommendation` (`object`, optional): The size answer with its method. On the first answer of a try-on it may already be there (`method: estimate`) while the 3D view is computed; once done it is the try-on's own size. An estimate is final (`ready` or `none`) only when it is certain for this body; otherwise the status is `computing`, no size is named, and the try-on's 3D view gives it. - `recommended_size` (`string`, optional) - `method` (`"estimate" | "simulated"`, required): Same as `summary.method`. A coarse informational label; do not build logic on its values. - `status` (`"ready" | "none" | "computing" | "unavailable"`, required) - `wearable_sizes` (`string[]`, optional) - `error` (`object`, optional): Present when `status` is `failed`. - `code` (`string`, required): INVALID_BODY, UNSUPPORTED_CATEGORY, MISSING_POM, INVALID_POM, INVALID_FABRIC, NO_SIZES, UNAVAILABLE or ENGINE_ERROR. - `message` (`string`, required) ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 No published garment for the product, or (start=false) this body was not simulated yet (`Error`) ### 422 The content is rejected (e.g. a body that cannot be rebuilt) (`Error`) ### 503 A dependency is temporarily unavailable; retry later (`Error`) ## Example ```bash curl -X POST https://api.tryyoursize.com/v1/tryon \ -H "Authorization: Bearer pk_test_…" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` --- # Audit Source: https://docs.tryyoursize.com/api/reference/operations/tags/audit/ Your organization's audit log, read with a restricted key holding `audit:read`. - [Audit log of the key's organization (server API)](https://docs.tryyoursize.com/api/reference/operations/listauditeventswithkey.md): `GET /v1/audit_events` --- # Catalog API Source: https://docs.tryyoursize.com/api/reference/operations/tags/catalog-api/ Products, size maps, fit groups and garment drafts, with a secret key or a restricted key holding the listed scopes. The catalog is shared by test and live mode: test answers drafts, live answers published versions. - [List the store's products](https://docs.tryyoursize.com/api/reference/operations/listcatalogproducts.md): `GET /v1/products` - [Create a product](https://docs.tryyoursize.com/api/reference/operations/createcatalogproduct.md): `POST /v1/products` - [A product](https://docs.tryyoursize.com/api/reference/operations/getcatalogproduct.md): `GET /v1/products/{product}` - [Update a product](https://docs.tryyoursize.com/api/reference/operations/updatecatalogproduct.md): `PATCH /v1/products/{product}` - [A product's size map (Studio size → your size label)](https://docs.tryyoursize.com/api/reference/operations/getcatalogsizemap.md): `GET /v1/products/{product}/size_map` - [Replace a product's size map](https://docs.tryyoursize.com/api/reference/operations/savecatalogsizemap.md): `PUT /v1/products/{product}/size_map` - [Garment versions of a product, newest first](https://docs.tryyoursize.com/api/reference/operations/listcataloggarments.md): `GET /v1/products/{product}/garments` - [Start a new draft garment version](https://docs.tryyoursize.com/api/reference/operations/createcataloggarmentdraft.md): `POST /v1/products/{product}/garments` - [A garment version](https://docs.tryyoursize.com/api/reference/operations/getcataloggarment.md): `GET /v1/garments/{garment_id}` - [Replace a draft's content](https://docs.tryyoursize.com/api/reference/operations/updatecataloggarmentdraft.md): `PUT /v1/garments/{garment_id}` - [Publish a complete draft](https://docs.tryyoursize.com/api/reference/operations/publishcataloggarment.md): `POST /v1/garments/{garment_id}/publish` - [List fit groups](https://docs.tryyoursize.com/api/reference/operations/listcatalogfitgroups.md): `GET /v1/fit_groups` - [Create a fit group](https://docs.tryyoursize.com/api/reference/operations/createcatalogfitgroup.md): `POST /v1/fit_groups` - [A fit group](https://docs.tryyoursize.com/api/reference/operations/getcatalogfitgroup.md): `GET /v1/fit_groups/{fit_group_id}` - [Move products into a fit group](https://docs.tryyoursize.com/api/reference/operations/linkcatalogfitgroupproducts.md): `POST /v1/fit_groups/{fit_group_id}/products` --- # Embed Source: https://docs.tryyoursize.com/api/reference/operations/tags/embed/ Called by TryYourSize.js and the measuring window from your page (publishable key). You do not call these yourself; they are listed so you can read your request logs and CSP reports. - [Bootstrap the measurement window](https://docs.tryyoursize.com/api/reference/operations/initembed.md): `POST /v1/embed/init` - [Count a shopper funnel step (measuring window)](https://docs.tryyoursize.com/api/reference/operations/recordfunnelevent.md): `POST /v1/funnel_events` --- # Outcomes Source: https://docs.tryyoursize.com/api/reference/operations/tags/outcomes/ Orders and returns from your server, for the A/B results and size-related return rates. No customer data is accepted. - [Record an order (idempotent by order ID)](https://docs.tryyoursize.com/api/reference/operations/createoutcomeorder.md): `POST /v1/orders` - [Record a return of an order line (idempotent by return ID and line)](https://docs.tryyoursize.com/api/reference/operations/createoutcomereturn.md): `POST /v1/returns` --- # Recommendations Source: https://docs.tryyoursize.com/api/reference/operations/tags/recommendations/ The shopper's recommended size for your server or a headless storefront, from a handle the browser SDK issues. Sizes only, never measurements. - [Seal the signed-in shopper's body into a handle for the brand's server](https://docs.tryyoursize.com/api/reference/operations/createrecommendationhandle.md): `POST /v1/recommendation_handles` - [The shopper's recommended size for up to 25 products](https://docs.tryyoursize.com/api/reference/operations/createrecommendations.md): `POST /v1/recommendations` --- # TryOn Source: https://docs.tryyoursize.com/api/reference/operations/tags/tryon/ The try-on window's routes (publishable key, from our window's origin). Shown for reference. - [Is "See it on you" available for a product?](https://docs.tryyoursize.com/api/reference/operations/getproducttryon.md): `GET /v1/products/{product}/tryon` - [See a product on the shopper's body](https://docs.tryyoursize.com/api/reference/operations/starttryon.md): `POST /v1/tryon` - [A try-on's status, progress and result](https://docs.tryyoursize.com/api/reference/operations/gettryon.md): `GET /v1/tryon/{tryon_id}` - [The colourways the window draws on the simulated garment](https://docs.tryyoursize.com/api/reference/operations/getpublicproductappearance.md): `GET /v1/products/{product}/appearance` --- # Replace a draft's content ```http PUT /v1/garments/{garment_id} ``` Source: https://docs.tryyoursize.com/api/reference/operations/updatecataloggarmentdraft/ **Key scopes:** `garments:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. Drafts may be incomplete but never hold impossible values. Published versions never change (409). ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `garment_id` (path, `string (uuid)`, required): A garment *version* (garment_versions.id). A fit group (garments table) is `fit_group_id`. ## Request body `application/json`: `GarmentInput` - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `fit_intent` (`"skinny" | "slim" | "regular" | "relaxed" | "oversized"`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - `sizes` (`string[]`, required): Size labels, smallest to largest. - `poms` (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - `grading` (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - `base_size` (`string`, required) - `increments_mm` (`object`, required) - `fabric` (`Fabric`, required): What the tech pack / mill sheet says. - `name` (`string`, optional) - `construction` (`"jersey" | "rib" | "interlock" | "fleece" | "pique" | "plain_weave" | "twill" | "denim" | "satin" | "other"`, optional) - `composition` (`FiberShare[]`, required) - `fiber` (`"cotton" | "polyester" | "elastane" | "viscose" | "modal" | "lyocell" | "wool" | "linen" | "polyamide" | "silk" | "acrylic" | "other"`, required) - `pct` (`number (double)`, required) - `weight_gsm` (`number (double)`, optional): Areal density, g/m². - `stretch_warp_pct` (`number (double)`, optional): Stretch along the warp (lengthwise), percent. - `stretch_weft_pct` (`number (double)`, optional): Stretch along the weft (crosswise), percent. - `notes` (`string`, optional) - `import_id` (`string (uuid)`, optional): Pattern import linked to this version (omit to unlink). - `season` (`string`, optional, max length 40): Season tag, e.g. "SS27". - `tolerances_mm` (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - `spec_source` (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - `kind` (`"manual" | "paste" | "xlsx" | "csv" | "pdf" | "workbook" | "clo" | "browzwear"`, required) - `filename` (`string`, optional, max length 255) ## Responses ### 200 OK (`GarmentVersion`) - `id` (`string (uuid)`, required) - `product_id` (`string (uuid)`, required) - `version` (`integer`, required) - `status` (`"draft" | "published" | "retired"`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `fit_intent` (`"skinny" | "slim" | "regular" | "relaxed" | "oversized"`, required): The designer's intended silhouette; sets the ease targets the fit judgement compares against. - `sizes` (`string[]`, required) - `poms` (`POMTable`, required): Garment measurements per size: size label → POM code → millimetres. Widths are flat half-measurements. See GET /v1/studio/garment_dictionary for codes and plausible ranges. - `grading` (`GradingRule`, optional): How the POM table was derived: the base size's values plus, per POM, the increment (mm) between each pair of consecutive sizes (len = number of sizes - 1). The table stays the source of truth. - `base_size` (`string`, required) - `increments_mm` (`object`, required) - `fabric` (`Fabric`, required): What the tech pack / mill sheet says. - `name` (`string`, optional) - `construction` (`"jersey" | "rib" | "interlock" | "fleece" | "pique" | "plain_weave" | "twill" | "denim" | "satin" | "other"`, optional) - `composition` (`FiberShare[]`, required) - `fiber` (`"cotton" | "polyester" | "elastane" | "viscose" | "modal" | "lyocell" | "wool" | "linen" | "polyamide" | "silk" | "acrylic" | "other"`, required) - `pct` (`number (double)`, required) - `weight_gsm` (`number (double)`, optional): Areal density, g/m². - `stretch_warp_pct` (`number (double)`, optional): Stretch along the warp (lengthwise), percent. - `stretch_weft_pct` (`number (double)`, optional): Stretch along the weft (crosswise), percent. - `notes` (`string`, optional) - `import_id` (`string (uuid)`, optional) - `issues` (`GarmentIssue[]`, required): What still blocks publishing (errors) or deserves a look (warnings). - `severity` (`"error" | "warning"`, required) - `code` (`string`, required): e.g. out_of_range, not_graded, missing_pom, composition_total - `field` (`string`, required): category | fit_intent | sizes | poms | grading | import | fabric. - `size` (`string`, optional) - `pom` (`string`, optional) - `params` (`IssueParams`, optional): The values in `message`, for clients that word the finding in their own language (keys depend on `code`, e.g. value_mm, min_mm, max_mm, prev_size, fiber, max). Numbers are plain decimals ("." separator); lengths in mm. - `message` (`string`, required): English text; clients in other languages can word it from code, size, pom and params. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `published_at` (`string (date-time)`, optional) - `season` (`string`, optional) - `tolerances_mm` (`TolerancesMM`, optional): Spec-sheet tolerance per POM code, ± mm (e.g. {"chest_width": 10}). Optional; the flat preview uses ±5 mm when a POM has none. - `spec_source` (`SpecSource`, optional): Where the POM table came from, kept for audit. Set by Studio's paste/import. - `kind` (`"manual" | "paste" | "xlsx" | "csv" | "pdf" | "workbook" | "clo" | "browzwear"`, required) - `filename` (`string`, optional, max length 255) - `fit_group_id` (`string (uuid)`, optional): The fit group (garments row) this version belongs to (absent until grouped). ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ### 409 Conflicts with the current state (duplicate SKU, editing a published version) (`Error`) ### 422 Values to fix (each issue points at the field, size and POM) (`GarmentRejected`) ## Example ```bash curl -X PUT https://api.tryyoursize.com/v1/garments/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.garments.updateDraft()`](https://docs.tryyoursize.com/reference/node/garments-update-draft.md) - Python: [`tys.garments.update_draft()`](https://docs.tryyoursize.com/reference/python/garments-update-draft.md) --- # Update a product ```http PATCH /v1/products/{product} ``` Source: https://docs.tryyoursize.com/api/reference/operations/updatecatalogproduct/ **Key scopes:** `products:write`. A secret key (`sk_`) has every scope; a restricted key (`rk_`) needs the scopes listed. Replaces the SKU, name and category (all three are required, like Studio). ## Authentication Secret key (`sk_…`) or restricted key (`rk_…`) from your server: `Authorization: Bearer sk_…`. ## Parameters - `product` (path, `string`, required): The product: its TryYourSize ID (UUID) or the brand's SKU / style ID (`external_id`). An ID is looked up first. ## Request body `application/json`: `ProductInput` - `external_id` (`string`, required) - `name` (`string`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. ## Responses ### 200 OK (`Product`) - `id` (`string (uuid)`, required) - `external_id` (`string`, required): The brand's SKU or style ID (unique per brand). - `name` (`string`, required) - `category` (`"tshirt" | "shirt" | "sweater" | "jacket" | "dress" | "skirt" | "jeans" | "trousers" | "shorts" | "leggings" | "polo" | "hoodie"`, required): The garment type; it sets which POMs are needed. - `published_version` (`integer`, optional): Garment version shoppers are fitted against, if any. - `draft_version` (`integer`, optional): Garment version being edited, if any. - `created_at` (`string (date-time)`, required) - `updated_at` (`string (date-time)`, required) - `fit_group_id` (`string (uuid)`, optional): The fit group whose garment this product uses (absent until grouped). - `brand_id` (`string (uuid)`, optional): The brand the product belongs to; every store of the brand sees it. - `home_store_id` (`string (uuid)`, optional): The store the product was created in. - `retired_at` (`string (date-time)`, optional): Set when the product is retired; shoppers no longer get a size for it. - `available` (`boolean`, optional): Whether the store it was read through offers it (false in the brand list, see `stores`). ### 400 Invalid request (`Error`) ### 401 Missing or invalid credentials (`Error`) ### 403 Not allowed (`Error`) ### 404 Not found (`Error`) ### 409 Conflicts with the current state (duplicate SKU, editing a published version) (`Error`) ## Example ```bash curl -X PATCH https://api.tryyoursize.com/v1/products/ \ -H "Authorization: Bearer $TRYYOURSIZE_SECRET_KEY" \ -H "TYS-Version: 2026-10-25" \ -H "Content-Type: application/json" \ -d @body.json ``` ## In the SDKs - Node.js: [`tys.products.update()`](https://docs.tryyoursize.com/reference/node/products-update.md) - Python: [`tys.products.update()`](https://docs.tryyoursize.com/reference/python/products-update.md) --- # API changelog > Dated versions of the TryYourSize server API (TYS-Version header), newest first. Source: https://docs.tryyoursize.com/api/changelog/ Pin a version with the `TYS-Version` header. Latest version: `2026-10-25`. ## 2026-10-25 Released 2026-10-25. First dated version of the server API. - **Added:** POST /v1/recommendations: the shopper's size for your server or headless storefront, from a handle the browser SDK issues (tys.recommendationHandle()). - **Added:** Catalog API with secret and restricted keys: /v1/products, /v1/products/{product}/size_map, /v1/products/{product}/garments, /v1/garments/{garment_id}(/publish), /v1/fit_groups. - **Added:** Idempotency-Key on POST requests made with secret and restricted keys (responses stored for 24 hours). - **Added:** Errors carry code, doc_url and request_id; every response carries X-Request-ID and TYS-Version. - **Added:** Cursor pagination for lists: limit, cursor → data, has_more, next_cursor. - **Note:** Orders and returns (POST /v1/orders, /v1/returns) and webhooks (envelope api_version "2026-10") are part of this version. --- # Errors > Every error code the TryYourSize API returns, with its HTTP status and what to do. Each error's doc_url links here. Source: https://docs.tryyoursize.com/api/errors/ Every error has the same shape: ```json { "error": "product not found", "status": 404, "code": "not_found", "doc_url": "https://docs.tryyoursize.com/api/errors/#not_found", "request_id": "0d6c1c5e-…" } ``` | Field | | |---|---| | `error` | A human-readable message. It can change: do not match on it. | | `status` | The HTTP status. | | `code` | Stable and machine-readable. Branch on this. | | `doc_url` | A link to the code's section on this page. | | `request_id` | Identifies the request, also in the `X-Request-ID` response header. Quote it when you contact support. You can send your own `X-Request-ID` to correlate with your logs. | The SDKs raise `TryYourSizeError` (Node: `code`, `docUrl`, `requestId`) and `tryyoursize.APIError` (Python: `code`, `doc_url`, `request_id`). Browser and mobile SDK errors have their own codes, listed in each [quickstart](https://docs.tryyoursize.com/quickstarts/web.md#errors). ## Codes | Code | Status | Retry? | |---|---|---| | [`invalid_request`](#invalid_request) | 400 | No, fix the request | | [`unauthorized`](#unauthorized) | 401 | No | | [`reauth_required`](#reauth_required) | 401 | After signing in again | | [`payment_required`](#payment_required) | 402 | No | | [`forbidden`](#forbidden) | 403 | No | | [`not_found`](#not_found) | 404 | No | | [`conflict`](#conflict) | 409 | No | | [`unprocessable`](#unprocessable) | 422 | No, fix the values | | [`rate_limited`](#rate_limited) | 429 | Yes, after `Retry-After` | | [`internal_error`](#internal_error) | 500 | Yes, with backoff | | [`not_implemented`](#not_implemented) | 501 | No | | [`unavailable`](#unavailable) | 503 | Yes, with backoff | | [`invalid_api_version`](#invalid_api_version) | 400 | No | | [`idempotency_key_in_use`](#idempotency_key_in_use) | 409 | Yes, same key | | [`idempotency_key_reused`](#idempotency_key_reused) | 422 | No, new key | | [`invalid_idempotency_key`](#invalid_idempotency_key) | 400 | No | | [`handle_expired`](#handle_expired) | 401 | With a new handle | | [`handle_invalid`](#handle_invalid) | 401 | No | | [`handle_mode_mismatch`](#handle_mode_mismatch) | 403 | No | ## invalid_request **400.** The request is malformed: invalid JSON, a missing or wrong-typed field, an unknown field (for example a customer `email` in an order), or a parameter out of range (`limit` above 100, more than 25 products). The message names the field. Fix the request; do not retry it unchanged. ## unauthorized **401.** No key, an unknown key, a revoked or expired key (a rolled key after its overlap), a key of a disabled store, or a publishable key used where a secret key is needed. Check the `Authorization: Bearer sk_…` header and the key's status in Studio → Developers → API keys. ## reauth_required **401.** A Studio action that needs a recent sign-in (for example creating, rolling or revoking a live secret key). Sign in again in Studio and repeat the action. Server API calls with keys never get this code. ## payment_required **402.** Your plan does not include this: for example publishing more styles than the plan allows, or another store on Starter. Upgrade in Studio → Settings → Billing, or retire a style. ## forbidden **403.** The key is valid but not allowed to do this: a restricted key without the operation's [key scope](https://docs.tryyoursize.com/get-started/keys-and-modes.md#key-scopes), or a publishable key used from an origin that is not registered for it. ## not_found **404.** The object does not exist **for this key's store** (a product ID or SKU, a garment, a fit group), or a return refers to an order you have not sent yet. Objects of other stores are always "not found". ## conflict **409.** The request conflicts with the current state: a SKU that already exists in the store, a second draft for a product, or a change to a published garment version (published versions never change). ## unprocessable **422.** The values are well-formed but cannot be accepted. For garments, the answer lists each issue with the field, size and point of measure to fix (for example an impossible measurement, a missing required point of measure, or a shop size with no Studio size in the size map when publishing). ## rate_limited **429.** Too many requests. Wait for the number of seconds in `Retry-After`, then retry with backoff. See [Rate limits](https://docs.tryyoursize.com/guides/rate-limits.md). ## internal_error **500.** Something failed on our side. Retry with exponential backoff (with the same `Idempotency-Key` for a `POST`). If it persists, contact [support](mailto:support@tryyoursize.com) with the `request_id`. ## not_implemented **501.** The endpoint exists in the specification but is not available yet. It is listed as coming soon in these docs. ## unavailable **503.** A dependency is temporarily unavailable, or a feature is switched off for your store (for example the Shopify order import before it is enabled). Retry later with backoff. ## invalid_api_version **400.** The `TYS-Version` header is not a known version. Send a date from the [changelog](https://docs.tryyoursize.com/api/changelog.md), for example `2026-10-25`, or omit the header to get the latest. See [Versioning](https://docs.tryyoursize.com/guides/versioning.md). ## idempotency_key_in_use **409.** A request with the same `Idempotency-Key` is still being processed. Wait a moment and retry with the same key: you will get the first request's response. See [Idempotency](https://docs.tryyoursize.com/guides/idempotency.md). ## idempotency_key_reused **422.** This `Idempotency-Key` was already used in the last 24 hours with a **different** request body. Use a new key for a new request. ## invalid_idempotency_key **400.** The `Idempotency-Key` is empty, longer than 255 characters or not printable ASCII. A UUID v4 is ideal. ## handle_expired **401.** The recommendation handle is older than one hour. Ask the browser for a new one with `tys.recommendationHandle()`. See [Server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md). ## handle_invalid **401.** The recommendation handle cannot be used: it is malformed or was tampered with, it was issued for another store, or the shopper has since deleted their TryYourSize account or disconnected your store. Do not retry; treat the shopper as not connected. ## handle_mode_mismatch **403.** The handle was issued in test mode and the key is live (or the reverse). Use a key of the same mode as the publishable key on the page.