True Fit Recommendations API
Size recommendations and general fit guidance for retailer products.
Size recommendations and general fit guidance for retailer products.
openapi: 3.2.0
info:
title: True Fit Partner Recommendations 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: Recommendations
description: Size recommendations and general fit guidance for retailer products.
paths:
/partner/{partnerId}/profile/{profileId}/recommendation/bulk:
parameters:
- $ref: '#/components/parameters/partnerId'
- $ref: '#/components/parameters/profileId'
- $ref: '#/components/parameters/partnerUserId'
- $ref: '#/components/parameters/tfPartnerUserId'
post:
operationId: getBulkRecommendation
tags:
- Recommendations
summary: Recommend sizes for up to 100 products
description: 'Returns a recommended size per product for the given profile.
The response array has the **same length and order as the request array**, so results
can be matched positionally. Each result also echoes the `retailerDomain`, `productId`,
and `locale` you sent. Failures are per item: a product that cannot be recommended
returns `success: false` with an `error`, and does not fail the other items.
This is a `POST` because the product list is sent in the body; it has no side effects
and returns `201`.
'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BulkRequest'
examples:
mixed:
value:
- retailerDomain: https://NYDJ.com/collections/jeans
productId: '7093242789933'
locale: en_US
- retailerDomain: landsend.com
productId: '532582'
locale: en_US
- retailerDomain: unknown.example.org
productId: abc
locale: en_US
responses:
'201':
description: One result per requested item, in request order.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/BulkRecommendationResult'
examples:
mixed:
value:
- retailerDomain: https://NYDJ.com/collections/jeans
productId: '7093242789933'
locale: en_US
success: true
recommendedSize: '10'
- retailerDomain: landsend.com
productId: '532582'
locale: en_US
success: false
error: noSizesFitTheUser
- retailerDomain: unknown.example.org
productId: abc
locale: en_US
success: false
error: Unsupported retailer domain
'400':
$ref: '#/components/responses/ValidationFailed'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/ProfileNotFound'
/partner/{partnerId}/general-guidance/bulk:
parameters:
- $ref: '#/components/parameters/partnerId'
post:
operationId: getBulkGeneralGuidance
tags:
- Recommendations
summary: Get aggregate fit guidance for up to 100 products
description: 'Returns crowd-sourced fit guidance for products - how the product ran for other
shoppers - without reference to any individual user.
Unlike every other endpoint, this one needs **no user identifier**: the partner API key
alone authorizes it, and no `partnerUserId` or `tfPartnerUserId` query parameter is
required. Use it for anonymous visitors and for product pages where no profile exists.
The response array has the same length and order as the request array, and failures are
per item.
'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BulkRequest'
examples:
mixed:
value:
- retailerDomain: nydj.com
productId: '7093242789933'
locale: en_US
- retailerDomain: unknown.example.org
productId: abc
locale: en_US
responses:
'201':
description: One result per requested item, in request order.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/BulkGeneralGuidanceResult'
examples:
mixed:
value:
- retailerDomain: nydj.com
productId: '7093242789933'
locale: en_US
success: true
recommendationSummary: Runs small
recommendationMessage: Most shoppers found this ran small - consider sizing up.
- retailerDomain: unknown.example.org
productId: abc
locale: en_US
success: false
error: Unsupported retailer domain
'400':
$ref: '#/components/responses/ValidationFailed'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/UnsupportedPartner'
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
BulkGeneralGuidanceResult:
description: 'One result, echoing the request item''s fields. Match results to requests by position.
'
oneOf:
- title: Guidance available
allOf:
- $ref: '#/components/schemas/BulkRequestItem'
- type: object
required:
- success
- recommendationSummary
- recommendationMessage
properties:
success:
type: boolean
const: true
recommendationSummary:
type: string
description: Short headline describing how the product runs, localized to `locale`.
example: Runs small
recommendationMessage:
type: string
description: Longer explanation suitable for display, localized to `locale`.
example: Most shoppers found this ran small - consider sizing up.
- title: No guidance
allOf:
- $ref: '#/components/schemas/BulkRequestItem'
- type: object
required:
- success
- error
properties:
success:
type: boolean
const: false
error:
type: string
description: "Why no guidance was returned.\n * `Invalid retailerDomain` - the value could not be parsed as a hostname.\n * `Unsupported retailer domain` - the hostname is not mapped to a retailer.\n * `No general guidance returned for product` - the product is unknown, or\n too few shoppers have rated it to produce guidance.\n"
examples:
- Unsupported retailer domain
- No general guidance returned for product
BulkRecommendationResult:
description: 'One result, echoing the request item''s fields. Match results to requests by position.
'
oneOf:
- title: Recommended
allOf:
- $ref: '#/components/schemas/BulkRequestItem'
- type: object
required:
- success
- recommendedSize
properties:
success:
type: boolean
const: true
recommendedSize:
type: string
description: The recommended size label, as the retailer prints it.
example: '10'
- title: Not recommended
allOf:
- $ref: '#/components/schemas/BulkRequestItem'
- type: object
required:
- success
- error
properties:
success:
type: boolean
const: false
error:
type: string
description: "Why no size was returned.\n\nDomain resolution:\n * `Invalid retailerDomain` - the value could not be parsed as a hostname.\n * `Unsupported retailer domain` - the hostname is not mapped to a retailer.\n * `No recommendation returned for product` - the product is unknown to True Fit.\n\nFit outcomes:\n * `incompleteProfile` - the profile lacks the measurements needed for this garment.\n * `noProfile` - the profile no longer exists.\n * `userEstimationInProgress` - body estimation is still running after a\n profile, measurement, or closet write. Retry after a short delay.\n * `noSizesFitTheUser` - no stocked size fits.\n * `invalidGender` - the product's department does not apply to this profile.\n * `unrecommendableDepartment` - the product is in a department True Fit does\n not size, such as accessories.\n * `productMissingData` - the product lacks the garment data needed to size it.\n * `recommendationDisabled` - recommendations are turned off for that retailer.\n * `error` - an unexpected failure. Safe to retry.\n"
examples:
- Unsupported retailer domain
- incompleteProfile
- userEstimationInProgress
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
BulkRequestItem:
type: object
required:
- retailerDomain
- productId
properties:
retailerDomain:
type: string
minLength: 1
description: 'The retailer the product belongs to, as a hostname. A full URL is also accepted -
only the hostname is used, and protocol, port, path, and query string are discarded.
`www.` is **not** stripped, so the hostname must match the mapping True Fit holds for
that retailer. Matching is case-insensitive. Ask True Fit to register any retailer
domain you intend to send.
'
examples:
- nydj.com
- https://NYDJ.com:443/collections/jeans?utm=x
productId:
type: string
minLength: 1
description: The retailer's own product identifier.
example: '7093242789933'
locale:
type: string
description: 'Locale for the product data and any returned copy, as `language_COUNTRY`. Defaults
to the retailer''s primary locale when omitted.
'
example: en_US
BulkRequest:
type: array
description: Between 1 and 100 items. Duplicate products are permitted and each gets its own result.
minItems: 1
maxItems: 100
items:
$ref: '#/components/schemas/BulkRequestItem'
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
UnsupportedPartner:
description: 'The `partnerId` is unknown or disabled. Returned before credentials are checked, so an
invalid API key against an unknown partner is a `404`, not a `401`.
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
unsupportedPartner:
value:
statusCode: 404
message: Unsupported partner
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)>`.
'