Bodygram

API Reference

Complete reference for all methods, configuration options, and types in the Bodygram Headless SDK.

Initialization

const sdk = new BodygramSDK({
  clientKey: 'YOUR_CLIENT_KEY',        // required
  locale: 'en',                        // required
  container: '#my-container',          // optional: CSS selector or DOM element
  scanShouldIncludeOverlay: true,      // optional: tap overlay for iOS audio fix
});
OptionTypeRequiredDescription
clientKeystringYesYour Bodygram client key.
localestringYesUI language for the scan page. See supported locales.
containerstring | HTMLElementNoCSS selector or DOM element where the scan UI embeds.
scanShouldIncludeOverlaybooleanNoWhen true, adds a tap overlay before the scan UI to prevent iOS audio issues.

Throws if clientKey or locale is missing.


scan(config?)

Opens the scan UI and returns base64-encoded front and side images.

const { front, side } = await sdk.scan(config);

Handles user cancellation and scan errors internally.

Config

type ScanConfig = {
  camera?: MediaTrackConstraints;
  silhouette?: 'male' | 'female';
  isDebuggerEnabled?: boolean;
  screensToInclude?: ScreenID[];
  welcomeModal?: 'tap-to-start' | 'how-to-scan' | false;
  isMirrored?: boolean;
  hasHeader?: boolean;
  hasActionsArea?: boolean;
};

All fields are optional.

OptionTypeDescription
cameraMediaTrackConstraintsConstraints passed to getUserMedia. Use to select front/rear camera, set resolution, etc.
silhouette"male" | "female"Overlay silhouette shown in the scanner to guide user positioning.
isDebuggerEnabledbooleanEnables the on-screen debug panel. Useful during development. Defaults to false.
screensToIncludeScreenID[]List of screens to include in the flow. Valid values: "scan", "how-to-scan", "all".
welcomeModal'tap-to-start' | 'how-to-scan' | falseControls the intro modal shown before the scanner opens. 'how-to-scan' shows positioning instructions before the camera opens. false skips the modal entirely. 'tap-to-start' shows a minimal tap prompt — use this on iOS when screensToInclude is set to ["scan"] only: without a prior screen, no user gesture has been registered, and iOS will block audio playback. This is not an issue on Android. Defaults to 'how-to-scan'.
isMirroredbooleanMirrors the camera preview horizontally. Defaults to true for front-facing cameras.
hasHeaderbooleanShows or hides the header bar on the scan screen only. Defaults to false.
hasActionsAreabooleanShows or hides the bottom actions area on the scan screen only. Defaults to false.

hasHeader and hasActionsArea apply to the scan screen only. The introduction screen (e.g. how-to-scan) always renders its own header and actions area. Configurable introduction screen layout is planned for a future release.

Camera width and height constraints depend on the device's camera capabilities. Not all devices support the same resolution ranges.

Examples

// Basic scan
const { front, side } = await sdk.scan();

// With camera constraints
const { front, side } = await sdk.scan({
  camera: {
    width: { ideal: 1920 },
    height: { ideal: 1080 },
    facingMode: 'user',
  },
});

// With silhouette overlay
const { front, side } = await sdk.scan({
  silhouette: 'female',
});

// With both options
const { front, side } = await sdk.scan({
  camera: { facingMode: 'user' },
  silhouette: 'male',
});

Display captured images

The front and side values are base64-encoded strings. You can set them directly as the src of an <img> tag:

<img id="front-image" />
<img id="side-image" />

<script>
  const { front, side } = await sdk.scan();
  document.getElementById('front-image').src = front;
  document.getElementById('side-image').src = side;
</script>

Returns

{ front: string; side: string }

Both values are base64-encoded strings (suitable as <img src> values directly).

Errors

The promise rejects if the user closes the scanner or the scan fails.

CodeDescription
SCAN_CLOSED_BY_USERThe user dismissed the scanner before completing the scan.
SCAN_FAILEDThe scan could not be completed due to an unexpected error.
try {
  const { front, side } = await sdk.scan(config);
} catch (err) {
  if (err.code === 'SCAN_CLOSED_BY_USER') {
    // user dismissed the scanner
  } else if (err.code === 'SCAN_FAILED') {
    // something went wrong during the scan
  }
}

getBody2FitIsProductSupported(params)

