True Fit Measurements API
Body measurements attached to a profile.
Body measurements attached to a profile.
openapi: 3.2.0
info:
title: True Fit Partner Measurements API
version: '2026-08-05'
description: "These APIs can be used to manage True Fit profiles on behalf of your own users and to\nrequest size recommendations for retailer products.\n\n# Getting Started\n\nHere are the general concepts - please see further below for the specific endpoint\ndocumentation.\n\n## Base URL\n\nEvery path in this reference is relative to the base URL, which already includes the\n`/api` prefix:\n\n```\nhttps://partner.truefitcorp.com/api\n```\n\n## Authentication\n\nAll `/partner/{partnerId}/**` endpoints use HTTP Basic authentication. The **username is\nempty** and the **password is your partner API key**:\n\n```\nAuthorization: Basic <base64(\":\" + apiKey)>\n```\n\nNote the leading colon. `base64(apiKey)` without it has no separator and is rejected\nwith `401`.\n\nAn unknown or disabled `partnerId` returns `404` **before** credentials are checked, so a\n`404` on your first call points at the `partnerId` or the environment, not at the key.\n\n`GET /id-sync` is authenticated differently - it is signed with HMAC-SHA256 rather than\nBasic auth. See that endpoint for the signing scheme.\n\n## Identifying a user\n\nPartner endpoints operate on a True Fit user, not on your identifier directly.\n`GET /id-sync` creates that user and maps one of your identifiers to it. Call it once per\nuser, store the `tfPartnerUserId` it returns, and drive every later request from that.\n\nExcept for `POST /general-guidance/bulk`, every endpoint needs a user identifier as a\nquery parameter:\n\n* `tfPartnerUserId` - the identifier returned by `GET /id-sync`. Preferred: it is a\n durable handle on one specific True Fit user and its data.\n* `partnerUserId` - your own identifier for the user, as passed to `GET /id-sync`.\n\nSupply at least one; omitting both returns `400`. If you send both, `tfPartnerUserId`\ntakes precedence. An identifier that was never synced returns `401 Unknown partner user`,\nwhich means \"call `GET /id-sync` first\" rather than \"bad credentials\".\n\n## Profiles\n\nA profile is the entity that holds information about the person being shopped for -\ndepartment, alias, measurements, and closet items. When a profile is created it is\nassigned a `profileId`, which is then used in the path of the measurements, closet, and\nrecommendation endpoints.\n\nA user may have several profiles, one per person they shop for. List them with\n`GET /profile` and address them explicitly by `profileId`; there is no implicit \"current\"\nprofile in this API.\n\nProfiles you create record your `partnerId` as their `originStore`.\n\n## Getting a size recommendation\n\nA profile needs measurements, closet items, or both before it can be sized: a profile\ncarrying neither has nothing to size against and returns `incompleteProfile`. Add\nmeasurements with `PUT /profile/{profileId}/measurements` and closet items with\n`POST /profile/{profileId}/closet`, then call\n`POST /profile/{profileId}/recommendation/bulk`.\n\nProfile writes trigger asynchronous body estimation, so a recommendation requested\nimmediately after one may return `userEstimationInProgress`. Retry after a short delay.\n\nWhen no profile exists - anonymous visitors, or a product page before registration -\nuse `POST /general-guidance/bulk` instead. It returns how a product ran for shoppers\ngenerally, needs no user identifier, and is the natural fallback when a recommendation\nreturns `incompleteProfile` or `noProfile`.\n\n## Example Flow - Product Page\n\n1. Once per user, call `GET /id-sync` and persist the returned `tfPartnerUserId`.\n2. Call `GET /profile` to list the user's profiles.\n3. If the list is empty, give the user an opportunity to register, then create a profile\n with `POST /profile`.\n4. Add what you know about the user: `PUT /profile/{profileId}/measurements`, and\n `POST /profile/{profileId}/closet` for garments they already own.\n5. Call `POST /profile/{profileId}/recommendation/bulk` with the products on the page and\n present `recommendedSize` for each successful result.\n6. For products that return `success: false`, or when no profile exists, fall back to\n `POST /general-guidance/bulk`.\n\n## Bulk requests\n\nBoth bulk endpoints take a JSON **array** of 1 to 100 items and return an array of the\n**same length and in the same order**, so results can be matched to requests by position.\nEach result also echoes the `retailerDomain`, `productId`, and `locale` you sent.\n\nFailures are per item: each result carries `success: true` with its data, or\n`success: false` with an `error` string. One unrecognised product does not fail the batch,\nand the HTTP status is still a success status.\n\n`retailerDomain` accepts a hostname or a full URL, from which only the hostname is used -\nprotocol, port, path, and query string are discarded, and matching is case-insensitive.\n`www.` is not stripped, so confirm with True Fit which exact hostnames are registered for\nyour retailers.\n\n## Request bodies\n\nEnum values are documented in their canonical lowercase form but matched\ncase-insensitively, so `Womens` and `womens` are equally valid - the stored and returned\nvalue is always lowercase. Fields not listed in a schema are ignored rather than rejected.\n\n## General API Behavior\n\n* Success statuses vary by endpoint. Empty-bodied writes return `200` in some places and\n `204` in others, and both bulk endpoints return `201`. Treat any 2xx as success rather\n than matching an exact code.\n* A `400 - Bad Request` is returned when a body or query string fails validation, or when\n no user identifier is supplied.\n* A `401 - Unauthorized` is returned for a missing or wrong API key, and for a user\n identifier that has no True Fit mapping.\n* A `404 - Not Found` is returned for an unknown or disabled `partnerId`, and for a\n `profileId` that does not exist or does not belong to the identified user. Another\n user's profile returns `404` rather than `403` so that the API cannot be used to detect\n whether a profile exists.\n* It is possible for any endpoint to return a `500 - Internal Server Error` if an\n unexpected error is encountered.\n* Validation failures carry a field-keyed error map and, unlike other errors, no\n `statusCode` field:\n\n```json\n{ \"message\": \"Validation failed\", \"error\": { \"0.retailerDomain\": \"Invalid input: expected string, received undefined\" } }\n```\n\nArray indices appear as the first path segment, so `0.retailerDomain` is the\n`retailerDomain` field of the first item in a bulk request. All other errors use the\nstandard shape:\n\n```json\n{ \"statusCode\": 404, \"message\": \"Profile not found\", \"error\": \"Not Found\" }\n```\n\n## Rate limits\n\nNo rate limits are enforced today. Expected request volume should be agreed during\nonboarding.\n"
contact:
name: True Fit
email: tech@truefit.com
license:
name: Proprietary - (c) True Fit Corporation
servers:
- url: https://partner.truefitcorp.com/api
description: Partner API
security:
- partnerApiKey: []
tags:
- name: Measurements
description: Body measurements attached to a profile.
paths:
/partner/{partnerId}/profile/{profileId}/measurements:
parameters:
- $ref: '#/components/parameters/partnerId'
- $ref: '#/components/parameters/profileId'
- $ref: '#/components/parameters/partnerUserId'
- $ref: '#/components/parameters/tfPartnerUserId'
get:
operationId: getMeasurements
tags:
- Measurements
summary: Read the profile's measurements
responses:
'200':
description: 'The profile''s measurements. Each exact measurement is echoed as submitted and
additionally carries a normalized value in True Fit''s standard unit (inches for
lengths and girths, pounds for weight, months for age).
'
content:
application/json:
schema:
$ref: '#/components/schemas/MeasurementsResponse'
examples:
heightWeightAge:
value:
exactMeasurements:
height:
unit: ft
value: 5
secondUnit: in
secondValue: 6
normalizedValue: 66
normalizedUnit: in
weight:
unit: lb
value: 140
normalizedValue: 140
normalizedUnit: lb
generalMeasurements:
waist: typical
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/ProfileNotFound'
put:
operationId: upsertMeasurements
tags:
- Measurements
summary: Add or replace measurements
description: 'Upserts the measurements you send. Measurement points you omit are left untouched;
this is not a full replacement of the measurement set.
Writing measurements re-runs body estimation, so a recommendation requested
immediately afterwards may return `userEstimationInProgress`. Retry after a short delay.
'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MeasurementsRequest'
examples:
exact:
value:
exactMeasurements:
height:
unit: ft
value: 5
secondUnit: in
secondValue: 6
weight:
unit: lb
value: 140
age:
unit: year
value: 30
general:
value:
generalMeasurements:
waist: typical
bust: small
responses:
'200':
description: Measurements written. No response body.
'400':
$ref: '#/components/responses/ValidationFailed'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/ProfileNotFound'
components:
schemas:
Error:
type: object
description: Standard error body.
required:
- statusCode
- message
properties:
statusCode:
type: integer
example: 404
message:
type: string
example: Profile not found
error:
type: string
example: Not Found
GeneralMeasurementValue:
type: string
description: A qualitative measurement - how the user describes a body point or fit preference.
enum:
- verySmall
- small
- typical
- big
- veryBig
example: typical
MeasurementPoint:
type: string
description: A point on the body that can carry a measurement. Case-sensitive.
enum:
- armLength
- bust
- calf
- chest
- cupSize
- footBall
- footHeelToBall
- footInstepHeight
- footInstepWidth
- footLength
- footWidth
- head
- height
- highHip
- inseam
- inseamWithLift
- lowHip
- naturalWaist
- neck
- ribcage
- shoulders
- sleeveLength
- stomach
- thigh
- torsoLength
- upperArm
- waist
- weight
- footArchSupport
- toe
- heelWidth
- sleeveWidth
- age
example: height
MeasurementsRequest:
type: object
description: 'Measurements to upsert. Points you omit are left unchanged. At least one of the two
maps should be present; an empty object is accepted and is a no-op.
'
properties:
exactMeasurements:
type: object
description: Numeric measurements keyed by measurement point.
propertyNames:
$ref: '#/components/schemas/MeasurementPoint'
additionalProperties:
$ref: '#/components/schemas/ExactMeasurementValue'
generalMeasurements:
type: object
description: 'Qualitative measurements keyed by measurement point - how the user describes a body
point, or how they prefer a garment to fit there.
'
propertyNames:
$ref: '#/components/schemas/MeasurementPoint'
additionalProperties:
$ref: '#/components/schemas/GeneralMeasurementValue'
ExactMeasurementValue:
type: object
description: 'A numeric measurement. Compound values use both unit pairs: 5''6" is
`{ unit: "ft", value: 5, secondUnit: "in", secondValue: 6 }`.
'
required:
- unit
- value
properties:
unit:
allOf:
- $ref: '#/components/schemas/Uom'
description: Primary unit - `ft` for feet, `kg` for kilograms, and so on.
value:
type: number
example: 5
secondUnit:
allOf:
- $ref: '#/components/schemas/Uom'
description: Secondary unit, used alongside the primary - `in` for inches with `ft` for feet.
secondValue:
type: number
example: 6
ExactMeasurementResponseValue:
allOf:
- $ref: '#/components/schemas/ExactMeasurementValue'
- type: object
required:
- normalizedValue
- normalizedUnit
properties:
normalizedValue:
type: number
description: The measurement converted to True Fit's standard unit for that point.
example: 66
normalizedUnit:
type: string
description: 'Standard unit for the point - `in` for lengths and girths, `lb` for weight,
`month` for age.
'
example: in
ValidationError:
type: object
description: Schema validation failure. Note this body carries no `statusCode`.
required:
- message
- error
properties:
message:
type: string
example: Validation failed
error:
type: object
description: Failing field path to error message.
additionalProperties:
type: string
MeasurementsResponse:
type: object
required:
- exactMeasurements
- generalMeasurements
properties:
exactMeasurements:
type: object
propertyNames:
$ref: '#/components/schemas/MeasurementPoint'
additionalProperties:
$ref: '#/components/schemas/ExactMeasurementResponseValue'
generalMeasurements:
type: object
propertyNames:
$ref: '#/components/schemas/MeasurementPoint'
additionalProperties:
$ref: '#/components/schemas/GeneralMeasurementValue'
Uom:
type: string
description: Unit of measure.
enum:
- cm
- mm
- ft
- in
- kg
- year
- st
- lb
- month
example: in
responses:
Unauthorized:
description: 'Missing or invalid Basic credentials, or a user identifier that does not match a known
partner user.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingCredentials:
value:
statusCode: 401
message: Missing Basic auth credentials
error: Unauthorized
invalidCredentials:
value:
statusCode: 401
message: Invalid credentials
error: Unauthorized
unknownUser:
summary: The identifier has no True Fit mapping - call GET /id-sync first
value:
statusCode: 401
message: Unknown partner user
error: Unauthorized
ProfileNotFound:
description: 'The `profileId` does not exist or does not belong to the identified user. Also returned
when the `partnerId` is unknown or disabled.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
profileNotFound:
value:
statusCode: 404
message: Profile not found
error: Not Found
ValidationFailed:
description: 'The request body or query string failed schema validation. `error` maps each failing
field path to its message; array indices appear as the first path segment.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
examples:
missingField:
value:
message: Validation failed
error:
0.retailerDomain: 'Invalid input: expected string, received undefined'
badEnum:
value:
message: Validation failed
error:
category: 'Invalid option: expected one of "belts"|"bottoms"|"bras"'
parameters:
partnerUserId:
name: partnerUserId
in: query
required: false
description: 'Your own identifier for the user, as passed to `GET /id-sync`. Send either this or
`tfPartnerUserId`; omitting both returns `400`. If both are sent, `tfPartnerUserId` wins.
'
schema:
type: string
example: acme-user-42817
tfPartnerUserId:
name: tfPartnerUserId
in: query
required: false
description: 'True Fit''s identifier for the user, returned by `GET /id-sync`. Send either this or
`partnerUserId`; omitting both returns `400`. Takes precedence when both are sent.
'
schema:
type: string
example: p-3f9c1a80-5d2e-4a17-9b64-8e0c2d7f1a55
partnerId:
name: partnerId
in: path
required: true
description: Your partner identifier, issued during onboarding.
schema:
type: string
example: acme
profileId:
name: profileId
in: path
required: true
description: 'A profile belonging to the identified user, from `GET /profile`. Profiles belonging to
another user return `404` rather than `403`, so the endpoint cannot be used to probe
for the existence of other users'' profiles.
'
schema:
type: string
format: uuid
example: 7c9e6679-7425-40de-944b-e07fc1f90ae7
securitySchemes:
partnerApiKey:
type: http
scheme: basic
description: 'Empty username, partner API key as the password:
`Authorization: Basic <base64(":" + apiKey)>`.
'