Body2Fit Integration
Integrate the Headless SDK to give shoppers accurate size recommendations using Bodygram's Body2Fit API.
Who this is for
This guide is for brands that have onboarded with Bodygram and want to build a custom size-recommendation experience — without using the pre-built Body2Fit widget.
With the Headless SDK you own the entire UI. The SDK handles the scanner iframe, photo capture events, measurement estimation, and size recommendation API calls. You decide how and where everything appears on your page.
Before you start
You need three things in place before writing any code.
A Bodygram client key
Your client key is issued by Bodygram during onboarding. It identifies your brand and authorizes API calls to the Body2Fit service.
The client key is safe to include in frontend code — it is not a secret. Do not confuse it with a platform API key, which must never be exposed client-side.
If you do not have a client key yet, contact contact@bodygram.com to start the onboarding process.
Garments uploaded to Bodygram
Size recommendations are garment-specific. Before the SDK can return a size, Bodygram needs your garment measurement data on file.
Your garments are uploaded during onboarding. Each garment is identified by two values you will use throughout the integration:
brandId— assigned by Bodygram and provided to you during onboarding.garmentSKU/garmentSku— the same SKU you shared with Bodygram when uploading your garments. The SDK is inconsistent with the casing across methods:getBody2FitIsProductSupportedandgetBody2FitSizeFittingusegarmentSKU(all-caps), whilegetBody2FitSizeRecommendationusesgarmentSku(lowercaseku).
If you need to add or update garments after onboarding, contact your Bodygram account manager.
The Headless SDK loaded
Load the SDK script in your page <head> before calling any SDK methods:
<!-- Prod (stable) -->
<script src="https://headless.body2fit.bodygram.com/sdk.umd.js"></script>
<!-- Beta (latest) -->
<script src="https://headless.stg.body2fit.bodygram.cc/sdk.umd.js"></script>The UMD build above defines BodygramSDK on window. An ES module build (sdk.es.js) is also available for bundler-based setups. For React, Next.js, Vue, and ES module usage see Loading the SDK.
Initialize the SDK
Create one instance per page with your client key:
const sdk = new BodygramSDK({
clientKey: 'org_1Ft28OBw925ToHy0FlaIwH',
locale: 'en', // optional — 'en' | 'ja' | 'ko' | 'fr' | 'es' | 'zh' | ...
});| Option | Type | Required | Description |
|---|---|---|---|
clientKey | string | Yes | Your Bodygram client key. Throws if empty or missing. |
locale | string | No | Language for the scanner UI. Throws if provided but unsupported. See all supported locales. |
container | string | HTMLElement | No | CSS selector or DOM element where the scanner iframe mounts. When omitted, the SDK creates a <div> and appends it to <body>. |
Check garment support
Not every garment in your catalogue may be set up in Bodygram. Before showing a "Find my size" button or triggering Scanflow, check whether the current garment is supported — and only render the experience if it is. The SDK returns true if the garment is supported, false otherwise.
const supported = await sdk.getBody2FitIsProductSupported({
brandId, // provided by Bodygram during onboarding
garmentSKU, // note: SKU is all-caps here — different from other SDK methods
});
if (supported) {
// show the scan button or size recommendation UI
}garmentSKU — note the capitalisation. This method uses garmentSKU (all-caps SKU), as does getBody2FitSizeFitting, while getBody2FitSizeRecommendation uses garmentSku (lowercase ku). This is an inconsistency in the SDK itself — double-check the casing when calling each method.
This call is lightweight and safe to make on every product page load.
Capture photos with Scanflow
The Headless SDK embeds Bodygram's camera scanner as an invisible iframe on your page. When a user is ready to scan, the iframe opens full-screen, guides them through capturing a front and side photo, and passes the images back to your code as base64-encoded strings.
You stay in control of when the scanner opens, how it's triggered, and what happens after the photos are captured.
Scanning is optional. If you prefer to skip photos, you can create an estimation token from the user's height, age, gender, and weight alone — no camera required. Jump to Stats estimation below.
For a full explanation of the scanner lifecycle, supported camera constraints, silhouette options, and event handling, see the Scanflow guide.
Call sdk.scan() to open the camera interface and wait for the user to complete both shots:
const { front, side } = await sdk.scan({
camera: {
facingMode: 'environment',
width: { ideal: 1080 },
height: { ideal: 1920 },
},
silhouette: 'male', // 'male' | 'female' — defaults to 'female'
welcomeModal: 'how-to-scan', // 'tap-to-start' | 'how-to-scan' | false
});
// front and side are base64-encoded jpeg strings
console.log(front); // 'data:image/jpeg;base64,...'
console.log(side); // 'data:image/jpeg;base64,...'Both values are base64-encoded jpegs ready to pass directly into the estimation methods below.
Get an estimation token
Before you can request a size recommendation, you need an estimation token. This token is generated by Bodygram's estimation engine and represents the user's body measurements — computed from two photos together with their height, age, gender, and weight, or from those stats alone if you want to skip scanning.
Once created, the token can be saved (in localStorage, a session store, or your backend) and reused on future visits. Returning users can skip the scan entirely.
Photo estimation
Pass the front and side images from sdk.scan() along with the user's basic stats. This is the most accurate method.
const estimation = await sdk.getBody2FitPhotoEstimation({
front, // base64 jpeg from sdk.scan()
side, // base64 jpeg from sdk.scan()
age: 25, // years
gender: 'male', // 'male' | 'female'
height: 1800, // millimeters — 180 cm → 1800
weight: 70000, // grams — 70 kg → 70000
});
const { token } = estimation.estimations.estimationToken;
const { measurements } = estimation.estimations;
localStorage.setItem('bg-estimation-token', token);Stats estimation
If the user skips scanning or you want to offer a no-camera path, use stats alone. The SDK sends height, age, gender, and weight to Bodygram's estimation engine and returns an equivalent token.
const estimation = await sdk.getBody2FitStatsEstimation({
age: 25, // years
gender: 'male', // 'male' | 'female'
height: 1800, // millimeters — 180 cm → 1800
weight: 70000, // grams — 70 kg → 70000
});
// The token encodes the user's estimated measurements.
// Save it to skip re-estimation on future visits.
const { token } = estimation.estimations.estimationToken;
// The measurements array contains individual body dimensions.
const { measurements } = estimation.estimations;Save token wherever makes sense for your app — localStorage, a React context, a server-side session:
localStorage.setItem('bg-estimation-token', token);Response shape
Both methods return the same structure. The only difference is input.inputType: "MANUAL_STATS" for stats estimation and "CAMERA_FLOW" for photo estimation.
Example success response:
{
"estimations": {
"estimationToken": {
"token": "BY1AGJaDM680SNg2bbAI_jj3dB1us..."
},
"measurements": [
{ "estimationType": "HEIGHT", "value": 1800, "unit": "MILLIMETERS" },
{ "estimationType": "WEIGHT", "value": 70000, "unit": "GRAMS" },
{ "estimationType": "BUST_GIRTH", "value": 898, "unit": "MILLIMETERS" },
{ "estimationType": "WAIST_GIRTH","value": 753, "unit": "MILLIMETERS" },
{ "estimationType": "HIP_GIRTH", "value": 934, "unit": "MILLIMETERS" }
],
"avatarData": null,
"input": {
"preferredSystemOfMeasurement": "METRIC",
"tightness": 0,
"inputType": "MANUAL_STATS"
}
}
}See all measurement types in the API reference.
Error response
When estimation fails, the error may look like:
{
"error": {
"code": "INTERNAL_ERROR",
"message": "A human-readable description of the error"
}
}The error object shape is not guaranteed — code, message, or the error wrapper itself may be missing or structured differently than shown. Always handle errors defensively and do not rely on a specific shape.
Pass token to the size recommendation API in the next step.
Get size recommendations
Size recommendations map a user's estimation token to one or more products. Pass the token from the previous step alongside the brandId and garmentSku for each product you want a recommendation for — you can request multiple products in a single call.
garmentSku — note the capitalisation. This method uses garmentSku (lowercase ku), while getBody2FitIsProductSupported and getBody2FitSizeFitting use garmentSKU (all-caps). This is an inconsistency in the SDK itself.
const recommendations = await sdk.getBody2FitSizeRecommendation({
estimationToken,
productInfo: [
{ brandId, garmentSku },
// add more products if needed
],
});The response is an array in the same order as productInfo. Each entry contains the recommended size and a goodFit flag indicating whether the recommendation is a strong match for the user's measurements.
Example success response:
[
{
"recommendation": {
"recommendedSize": "M",
"goodFit": true
},
"productInfo": {
"brandId": "bodygram-client",
"garmentSku": "body2fit-garment-1"
}
}
]Error response
{
"code": "INVALID_ESTIMATION_TOKEN", // or something else
"message": "A human-readable description of the error"
}Common causes include an expired or malformed token, an incorrect brandId, or a garmentSku that does not match what was uploaded to Bodygram — verify all three if you receive an error. The error object shape is not guaranteed and may differ from what is shown above — always handle errors defensively and do not rely on a specific shape.
Get size fitting
Size recommendations tell you which size to pick. Size fitting goes further — it tells you how each available size fits across specific body areas like the chest, waist, and hips. Use this when you want to show users a detailed breakdown: whether a size runs tight or loose at the bust, how the recommended size compares to going one size up, and so on.
garmentSKU — note the capitalisation. This method uses garmentSKU (all-caps), as does getBody2FitIsProductSupported, while getBody2FitSizeRecommendation uses garmentSku (lowercase ku). This is an inconsistency in the SDK itself.
const fitting = await sdk.getBody2FitSizeFitting({
estimationToken,
productInfo: [
{ brandId, garmentSKU },
],
});The response is an array in the same order as productInfo. Each entry contains a result with all available sizes for that garment, and for each size a list of fitting points — individual body measurements and their tightness value at that size.
Example success response:
[
{
"result": {
"estimationToken": { "token": "BY1AGJaDM680SNg2bbAI_..." },
"avatarType": "UPPER_BODY",
"sizes": [
{
"size": { "index": 2, "name": "M" },
"recommendationRank": 0,
"neutralRecommendationRank": 0,
"tightness": 0,
"fittingPoints": [
{ "fittingPoint": "BUST_GIRTH", "tightness": -1 },
{ "fittingPoint": "WAIST_GIRTH", "tightness": 0 },
{ "fittingPoint": "HIP_GIRTH", "tightness": 0 }
]
},
{
"size": { "index": 3, "name": "L" },
"recommendationRank": 1,
"neutralRecommendationRank": 1,
"tightness": 1,
"fittingPoints": [
{ "fittingPoint": "BUST_GIRTH", "tightness": 0 },
{ "fittingPoint": "WAIST_GIRTH", "tightness": 1 },
{ "fittingPoint": "HIP_GIRTH", "tightness": 2 }
]
}
],
"sizeGroups": []
},
"productInfo": {
"brandId": "bodygram-client",
"garmentSKU": "body2fit-garment-1"
}
}
]recommendationRank: 0 is Bodygram's top recommendation for this user — the best fit. Higher ranks (1, 2, ...) are the next best options if the user wants to explore a looser or tighter fit. tightness on each fitting point indicates how the garment fits at that body area — negative values mean looser, positive means tighter, 0 is a neutral fit.
Error response
Common causes include an expired or malformed token, an incorrect brandId, or a garmentSKU that does not match what was uploaded to Bodygram — verify all three if you receive an error. The error object shape is not guaranteed and may differ from what is shown — always handle errors defensively and do not rely on a specific structure.
{
"code": "INVALID_ESTIMATION_TOKEN", // or something else
"message": "A human-readable description of the error"
}Full example
A complete end-to-end integration in plain HTML. Copy this, replace the placeholder values, and open it in a browser.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Body2Fit Example</title>
<!-- Load the Bodygram Headless SDK -->
<script src="https://headless.body2fit.bodygram.com/sdk.umd.js"></script>
</head>
<body>
<button id="startBtn">Find my size</button>
<script>
// ─── Replace these with your actual values from Bodygram ───────────────
const CLIENT_KEY = 'YOUR_CLIENT_KEY'; // issued during onboarding
const BRAND_ID = 'YOUR_BRAND_ID'; // provided by Bodygram
const GARMENT_SKU = 'YOUR_GARMENT_SKU'; // the SKU you uploaded to Bodygram
// ───────────────────────────────────────────────────────────────────────
async function start() {
// Step 1 — Initialize the SDK with your client key.
// The SDK sets up the scanner iframe in the background.
const sdk = new BodygramSDK({
clientKey: CLIENT_KEY,
locale: 'en', // change to match your user's language
});
// Step 2 — Check whether this garment is supported before going further.
// Only show the size-finding experience when Bodygram has data for the product.
const supported = await sdk.getBody2FitIsProductSupported({
brandId: BRAND_ID,
garmentSKU: GARMENT_SKU, // note: SKU is all-caps here
});
if (!supported) {
console.log('This garment is not supported by Bodygram.');
return;
}
// Step 3 — Open Scanflow so the user can take a front and side photo.
// Both images come back as base64-encoded jpegs.
//
// Want to skip the camera entirely? Replace this block with:
// const estimation = await sdk.getBody2FitStatsEstimation({
// age: 25, gender: 'male', height: 1800, weight: 70000,
// });
const { front, side } = await sdk.scan({
silhouette: 'male', // 'male' | 'female'
welcomeModal: 'how-to-scan',
});
// Step 4 — Create an estimation token from the photos and user stats.
// The token represents the user's body measurements and is reusable —
// save it to skip the scan on future visits.
const estimation = await sdk.getBody2FitPhotoEstimation({
front,
side,
age: 25, // replace with actual user input
gender: 'male', // replace with actual user input
height: 1800, // millimeters — 180 cm → 1800
weight: 70000, // grams — 70 kg → 70000
});
const estimationToken = estimation.estimations.estimationToken.token;
// Optional: save the token so returning users skip the scan next time.
localStorage.setItem('bg-estimation-token', estimationToken);
// Step 5 — Get the size recommendation for the product.
// You can pass multiple products in a single call.
const recommendations = await sdk.getBody2FitSizeRecommendation({
estimationToken,
productInfo: [{ brandId: BRAND_ID, garmentSku: GARMENT_SKU }],
});
const { recommendedSize, goodFit } = recommendations[0].recommendation;
console.log(`Recommended size: ${recommendedSize} (good fit: ${goodFit})`);
}
document.getElementById('startBtn').onclick = start;
</script>
</body>
</html>Want to see the full flow in action? The Body2Fit Playground lets you run every step live — product support check, scan, estimation, and size recommendation — against Bodygram's demo garments.