Checks whether Bodygram has garment data on file for a specific product. Call this on every product page load and only show the size-finding UI if it returns true.

When to use: Before rendering a "Find my size" button or triggering Scanflow.

const supported = await sdk.getBody2FitIsProductSupported({
  brandId,    // string — required
  garmentSKU, // string — required (note: all-caps SKU)
});

if (supported) {
  // safe to show the scan/size UI
}

garmentSKU — note the capitalisation. This method uses garmentSKU (all-caps), as does getBody2FitSizeFitting, while getBody2FitSizeRecommendation uses garmentSku (lowercase ku). This is an inconsistency in the SDK itself.

Parameters

ParameterTypeRequiredDescription
brandIdstringYesYour brand identifier assigned by Bodygram during onboarding.
garmentSKUstringYesThe product SKU to check. Note: all-caps SKU, matching getBody2FitSizeFitting but differing from getBody2FitSizeRecommendation.

Response

boolean

Returns true if Bodygram has sizing data for the garment, false otherwise. This call is lightweight and safe to make on every page load.


getBody2FitPhotoEstimation(params)

Estimates body measurements from front and side photos using computer vision. This is the most accurate estimation method.

When to use: For new users who can take photos, or when you need the highest measurement accuracy.

const result = await sdk.getBody2FitPhotoEstimation({
  front,   // base64 string — required
  side,    // base64 string — required
  age,     // number, > 0 — required
  gender,  // 'male' | 'female' — required
  height,  // millimeters, 500–2500 — required
  weight,  // grams, 10000–200000 — required
  // optional:
  tightness,
  systemOfMeasurement,
  avatarType,
});

Parameters

ParameterTypeRequiredDescription
frontstringYesBase64-encoded front photo (from sdk.scan()).
sidestringYesBase64-encoded side photo (from sdk.scan()).
agenumberYesUser's age in years. Must be > 0.
gender"male" | "female"YesUser's gender.
heightnumberYesHeight in millimeters (500–2500). 175 cm → 1750.
weightnumberYesWeight in grams (10,000–200,000). 70 kg → 70000.
tightnessnumberNoFit preference offset.
systemOfMeasurement"metric" | "imperial"NoOutput unit system. Defaults to "metric".
avatarType"NO_AVATAR" | "GLB"NoPass "GLB" to receive a base64-encoded 3D avatar in the response. Defaults to "NO_AVATAR".

Throws if any required field is missing or out of range.

Response

{
  "estimations": {
    "estimationToken": {
      "token": "ESTIMATION_TOKEN"
    },
    "measurements": [
      {
        "estimationType": "BUST_GIRTH",
        "value": "920",
        "unit": "MILLIMETERS"
      }
    ],
    "avatarData": null,
    "input": {
      "preferredSystemOfMeasurement": "METRIC",
      "tightness": 0,
      "inputType": "CAMERA_FLOW"
    }
  }
}

Save the estimationToken — you can pass it to getBody2FitTokenEstimation later to retrieve the same measurements without requiring a new scan.

See all measurements for the full list of estimationType values.


getBody2FitStatsEstimation(params)

Estimates body measurements from age, height, weight, and gender alone — no photos required.

When to use: When users prefer not to take photos, when photo capture isn't available, or as a fallback. Less accurate than photo estimation.

const result = await sdk.getBody2FitStatsEstimation({
  age,     // number, > 0 — required
  gender,  // 'male' | 'female' — required
  height,  // millimeters, 500–2500 — required
  weight,  // grams, 10000–200000 — required
  // optional:
  tightness,
  systemOfMeasurement,
  avatarType,
});

Parameters

ParameterTypeRequiredDescription
agenumberYesUser's age in years. Must be > 0.
gender"male" | "female"YesUser's gender.
heightnumberYesHeight in millimeters (500–2500). 175 cm → 1750.
weightnumberYesWeight in grams (10,000–200,000). 70 kg → 70000.
tightnessnumberNoFit preference offset.
systemOfMeasurement"metric" | "imperial"NoOutput unit system. Defaults to "metric".
avatarType"NO_AVATAR" | "GLB"NoPass "GLB" to receive a base64-encoded 3D avatar in the response. Defaults to "NO_AVATAR".

Throws if any required field is missing or out of range.

Response

