Bodygram Scanner Platform Integration
Integrate the Bodygram Scanner Platform to capture raw body measurements using the Headless SDK.
Who this is for
This guide is for developers who want raw body measurements. Unlike the Body2Fit path which returns size labels, the Bodygram Scanner Platform returns the underlying measurement data directly. Like Body2Fit, you can get measurements using two photos together with stats, or from stats alone if you want to skip the camera.
Before you start
An organization ID
Your organization ID is your clientKey. It is safe to use in frontend code. You can find it in your Bodygram Scanner Platform account, or sign up for free if you do not have one yet.
An API key
Your API key is used server-side to generate short-lived scan tokens. The API key itself must never be exposed in client-side code or committed to source control — store it as an environment variable on your server. The scan tokens it generates are safe to pass to the client.
The API key must only ever be used on your server. If it reaches the browser your account is compromised.
The Headless SDK loaded
<!-- 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 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.
How it works
The Bodygram Scanner Platform integration requires a server component. The flow is:
- The user initiates a scan on the client
- The client requests a scan token from your server
- Your server uses the API key to generate a short-lived token and returns it to the client
- The client opens Scanflow, captures the photos, and calls the estimation API with the token
- The response contains the user's full set of body measurements
This separation ensures your API key never leaves the server.
Initialize the SDK
On the client, initialize with your organization ID:
const sdk = new BodygramSDK({
clientKey: 'org_...', // your organization ID — safe to use client-side
locale: 'en', // UI language for the scan page
});locale controls the language of the scan UI. See Supported Locales for the full list of accepted codes.
Generate a scan token (server-side)
To use the Bodygram Scanner Platform API, start by generating a scan token. Each token authorizes exactly one scan, has a limited lifetime, and is safe to share with the client once created.
Scan tokens are standard JWTs — you can decode and inspect them if needed.
Scopes
The scope array controls what the token bearer is allowed to do:
| Scope | Description |
|---|---|
api.platform.bodygram.com/scans:create | Allows a new scan to be created |
api.platform.bodygram.com/scans:read | Allows scan results to be read after scanning |
Both scopes are required for a complete scan flow.
Implementation
Token generation must run on your server to keep the API key private. Both the ES and UMD server builds are supported.
Download the build you need and place it in your project before importing:
| Build | URL |
|---|---|
| ES (prod) | https://headless.body2fit.bodygram.com/sdk.es.js |
| UMD (prod) | https://headless.body2fit.bodygram.com/sdk.umd.js |
| ES (beta) | https://headless.stg.body2fit.bodygram.cc/sdk.es.js |
| UMD (beta) | https://headless.stg.body2fit.bodygram.cc/sdk.umd.js |
ES build
import BodygramSDK from './sdk.es.js'; // path to your downloaded SDK
const sdk = new BodygramSDK({
clientKey: process.env.BODYGRAM_ORG_ID, // Organization ID (org_...)
});
export async function createScanToken() {
const response = await sdk.getBodygramScannerPlatformScanToken({
apiKey: process.env.BODYGRAM_API_KEY, // ← server only, never expose this
scope: [
'api.platform.bodygram.com/scans:create',
'api.platform.bodygram.com/scans:read',
],
});
return response.token;
}UMD build
require('./sdk.umd.js'); // path to your downloaded SDK
const sdk = new globalThis.BodygramSDK({
clientKey: process.env.BODYGRAM_ORG_ID, // Organization ID (org_...)
});
async function createScanToken() {
const response = await sdk.getBodygramScannerPlatformScanToken({
apiKey: process.env.BODYGRAM_API_KEY, // ← server only, never expose this
scope: [
'api.platform.bodygram.com/scans:create',
'api.platform.bodygram.com/scans:read',
],
});
return response.token;
}Return the token to your client through a protected API endpoint or server-rendered props — never hardcode it in client-side code.
Example route (Express)
// GET /api/scan-token
// Protect this route — only serve the token to authenticated or authorized users.
app.get('/api/scan-token', requireAuth, async (req, res) => {
const token = await createScanToken();
res.json({ token });
});On the client, fetch the token before starting the scan:
const { token } = await fetch('/api/scan-token').then(r => r.json());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.
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 method below.
Get body measurements
Once the client has a scan token, it can request body measurements. There are two paths — choose based on whether you have photos available.
Stats only
Implementation
const response = await sdk.getBodygramScannerPlatformStatsEstimation({
token, // scan token received from your server
age: 25, // years
gender: 'male', // 'male' | 'female'
height: 1800, // millimeters — 180 cm → 1800
weight: 70000, // grams — 70 kg → 70000
});Response shape
{
"entry": {
"id": "scan_abc123",
"status": "success",
"input": {
"statsEstimations": {
"age": 25,
"gender": "male",
"height": 1800,
"weight": 70000
}
},
"measurements": [
{ "name": "bustGirth", "unit": "mm", "value": 895 },
{ "name": "waistGirth", "unit": "mm", "value": 762 },
{ "name": "hipGirth", "unit": "mm", "value": 905 }
],
"bodyComposition": null,
"avatar": {
"data": "base64-encoded-avatar-data...",
"format": "obj",
"type": "highResolution"
},
"posture": null
}
}status is "success" or "failure" — always check this before reading other fields. measurements is an array of { name, unit, value } entries — name is the camelCase measurement key, unit is typically "mm", and value is an integer in that unit. See the PlatformMeasurement type for the full list of measurement names. Stats-only estimation still generates an avatar, but bodyComposition and posture are null.
Photo + stats (recommended)
Implementation
const response = await sdk.getBodygramScannerPlatformPhotoEstimation({
token, // scan token received from your server
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
});Response shape
{
"entry": {
"id": "scan_abc123",
"status": "success",
"input": {
"photoScan": {
"age": 25,
"gender": "male",
"height": 1800,
"weight": 70000
}
},
"measurements": [
{ "name": "bustGirth", "unit": "mm", "value": 895 },
{ "name": "waistGirth", "unit": "mm", "value": 762 },
{ "name": "hipGirth", "unit": "mm", "value": 905 }
],
"bodyComposition": {
"bodyFatPercentage": 18.5,
"skeletalMuscleMass": 32.1
},
"avatar": {
"data": "base64-encoded-avatar-data...",
"format": "glb",
"type": "UPPER_BODY"
},
"posture": {
"front": {},
"right": {}
}
}
}status is "success" or "failure" — always check this before reading other fields. measurements is an array of { name, unit, value } entries — see the PlatformMeasurement type for the full list of measurement names. For the structure of avatar and posture, see the API reference.
Error response
{
"entry": {
"id": "scan_abc123",
"status": "failure",
"measurements": null,
"bodyComposition": null,
"avatar": null
}
}The error object shape is not guaranteed — always handle errors defensively and do not rely on a specific structure.
Retrieving scans
All scan results are stored on Bodygram's servers and can be retrieved at any time using the scan ID returned in the estimation response.
Retrieve all scans
Coming soon — SDK support for listing all scans associated with your organization is under development.
Retrieve a scan by ID
Coming soon — SDK support for fetching a single scan by ID is under development.
Real world example (dashboard app)
This pattern keeps your API key on the server while the scan and estimation run on the client.
- The user clicks the scan button — the client requests a short-lived scan token from your server.
- The client opens Scanflow with that token and captures front and side photos.
- The client calls the estimation API directly using the token and the captured images.
Server
Place your credentials in a .env file at the project root and make sure it is listed in .gitignore — never commit it.
your-project/
├── .env ← credentials (never commit this)
├── app.js ← your server
└── sdk.es.js ← downloaded Bodygram Headless SDK.env
BODYGRAM_ORG_ID=org_... # your organization ID
BODYGRAM_API_KEY=YOUR_API_KEY # your API key — keep this secretapp.js
import 'dotenv/config'; // loads .env into process.env
import express from 'express';
import BodygramSDK from './sdk.es.js';
const app = express();
app.use(express.json());
const sdk = new BodygramSDK({ clientKey: process.env.BODYGRAM_ORG_ID });
// GET /api/scan-token
// Returns a short-lived scan token to the client.
// The API key is used here and never sent to the browser.
// Protect this route so only authenticated users can request a token.
app.get('/api/scan-token', requireAuth, async (req, res) => {
const { token } = await sdk.getBodygramScannerPlatformScanToken({
apiKey: process.env.BODYGRAM_API_KEY,
scope: [
'api.platform.bodygram.com/scans:create',
'api.platform.bodygram.com/scans:read',
],
});
res.json({ token });
});Client
<script src="https://headless.body2fit.bodygram.com/sdk.umd.js"></script>
<button id="startBtn">Start scan</button>
<script>
// Your organization ID — safe to use in client-side code.
const sdk = new BodygramSDK({ clientKey: 'org_...', locale: 'en' });
async function startScan() {
// Step 1 — Request a scan token from your server.
// The server generates it using the API key, which never reaches the client.
const { token } = await fetch('/api/scan-token').then(r => r.json());
// Step 2 — Open Scanflow and capture front and side photos.
const { front, side } = await sdk.scan({
silhouette: 'male',
welcomeModal: 'how-to-scan',
});
// Step 3 — Call the estimation API with the token and captured photos.
const result = await sdk.getBodygramScannerPlatformPhotoEstimation({
token,
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
});
console.log('Measurements:', result);
// To retrieve this scan later, use result.entry.id.
// SDK support for fetching scans by ID and listing all scans is coming soon.
}
document.getElementById('startBtn').addEventListener('click', startScan);
</script>