> ## Documentation Index
> Fetch the complete documentation index at: https://docs.virtual.fit/llms.txt
> Use this file to discover all available pages before exploring further.

# Sync product reviews

> Optionally sync complete customer review records and fit evidence.

Review sync is optional. Upload the product first, then send review batches
whenever your review source changes.

Prefer a stable `external_review_id`. A public `permalink` is the next-best
identity. If neither exists, virtual.fit derives a conservative identity from
the available author, date, rating, title, and body.

Each item is its complete current review record. Include its current stars,
text, purchased variant, and media on every update. To delete one review, send
its stable ID or permalink with `deleted: true`.

Use `snapshot_complete: true` only when the batch contains every current review
in the named `partition_key`. Omitted reviews become inactive after two
consecutive complete snapshots. Leave it `false` for pages, partial feeds, and
incremental updates.

<Note>
  virtual.fit extracts fit evidence from review text asynchronously. You only
  need to send `fit_signals` when the source already provides an explicit
  structured vote such as “runs small” or “true to size.”
</Note>


## OpenAPI

````yaml openapi.yaml POST /vfr/manage/sites/{site_id}/products/{product_id}/reviews
openapi: 3.1.0
info:
  title: virtual.fit API
  version: 2.0.0
  description: Catalog sync, storefront fit evidence, and product recommendation endpoints.
servers:
  - url: https://api.virtual.fit
security:
  - bearerAuth: []
tags:
  - name: Catalog
  - name: Storefront evidence
  - name: Recommendations
paths:
  /vfr/manage/sites/{site_id}/products/{product_id}/reviews:
    post:
      tags:
        - Catalog
      summary: Sync optional product reviews
      description: |
        Adds or updates complete review records after the product exists.
        Review enrichment is optional and does not block product readiness.
      operationId: syncCatalogProductReviews
      parameters:
        - $ref: '#/components/parameters/SiteID'
        - $ref: '#/components/parameters/ProductID'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CatalogReviewBatch'
      responses:
        '200':
          description: Review batch accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CatalogReviewBatchResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/TooLarge'