Same shape as getBody2FitPhotoEstimation, with inputType: "MANUAL_STATS".

{
  "estimations": {
    "estimationToken": { "token": "ESTIMATION_TOKEN" },
    "measurements": [...],
    "avatarData": null,
    "input": {
      "preferredSystemOfMeasurement": "METRIC",
      "tightness": 0,
      "inputType": "MANUAL_STATS"
    }
  }
}

See all measurements for the full list of estimationType values.


getBody2FitTokenEstimation(params)

Retrieves measurements for a returning user using a previously saved estimation token — no scan or stats entry required.

When to use: When a user has already scanned or entered stats in a previous session. Eliminates friction on repeat visits.

const result = await sdk.getBody2FitTokenEstimation({
  estimationToken,  // string — required
});

Parameters

ParameterTypeRequiredDescription
estimationTokenstringYesToken returned by a previous getBody2FitPhotoEstimation or getBody2FitStatsEstimation call.

Throws if estimationToken is missing.

Response

Same shape as the photo and stats estimation responses. The inputType reflects the original estimation method ("CAMERA_FLOW" or "MANUAL_STATS").


getBody2FitSizeRecommendation(params)

Returns the recommended size for one or more garments based on an estimation token.

When to use: When you need a clear size label to show to the user (e.g., "M", "L", "32").

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 result = await sdk.getBody2FitSizeRecommendation({
  estimationToken,  // string — required
  productInfo: [
    { brandId: 'BRAND_ID', garmentSku: 'GARMENT_SKU' },
    // additional products...
  ],
});

Parameters

ParameterTypeRequiredDescription
estimationTokenstringYesToken from a previous estimation call.
productInfoArray<{ brandId: string; garmentSku: string }>YesOne or more products to get recommendations for.

Throws if estimationToken or productInfo is missing, or if any product is missing brandId or garmentSku.

Response

An array with one entry per product, in the same order as productInfo:

[
  {
    "recommendation": {
      "recommendedSize": "M"
    }
  },
  {
    "recommendation": {
      "recommendedSize": "L"
    }
  }
]

getBody2FitSizeFitting(params)

Returns detailed fitting analysis for all available sizes of one or more garments.

When to use: When you want to show users how different sizes fit them — tightness at specific body areas, recommendation ranking, etc.

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 result = await sdk.getBody2FitSizeFitting({
  estimationToken,  // string — required
  productInfo: [
    { brandId: 'BRAND_ID', garmentSKU: 'GARMENT_SKU' },
  ],
});

Parameters

ParameterTypeRequiredDescription
estimationTokenstringYesToken from a previous estimation call.
productInfoArray<{ brandId: string; garmentSKU: string }>YesOne or more products to analyze.

Throws if estimationToken or productInfo is missing, or if any product is missing brandId or garmentSKU.

Response

[
  {
    "result": {
      "estimationToken": { "token": "ESTIMATION_TOKEN" },
      "sizes": [
        {
          "size": { "index": 0, "name": "0" },
          "recommendationRank": 9,
          "fittingPoints": [
            { "fittingPoint": "BUST_GIRTH", "tightness": 0 }
          ],
          "tightness": 0,
          "neutralRecommendationRank": 9
        }
      ],
      "sizeGroups": []
    }
  }
]

getBodygramScannerPlatformScanToken(params)

Generates a short-lived scan token that authorizes a single Bodygram Scanner Platform scan. Each token is a standard JWT, valid for one scan, and safe to share with the client once issued.

When to use: On your server, before the client opens Scanflow or calls a Platform estimation method. The token is then passed back to the client through a protected route.

This method takes your API key and must only ever run on your server. If the API key reaches the browser your account is compromised. The client should only see the issued token, never the key. See the full server pattern in the Platform integration guide.

const { token } = await serverSdk.getBodygramScannerPlatformScanToken({
  apiKey: process.env.BODYGRAM_API_KEY,  // server-side env var
  scope: [
    'api.platform.bodygram.com/scans:create',
    'api.platform.bodygram.com/scans:read',
  ],
});

Parameters

ParameterTypeRequiredDescription
apiKeystringYesYour Bodygram Scanner Platform API key. Server-only — never expose in client code.
scopestring[]YesWhat the token bearer is allowed to do. See scopes below.

Scopes

