True Fit Profiles API

Creating and managing fit profiles belonging to a partner user.

OpenAPI Specification

true-fit-profiles-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: True Fit Partner Profiles 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: Profiles
  description: Creating and managing fit profiles belonging to a partner user.
paths:
  /partner/{partnerId}/profile:
    parameters:
    - $ref: '#/components/parameters/partnerId'
    - $ref: '#/components/parameters/partnerUserId'
    - $ref: '#/components/parameters/tfPartnerUserId'
    get:
      operationId: listProfiles
      tags:
      - Profiles
      summary: List the user's profiles
      description: 'Returns every profile belonging to the user, oldest first is not guaranteed - treat the

        order as unspecified and address profiles by `profileId`. A user with no profiles yet

        returns an empty array.

        '
      responses:
        '200':
          description: The user's profiles.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Profile'
              examples:
                single:
                  value:
                  - profileId: 7c9e6679-7425-40de-944b-e07fc1f90ae7
                    alias: Primary
                    department: womens
                    isActive: false
                    estimatedDob: '1994-03-01T00:00:00.000Z'
                    originStore: acme
                    originLocale: null
                    createdAt: '2026-07-14T09:12:44.000Z'
                    updatedAt: '2026-07-31T16:02:10.000Z'
                empty:
                  value: []
        '400':
          $ref: '#/components/responses/MissingUserIdentifier'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/UnsupportedPartner'
    post:
      operationId: createProfile
      tags:
      - Profiles
      summary: Create a profile
      description: 'Creates a profile for the user. The created profile records your `partnerId` as its

        `originStore`. An empty object is a valid body; both fields are optional.

        '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProfileRequest'
            examples:
              withDepartment:
                value:
                  alias: Primary
                  department: womens
              minimal:
                value: {}
      responses:
        '201':
          description: The created profile.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Profile'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/UnsupportedPartner'
  /partner/{partnerId}/profile/{profileId}:
    parameters:
    - $ref: '#/components/parameters/partnerId'
    - $ref: '#/components/parameters/profileId'
    - $ref: '#/components/parameters/partnerUserId'
    - $ref: '#/components/parameters/tfPartnerUserId'
    put:
      operationId: updateProfile
      tags:
      - Profiles
      summary: Update a profile
      description: 'Partially updates the profile. Only the fields you send are changed. Updating a profile

        re-runs body estimation, so a subsequent recommendation may briefly return

        `userEstimationInProgress`.

        '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProfileRequest'
            examples:
              rename:
                value:
                  alias: Weekend
      responses:
        '204':
          description: Updated. No response body.
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/ProfileNotFound'
    delete:
      operationId: deleteProfile
      tags:
      - Profiles
      summary: Delete a profile
      responses:
        '204':
          description: Deleted. No response body.
        '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
    CreateProfileRequest:
      type: object
      properties:
        department:
          allOf:
          - $ref: '#/components/schemas/Department'
          description: The department the user primarily shops.
        alias:
          type: string
          description: A label for the profile, shown to the user. Not required to be unique.
          example: Primary
    UpdateProfileRequest:
      type: object
      description: Every field is optional; only the fields you send are changed.
      properties:
        department:
          allOf:
          - $ref: '#/components/schemas/Department'
        alias:
          type: string
          example: Weekend
        registeredAt:
          type: string
          format: date-time
          description: When the user registered with you. Set once when the user creates an account.
        savedAt:
          type: string
          format: date-time
          description: When the user last explicitly saved the profile.
    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
    Profile:
      type: object
      required:
      - profileId
      - isActive
      - estimatedDob
      - originStore
      - originLocale
      - createdAt
      - updatedAt
      properties:
        profileId:
          type: string
          format: uuid
        alias:
          type: string
          example: Primary
        department:
          allOf:
          - $ref: '#/components/schemas/Department'
        userId:
          type: string
          description: True Fit's internal user identifier. Not required for any partner call.
        isActive:
          type: boolean
          description: 'Whether this is the active profile in a browser session. Always `false` for partner

            requests, which carry no browser session - ignore it.

            '
        estimatedDob:
          type:
          - string
          - 'null'
          format: date-time
          description: Date of birth inferred from the profile's age measurement, when available.
        originStore:
          type:
          - string
          - 'null'
          description: 'Where the profile was created. Profiles you create through this API carry your

            `partnerId`.

            '
          example: acme
        originLocale:
          type:
          - string
          - 'null'
          example: en_US
        registeredAt:
          type:
          - string
          - 'null'
          format: date-time
        savedAt:
          type:
          - string
          - 'null'
          format: date-time
        createdAt:
          type:
          - string
          - 'null'
          format: date-time
        updatedAt:
          type:
          - string
          - 'null'
          format: date-time
    Department:
      type: string
      description: 'Garment department. Matched case-insensitively - `Womens` and `womens` are both accepted

        - and stored lowercased.

        '
      enum:
      - baby
      - boys
      - donotrecommend
      - girls
      - maternity
      - mens
      - not apparel
      - other
      - unisexkids
      - womens
      - unisex
      example: womens
  parameters:
    partnerId:
      name: partnerId
      in: path
      required: true
      description: Your partner identifier, issued during onboarding.
      schema:
        type: string
      example: acme
    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
    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
  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"'
    MissingUserIdentifier:
      description: Neither `partnerUserId` nor `tfPartnerUserId` was supplied.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missingIdentifier:
              value:
                statusCode: 400
                message: tfPartnerUserId or partnerUserId query parameter is required
                error: Bad Request
  securitySchemes:
    partnerApiKey:
      type: http
      scheme: basic
      description: 'Empty username, partner API key as the password:

        `Authorization: Basic <base64(":" + apiKey)>`.

        '