components:
  parameters:
    SiteID:
      name: site_id
      in: path
      required: true
      description: Custom JavaScript site ID from virtual.fit.
      schema:
        type: string
        format: uuid
    ProductID:
      name: product_id
      in: path
      required: true
      description: >
        Stable, case-sensitive identity that remains unchanged when the
        product's

        title, URL, options, variants, price, or media change. URL-encode it in

        the request path.
      schema:
        type: string
        maxLength: 255
  schemas:
    CatalogReviewBatch:
      type: object
      required:
        - event_id
        - provider
        - reviews
      properties:
        event_id:
          type: string
          maxLength: 255
          description: Unique idempotency key. Reusing it with different data is rejected.
        provider:
          type: string
          maxLength: 255
          description: Stable lowercase key for the review source.
        partition_key:
          type: string
          maxLength: 255
          default: product_reviews
        snapshot_complete:
          type: boolean
          default: false
          description: >-
            True only when this batch is a complete view of the exact provider,
            product, and partition.
        observed_at:
          type: string
          format: date-time
        reviews:
          type: array
          maxItems: 250
          items:
            $ref: '#/components/schemas/CatalogReview'
    CatalogReviewBatchResult:
      type: object
      required:
        - accepted_count
        - created_count
        - updated_count
        - versioned_count
        - deactivated_count
        - replay
      properties:
        accepted_count:
          type: integer
        created_count:
          type: integer
        updated_count:
          type: integer
        versioned_count:
          type: integer
        deactivated_count:
          type: integer
        replay:
          type: boolean
    CatalogReview:
      type: object
      properties:
        external_review_id:
          type: string
          maxLength: 255
          description: Preferred stable identity from the review source.
        external_id_scope:
          type: string
          maxLength: 255
        permalink:
          type: string
          format: uri
          maxLength: 8192
        title:
          type: string
          maxLength: 500
        body:
          type: string
          maxLength: 65536
        rating:
          type: number
          minimum: 0
          maximum: 5
        language:
          type: string
          maxLength: 255
        locale:
          type: string
          maxLength: 255
        author_display_name:
          type: string
          maxLength: 255
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        verified_purchase:
          type: boolean
        incentivized:
          type: boolean
        badges:
          type: array
          items:
            type: string
        helpful_count:
          type: integer
          minimum: 0
        merchant_response:
          type: object
          additionalProperties: true
        purchased_variant:
          $ref: '#/components/schemas/CatalogReviewVariant'
        media:
          type: array
          maxItems: 12
          items:
            $ref: '#/components/schemas/CatalogReviewMedia'
        measurements:
          type: array
          maxItems: 6
          items:
            $ref: '#/components/schemas/CatalogReviewMeasurementSet'
        fit_signals:
          type: array
          maxItems: 24
          items:
            $ref: '#/components/schemas/CatalogReviewFitSignal'
        deleted:
          type: boolean
          default: false
          description: >-
            Deletes the review association. Requires a stable review ID or
            permalink.
      anyOf:
        - required:
            - external_review_id
        - required:
            - permalink
        - required:
            - title
        - required:
            - body
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
    CatalogReviewVariant:
      type: object
      properties:
        source_variant_id:
          type: string
          maxLength: 255
        sku:
          type: string
          maxLength: 255
        label:
          type: string
          maxLength: 255
        option_values:
          type: object
          maxProperties: 20
          additionalProperties:
            type: string
        size_signature:
          type: string
          maxLength: 255
      description: >-
        Send any identifiers available for the purchased variant. Unresolved
        source values are preserved.
    CatalogReviewMedia:
      type: object
      required:
        - url
      properties:
        external_media_id:
          type: string
          maxLength: 255
        url:
          type: string
          format: uri
          maxLength: 8192
        kind:
          type: string
          enum:
            - image
            - video
          default: image
        caption:
          type: string
        alt_text:
          type: string
        width:
          type: integer
          minimum: 0
        height:
          type: integer
          minimum: 0
        position:
          type: integer
          minimum: 0
    CatalogReviewMeasurementSet:
      type: object
      properties:
        subject_kind:
          type: string
          enum:
            - reviewer
          default: reviewer
        basis:
          type: string
          enum:
            - body
            - unknown
          default: unknown
        source_key:
          type: string
        source_kind:
          type: string
          default: public_review
        capture_method:
          type: string
        size_label:
          type: string
        option_selector:
          type: object
          additionalProperties:
            type: string
        confidence:
          type: number
          minimum: 0
          maximum: 1
        values:
          type: array
          maxItems: 32
          items:
            $ref: '#/components/schemas/CatalogMeasurementValue'
    CatalogReviewFitSignal:
      type: object
      required:
        - dimension_code
        - score
        - confidence
        - evidence
      properties:
        dimension_code:
          type: string
          enum:
            - overall
            - bust
            - chest
            - underbust
            - waist
            - hips
            - shoulder_width
            - sleeve_length
            - total_length
            - inseam
            - front_rise
            - back_rise
            - thigh
            - leg_opening
            - foot_width
        score:
          type: integer
          minimum: -2
          maximum: 2
          description: >-
            Negative means tighter, smaller, or shorter. Positive means looser,
            larger, or longer.
        confidence:
          type: number
          minimum: 0
          maximum: 1
        evidence:
          type: string
          maxLength: 4096
        source_kind:
          type: string
          enum:
            - structured
            - deterministic
            - llm
            - vlm
            - manual
          default: structured
    CatalogMeasurementValue:
      type: object
      required:
        - dimension_code
        - unit
      properties:
        dimension_code:
          type: string
          enum:
            - height
            - weight
            - bust
            - chest
            - underbust
            - waist
            - hips
            - shoulder_width
            - arm_length
            - sleeve_length
            - total_length
            - inseam
            - front_rise
            - back_rise
            - thigh
            - leg_opening
            - foot_length
            - foot_width
        value:
          type: number
          minimum: 0
        min_value:
          type: number
          minimum: 0
        max_value:
          type: number
          minimum: 0
        unit:
          type: string
          enum:
            - cm
            - kg
          description: Use kg for weight and cm for every length.
        raw_label:
          type: string
        raw_value:
          type: string
        raw_unit:
          type: string
        confidence:
          type: number
          minimum: 0
          maximum: 1
      anyOf:
        - required:
            - value
        - required:
            - min_value
        - required:
            - max_value
  responses:
    BadRequest:
      description: Invalid field or inconsistent catalog data.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: The API-key account cannot modify this site.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Site, product, variant, or recommendation context not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooLarge:
      description: Request body, file, field, or collection exceeds its documented limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Keep the virtual.fit API key on your server.

````