ScopeDescription
api.platform.bodygram.com/scans:createAllows a new scan to be created.
api.platform.bodygram.com/scans:readAllows scan results to be read after scanning.

Both scopes are required for a complete scan flow.

Response

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
FieldTypeDescription
tokenstringShort-lived JWT to pass to getBodygramScannerPlatform*Estimation calls on the client.

getBodygramScannerPlatformPhotoEstimation(params)

Estimates body measurements from front and side photos plus user stats, using the Bodygram Scanner Platform. Returns the full set of body dimensions, optional body composition, and optional posture analysis.

When to use: When you want raw body measurements (not size recommendations) and you have, or can capture, both photos. This is the most accurate Platform estimation method.

const result = await sdk.getBodygramScannerPlatformPhotoEstimation({
  token,    // string — required (from getBodygramScannerPlatformScanToken)
  front,    // base64 string — required (from sdk.scan())
  side,     // base64 string — required (from sdk.scan())
  age,      // number — required, years
  gender,   // 'male' | 'female' — required
  height,   // number — required, millimeters
  weight,   // number — required, grams
});

Parameters

ParameterTypeRequiredDescription
tokenstringYesScan token from getBodygramScannerPlatformScanToken. One token per scan.
frontstringYesBase64-encoded front photo (e.g. from sdk.scan()).
sidestringYesBase64-encoded side photo (e.g. from sdk.scan()).
agenumberYesUser's age in years. Must be > 0.
gender"male" | "female"YesUser's gender.
heightnumberYesHeight in millimeters. 180 cm → 1800.
weightnumberYesWeight in grams. 70 kg → 70000.

Response

{
  "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": {}
    }
  }
}
FieldTypeDescription
entry.idstringScan ID — save this to retrieve the scan later.
entry.status"success" | "failure"Always check before reading other fields.
entry.measurementsPlatformMeasurement[] | nullFull set of body dimensions. null on failure.
entry.bodyComposition{ bodyFatPercentage, skeletalMuscleMass } | nullBody composition derived from photos.
entry.avatarAvatar | null3D avatar (when generated).
entry.posture{ front: FrontPose, right: RightPose } | nullPose analysis from front and right views.

On status: "failure" all of measurements, bodyComposition, avatar, and posture are null. The error object shape is not guaranteed — handle errors defensively.


getBodygramScannerPlatformStatsEstimation(params)

Estimates body measurements from age, gender, height, and weight alone — no photos required.

When to use: When users prefer not to take photos, when photo capture isn't available, or as a fallback. Less accurate than photo estimation, and bodyComposition and posture are always null (an avatar is still generated).

const result = await sdk.getBodygramScannerPlatformStatsEstimation({
  token,    // string — required (from getBodygramScannerPlatformScanToken)
  age,      // number — required, years
  gender,   // 'male' | 'female' — required
  height,   // number — required, millimeters
  weight,   // number — required, grams
});

Parameters

ParameterTypeRequiredDescription
tokenstringYesScan token from getBodygramScannerPlatformScanToken. One token per scan.
agenumberYesUser's age in years. Must be > 0.
gender"male" | "female"YesUser's gender.
heightnumberYesHeight in millimeters. 180 cm → 1800.
weightnumberYesWeight in grams. 70 kg → 70000.

Response

{
  "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
  }
}

Response shape is identical to getBodygramScannerPlatformPhotoEstimation except input is keyed under statsEstimations instead of photoScan, and bodyComposition and posture are always null. An avatar is still generated from the stats.


Supported Locales

Pass one of these codes as the locale option during initialization.

CodeLanguage
arArabic
enEnglish
esSpanish
es-419Spanish (Latin America)
frFrench
hi-INHindi
jaJapanese
ptPortuguese
pt-BRPortuguese (Brazil)
trTurkish
viVietnamese
zhChinese (Simplified)
zh-CNChinese (Simplified)
zh-HKChinese (Hong Kong)
zh-TWChinese (Traditional)

Measurements

All measurements returned by Body2Fit methods (getBody2FitPhotoEstimation, getBody2FitStatsEstimation, getBody2FitTokenEstimation) in estimations.measurements. Each entry has estimationType, value, and unit.

Note: The Bodygram Scanner Platform methods (getBodygramScannerPlatform*Estimation) use a different shape — see PlatformMeasurement below.

estimationTypeunitDescription
AGEYEARSAge
GENDERGENDERGender — "1" for male, "2" for female
WEIGHTGRAMSWeight
HEIGHTMILLIMETERSHeight
ACROSS_BACK_SHOULDER_WIDTHMILLIMETERSWidth across back between shoulders
BACK_NECK_HEIGHTMILLIMETERSHeight of back neck point
BACK_NECK_POINT_TO_GROUND_CONTOUREDMILLIMETERSLength from back neck to ground following body contour
BACK_NECK_POINT_TO_WAISTMILLIMETERSLength from back neck to waist
BACK_NECK_POINT_TO_WRIST_RMILLIMETERSLength from back neck to right wrist
BELLY_WAIST_GIRTHMILLIMETERSBelly waist circumference
BELLY_WAIST_HEIGHTMILLIMETERSHeight of belly waist
BUST_GIRTHMILLIMETERSBust circumference
BUST_HEIGHTMILLIMETERSHeight of bust point
CALF_GIRTH_RMILLIMETERSRight calf circumference
FOREARM_GIRTH_RMILLIMETERSRight forearm circumference
HIP_GIRTHMILLIMETERSHip circumference
HIP_HEIGHTMILLIMETERSHeight of hips
INSIDE_LEG_HEIGHTMILLIMETERSHeight of inside leg
INSIDE_LEG_LENGTH_RMILLIMETERSRight inside leg length
KNEE_GIRTH_RMILLIMETERSRight knee circumference
KNEE_HEIGHT_RMILLIMETERSHeight of right knee
MID_THIGH_GIRTH_RMILLIMETERSRight mid-thigh circumference
NECK_BASE_GIRTHMILLIMETERSNeck base circumference
NECK_GIRTHMILLIMETERSNeck circumference
OUTER_ANKLE_HEIGHT_RMILLIMETERSHeight of right outer ankle
OUTER_ARM_LENGTH_RMILLIMETERSRight outer arm length
OUTSEAM_RMILLIMETERSRight outseam length
OUTSIDE_LEG_LENGTH_RMILLIMETERSRight outside leg length
SHOULDER_TO_ELBOW_RMILLIMETERSRight shoulder to elbow length
THIGH_GIRTH_RMILLIMETERSRight thigh circumference
TOP_HIP_GIRTHMILLIMETERSTop hip circumference
TOP_HIP_HEIGHTMILLIMETERSHeight of top hip
UNDER_BUST_GIRTHMILLIMETERSUnder bust circumference
UPPER_ARM_GIRTH_RMILLIMETERSRight upper arm circumference
WAIST_GIRTHMILLIMETERSWaist circumference
WAIST_HEIGHTMILLIMETERSHeight of waist
WRIST_GIRTH_RMILLIMETERSRight wrist circumference

PlatformMeasurement

Returned by the Bodygram Scanner Platform methods (getBodygramScannerPlatformPhotoEstimation and getBodygramScannerPlatformStatsEstimation) inside entry.measurements. Each entry has name, unit, and value.

type PlatformMeasurement = {
  name: string;   // camelCase measurement key — see table below
  unit: string;   // unit string — typically "mm"
  value: number;  // integer value in the reported unit
};

Unlike the Body2Fit Measurements table above (uppercase estimationType, MILLIMETERS), the platform response uses camelCase names and lowercase unit codes. To convert millimeter values to centimeters, divide by 10.

const { measurements } = result.entry;

// Look up a single measurement by name
const bust = measurements.find(m => m.name === 'bustGirth');
const bustCm = bust ? bust.value / 10 : null;

// Build a lookup map keyed by measurement name
const byName = Object.fromEntries(measurements.map(m => [m.name, m]));
const waistCm = byName.waistGirth.value / 10;

Measurement names

nameunitDescription
acrossBackShoulderWidthmmWidth across back between shoulders
backNeckHeightmmHeight of back neck point
backNeckPointToGroundContouredmmLength from back neck to ground following body contour
backNeckPointToWaistmmLength from back neck to waist
backNeckPointToWristLengthRmmLength from back neck to right wrist
bellyWaistDepthmmBelly waist depth (front-to-back)
bellyWaistGirthmmBelly waist circumference
bellyWaistHeightmmHeight of belly waist
bellyWaistWidthmmBelly waist width (side-to-side)
bustGirthmmBust circumference
bustHeightmmHeight of bust point
calfGirthRmmRight calf circumference
forearmGirthRmmRight forearm circumference
hipGirthmmHip circumference
hipHeightmmHeight of hips
insideLegHeightmmHeight of inside leg
insideLegLengthRmmRight inside leg length
kneeGirthRmmRight knee circumference
kneeHeightRmmHeight of right knee
midThighGirthRmmRight mid-thigh circumference
neckBaseGirthmmNeck base circumference
neckGirthmmNeck circumference
outerAnkleHeightRmmHeight of right outer ankle
outerArmLengthRmmRight outer arm length
outseamRmmRight outseam length
outsideLegLengthRmmRight outside leg length
shoulderToElbowRmmRight shoulder to elbow length
thighGirthRmmRight thigh circumference
topHipGirthmmTop hip circumference
topHipHeightmmHeight of top hip
underBustGirthmmUnder bust circumference
upperArmGirthRmmRight upper arm circumference
waistGirthmmWaist circumference
waistHeightmmHeight of waist
wristGirthRmmRight wrist circumference

Names suffixed with R are right-side measurements. Photo scans return the full set; stats-only estimations return a subset based on the inputs available.


Fitting Points

All fitting points returned in sizes[].fittingPoints. Each entry has a fittingPoint key and a tightness value.

fittingPointDisplay name
NECK_GIRTHNeck
SHOULDER_WIDTHShoulders
SLEEVE_LENGTH_CENTER_BACKSleeve
SLEEVE_LENGTHSleeve
CHESTChest Width
BODY_WIDTHChest Width
WAISTWaist
HIPHips
INSEAMInseam
THIGH_GIRTHThigh
DRESS_LENGTHLength
SKIRT_LENGTHLength
MINISKIRT_LENGTHLength
HALF_CALF_SKIRT_LENGTHLength
ANKLE_SKIRT_LENGTHLength
ANKLE_DRESS_LENGTHLength
BODY_LENGTHLength
UNDERBUSTUnderbust

Avatar

The avatar object is returned when the scan includes avatar generation.

{
  data: string;   // base64-encoded avatar data
  format: string; // file format (e.g. "glb")
  type: string;   // avatar type (e.g. "UPPER_BODY", "FULL_BODY")
}

Posture

The posture object contains pose analysis data for the front and right views captured during scanning. Each view returns two arrays:

  • angles — measured angles (in degrees) for named body landmarks.
  • lines — pixel-coordinate polylines used to derive those angles, plotted in the source image space (origin at the top-left of the captured photo).
{
  front: FrontPose;
  right: RightPose;
}

FrontPose

Front view exposes one two-point line per landmark plus a single angle per line. Each angle is the counterclockwise angle from the horizontal, in degrees, measured between the two points of the matching line (left-to-right in the image, i.e. right-to-left on the body).

type FrontPose = {
  angles: Array<{
    name: 'ear' | 'shoulder' | 'topHip' | 'foot';
    unit: 'degrees';
    value: number;          // counterclockwise angle from horizontal
  }>;
  lines: Array<{
    name: 'ear' | 'shoulder' | 'topHip' | 'foot';
    points: Array<{ x: number; y: number }>; // exactly 2 points (line segment)
  }>;
};

Front landmarks

Each line connects the left/right pair of the same anatomical landmark.

nameLandmarkReference
earTragion (the cartilaginous notch in front of the ear canal).ISO 8559-1:2017, 3.1.3
shoulderShoulder point.ISO 8559-1:2017, 3.1.1
topHipHighest point of the hip bone.ISO 8559-1:2017, 3.1.16
footGround level — used as a horizontal reference to compare the other angles against.

Front posture landmarks: ear, shoulder, top hip, and foot lines, with the angle measured counterclockwise from horizontal.

Example

{
  "angles": [
    { "name": "ear",      "unit": "degrees", "value": -0.59860957 },
    { "name": "foot",     "unit": "degrees", "value": -2.0101578 },
    { "name": "shoulder", "unit": "degrees", "value": -0.23841834 },
    { "name": "topHip",   "unit": "degrees", "value": -1.3451012 }
  ],
  "lines": [
    {
      "name": "topHip",
      "points": [
        { "x": 710.16583, "y": 940.7551 },
        { "x": 456.1496,  "y": 934.7906 }
      ]
    },
    {
      "name": "ear",
      "points": [
        { "x": 654, "y": 381 },
        { "x": 519, "y": 380 }
      ]
    },
    {
      "name": "foot",
      "points": [
        { "x": 735.14667, "y": 1717.625 },
        { "x": 414.76804, "y": 1706.3802 }
      ]
    },
    {
      "name": "shoulder",
      "points": [
        { "x": 738.6984, "y": 537.90106 },
        { "x": 433.4781, "y": 536.631 }
      ]
    }
  ]
}

RightPose

Right (side) view exposes multi-segment polylines. Each entry in angles carries an angles array — one value per segment between consecutive points. Each angle is the clockwise angle from the vertical, in degrees, measured along the segment from the lower to the higher point (i.e. ankle → ear direction along the body).

type RightPose = {
  angles: Array<{
    name: 'bodyLine' | 'backWaistLine';
    unit: 'degrees';
    angles: number[];        // bodyLine: 4 values, backWaistLine: 3 values
  }>;
  lines: Array<{
    name: 'bodyLine' | 'backWaistLine';
    points: Array<{ x: number; y: number }>; // bodyLine: 5 points, backWaistLine: 4 points
  }>;
};

For a polyline with n points, the matching angles array contains n − 1 values — each angle corresponds to the segment between points[i] and points[i + 1].

Right landmarks

bodyLine — 5 points, 4 angles

Connects the body's main vertical structure from the ground up.

IndexLandmarkReference
0Outer ankle point.ISO 8559-1:2017, 3.1.18
1Center of knee girth.ISO 8559-1:2017, 5.3.22
2Highest point of the hip bone.ISO 8559-1:2017, 3.1.16
3Shoulder point.ISO 8559-1:2017, 3.1.1
4Tragion.ISO 8559-1:2017, 3.1.3
backWaistLine — 4 points, 3 angles

Traces the back contour through the torso. Each point is the back-most point of the corresponding girth.

IndexLandmarkReference
0Back-most point of top hip girth.ISO 8559-1:2017, 5.3.13
1Back-most point of belly waist girth (horizontal girth through the navel).
2Back-most point of waist girth.ISO 8559-1:2017, 5.3.10
3Back-most point of under-bust girth.ISO 8559-1:2017, 5.3.8

Right (side) posture landmarks: bodyLine polyline from outer ankle (0) up through knee (1), top hip (2), shoulder (3), and tragion (4), with each segment's angle measured clockwise from vertical.

Example

{
  "angles": [
    {
      "name": "backWaistLine",
      "unit": "degrees",
      "angles": [21.443363, 9.388262, -4.5393705]
    },
    {
      "name": "bodyLine",
      "unit": "degrees",
      "angles": [6.476125, 1.875514, -7.529927, 21.864304]
    }
  ],
  "lines": [
    {
      "name": "backWaistLine",
      "points": [
        { "x": 549.01276, "y": 890.2904 },
        { "x": 567.0013,  "y": 844.4911 },
        { "x": 572.44586, "y": 811.5611 },
        { "x": 565.02185, "y": 718.0516 }
      ]
    },
    {
      "name": "bodyLine",
      "points": [
        { "x": 607, "y": 1770 },
        { "x": 649, "y": 1400 },
        { "x": 662, "y": 1003 },
        { "x": 593, "y": 481 },
        { "x": 656, "y": 324 }
      ]
    }
  ]
}

Notes

  • All coordinates are in pixel space of the captured photo (origin top-left, +x right, +y down).
  • Front angles are signed degrees, counterclockwise from horizontal. Right angles are signed degrees, clockwise from vertical, with each segment measured from its lower point to its higher point.
  • A perfectly upright pose produces angles close to 0. The sign indicates the tilt direction.
  • Look up entries in lines and angles by name rather than by array position.

Posture names in the Bodygram Platform dashboard

The Posture analysis section of the Bodygram Platform dashboard uses friendlier display names for the same data. They map to the API names as follows:

Dashboard labelAPI field
Headfront.angles[name="ear"]
Shoulderfront.angles[name="shoulder"]
Pelvisfront.angles[name="topHip"]
Neckright.angles[name="bodyLine"].angles[3]
Backright.angles[name="bodyLine"].angles[2]

On this page