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

# Bulk-add exercises to a workout v2 session

> Assisted plan slots use nonnegative kilograms of assistance and do not contribute lifted-load volume or estimated 1RM. Adds multiple exercise performance records to a session in a single request. All exercise IDs must be unique within the request and must not already exist in the session. If any exercise in the batch already exists the entire request is rejected with 409. Exercise type is resolved from the provided sets; if no metrics are supplied and a `planExerciseId` is given the type is resolved from the plan exercise definition.

Requires the workout_sessions:write scope. This operation maps to /app/v1/workout/performance/sessions/:sessionId/exercises/bulk and retains its Workout V2 permission, feature-flag, and resource-scope checks.

The clientId path parameter is resolved inside the company bound to the Public API token when present.



## OpenAPI

````yaml /openapi/public-v1.json post /public/v1/workout/performance/sessions/{sessionId}/exercises/bulk
openapi: 3.1.0
info:
  title: FITsociety Public API v1
  version: 1.0.0
  description: >-
    Developer Public API endpoints under `/public/v1`. This reference is
    filtered to OAuth/Bearer Public API resources and excludes provider callback
    receivers, storefront routes, public widgets, wishlist routes, and
    access-device validation endpoints.
  contact:
    name: FITsociety Engineering
servers:
  - url: https://api.fitsociety.io
    description: Production
security: []
tags:
  - name: Workout
    description: >-
      Workout V2 libraries, programmes, client plans, calendars, sessions,
      groups, settings and progress. AI operations are excluded.
  - name: OAuth
    description: Public API OAuth endpoints for server-to-server client credentials.
  - name: Health
    description: Public API token health checks.
  - name: Platform
    description: >-
      Inspect Public API client context, capabilities, scopes, and redacted
      audit logs.
  - name: Company Catalog
    description: Read and manage company profile metadata and locations.
  - name: Clients
    description: >-
      Create and manage clients through the Public API using Bearer access
      tokens.
  - name: Coaches
    description: Retrieve coaches for the authenticated company via integrations.
  - name: Calendar Events
    description: Read Public API calendar events.
  - name: Calendar Templates
    description: >-
      Read and manage event types and event templates used by calendar
      availability and bookings.
  - name: Calendar Extensions
    description: >-
      Read recurring bookings, booking requests, calendar tasks, and
      availability closure metadata.
  - name: Availability
    description: Read bookable availability slots and signed availability tokens.
  - name: Availability Management
    description: >-
      Manage coach and location availability templates used to derive bookable
      slots.
  - name: Bookings
    description: Read and manage Public API bookings.
  - name: Finance
    description: >-
      Read invoices, transactions, products, subscriptions, and memberships with
      guarded finance writes.
  - name: Credits
    description: >-
      Read company-wide and client-scoped credit allocations, mutations, and
      guarded credit adjustments.
  - name: Exports
    description: >-
      Create and monitor asynchronous company exports through Public API Bearer
      endpoints.
  - name: Webhooks
    description: >-
      Manage outbound webhook subscriptions and inspect delivery attempts
      through Public API Bearer endpoints.
  - name: Measurements
    description: Read and write client measurement entries.
  - name: Progress Photos
    description: Read client progress photo metadata and short-lived signed media URLs.
  - name: Forms
    description: Read, create, update, and archive company form templates.
  - name: Intakes
    description: Assign intake forms and read client intake assignments and submissions.
  - name: Check-ups
    description: >-
      Schedule and cancel client check-ups and read their status and
      submissions.
  - name: Documents
    description: >-
      Read client document and folder metadata, register external document
      links, update metadata, and archive documents from Public API listings.
      Binary upload, permanent deletion, and company-wide document management
      are not exposed.
  - name: Habits
    description: Read habits and habit entries.
  - name: Goals
    description: Read client goal summaries.
  - name: Conversations
    description: >-
      Read and manage direct and group chat conversations through the Public
      API.
  - name: Reports
    description: Read aggregate attendance, revenue, and retention summaries.
paths:
  /public/v1/workout/performance/sessions/{sessionId}/exercises/bulk:
    post:
      tags:
        - Workout
      summary: Bulk-add exercises to a workout v2 session
      description: >-
        Assisted plan slots use nonnegative kilograms of assistance and do not
        contribute lifted-load volume or estimated 1RM. Adds multiple exercise
        performance records to a session in a single request. All exercise IDs
        must be unique within the request and must not already exist in the
        session. If any exercise in the batch already exists the entire request
        is rejected with 409. Exercise type is resolved from the provided sets;
        if no metrics are supplied and a `planExerciseId` is given the type is
        resolved from the plan exercise definition.


        Requires the workout_sessions:write scope. This operation maps to
        /app/v1/workout/performance/sessions/:sessionId/exercises/bulk and
        retains its Workout V2 permission, feature-flag, and resource-scope
        checks.


        The clientId path parameter is resolved inside the company bound to the
        Public API token when present.
      operationId: >-
        publicWorkoutpostPublicV1WorkoutPerformanceSessionsSessionIdExercisesBulk
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 200
            example: booking-create-20260714-001
          description: >-
            Required for Public API write requests. Reusing the same key with
            the same method, path, and body replays the stored successful
            response; reusing it with a different request returns `409
            idempotency.conflict`.
        - in: path
          name: sessionId
          required: true
          description: Workout session id.
          schema:
            type: string
            example: 67f1234567890abcdef1234
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: >-
                #/components/schemas/PublicWorkoutSourcePOSTAppV1WorkoutPerformanceSessionsSessionIdExercisesBulkRequest
      responses:
        '201':
          description: Array of created performance records with totals and sets.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PublicApiWriteSuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: >-
                          #/components/schemas/PublicWorkoutSourcePOSTAppV1WorkoutPerformanceSessionsSessionIdExercisesBulkResponse201
              examples:
                success:
                  summary: Successful response
                  value:
                    data:
                      data:
                        - id: 67f1234567890abcdef1234
                          exerciseId: 67f1234567890abcdef1234
                          planExerciseId: null
                          order: 0
                          isCompleted: false
                          date: '2026-04-12T10:00:00.000Z'
                          startedAt: null
                          endedAt: null
                          totals:
                            totalReps: 24
                            totalTimeSeconds: 0
                            totalDistanceMeters: 0
                            totalVolume: 1680
                            maxWeight: 75
                            estimated1RM: 95
                            averageRPE: 8.2
                          sets:
                            - setNumber: 1
                              setType: 'N'
                              displayNumber: 1
                              displayLabel: '1'
                              targetReps: 8-12
                              targetWeight: 60
                              targetTimeSeconds: null
                              targetDistanceMeters: null
                              targetRestSeconds: 90
                              weight: 70
                              reps: 8
                              timeSeconds: null
                              distanceMeters: null
                              restSeconds: 120
                              rpe: 8
                              completed: true
                              notes: Felt strong.
                              isPersonalBest: false
                              repsPlaceholder: 22
                              weightPlaceholder: 30
                              timeSecondsPlaceholder: 55
                              distanceMetersPlaceholder: 40
                              rpePlaceholder: 3
                              restSecondsPlaceholder: 60
                              repsActual: 30
                              weightActual: 42
                              timeSecondsActual: null
                              distanceMetersActual: null
                              rpeActual: 8
                              restSecondsActual: 90
                          notes: Last set was slow.
                          nextSessionTarget:
                            reps: 5-8
                            weight: 80
                            weightUnit: kg
                            timeSeconds: null
                            distanceMeters: null
                            restSeconds: 120
                            rpe: 8
                            notes: Control the descent.
                            sets:
                              - {}
                          targetSnapshot:
                            exerciseType: reps
                            todayTarget:
                              reps: 5-8
                              weight: 80
                              weightUnit: kg
                              timeSeconds: null
                              distanceMeters: null
                              restSeconds: 120
                              rpe: 8
                              notes: Control the descent.
                              sets:
                                - {}
                            lastSessionTarget:
                              reps: 5-8
                              weight: 80
                              weightUnit: kg
                              timeSeconds: null
                              distanceMeters: null
                              restSeconds: 120
                              rpe: 8
                              notes: Control the descent.
                              sets:
                                - {}
                            personalRecord:
                              metric: weight
                              value: 82.5
                              unit: kg
                              reps: 6
                              performedAt: null
                              sessionId: null
                              performanceId: null
                            todaySetupValues: {}
                            lastSetupValues: {}
                            sourceSessionId: null
                            sourcePerformanceId: null
                          setupFields:
                            - key: seat
                              label: Seat
                              type: text
                              unit: ''
                              options:
                                - '1'
                                - '2'
                                - '3'
                                - '4'
                              order: 1
                              required: false
                              enabled: true
                              defaultValue: 3
                          setupValues: {}
                          nextSetupValues: {}
                          showSetRpe: true
                          hasMachineSettings: true
                          setupSettingsEnabled: true
                          isMachineSetting: true
                          provenance:
                            recordedByActorType: coach
                            recordedByClientId: null
                            recordedByCoachId: null
                            lastUpdatedByActorType: coach
                            lastUpdatedByClientId: null
                            lastUpdatedByCoachId: null
                          addedSets:
                            - setNumber: 1
                              setType: 'N'
                              displayNumber: 1
                              displayLabel: '1'
                              previous: 12 × 22kg
                              reps: 8
                              weight: 50
                              timeSeconds: null
                              distanceMeters: null
                              rpe: 8
                              rest: 60
                              restSeconds: 60
                              completed: true
                              targetReps: '8'
                              targetWeight: 50
                              targetTimeSeconds: null
                              targetDistanceMeters: null
                              targetRestSeconds: 60
                              repsPlaceholderCoach: 12
                              weightPlaceholderCoach: 40
                              timeSecondsPlaceholderCoach: null
                              distanceMetersPlaceholderCoach: null
                              rpePlaceholderCoach: 7
                              restSecondsPlaceholderCoach: 90
                              repsPlaceholder: 12
                              weightPlaceholder: 40
                              timeSecondsPlaceholder: null
                              distanceMetersPlaceholder: null
                              rpePlaceholder: 7
                              restSecondsPlaceholder: 90
                              repsActual: 30
                              weightActual: 42
                              timeSecondsActual: null
                              distanceMetersActual: null
                              rpeActual: 8
                              restSecondsActual: 90
                      count: 3
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                      idempotency:
                        replayed: false
        '400':
          description: >-
            Invalid session ID, missing or empty exercises array, duplicate
            exercise IDs in the request, or invalid exercise ID at a given
            index.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                invalidRequest:
                  summary: Invalid request
                  value:
                    error:
                      code: 400
                      key: request.invalid
                      message: The request is invalid.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                idempotencyRequired:
                  summary: Missing Idempotency-Key
                  value:
                    error:
                      code: 400
                      key: idempotency.required
                      message: >-
                        Idempotency-Key header is required for Public API write
                        requests.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                idempotencyInvalid:
                  summary: Invalid Idempotency-Key
                  value:
                    error:
                      code: 400
                      key: idempotency.invalid
                      message: Idempotency-Key header must be 200 characters or fewer.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '401':
          $ref: '#/components/schemas/ErrorResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                missingBearer:
                  summary: Missing Bearer token
                  value:
                    error:
                      code: 401
                      key: auth.missing_bearer
                      message: Authorization Bearer token is required.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                invalidToken:
                  summary: Invalid or expired token
                  value:
                    error:
                      code: 401
                      key: auth.invalid_token
                      message: The access token is invalid or expired.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                invalidClient:
                  summary: Inactive or revoked client
                  value:
                    error:
                      code: 401
                      key: auth.invalid_client
                      message: The Public API client is inactive or revoked.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '403':
          $ref: '#/components/schemas/ErrorResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                insufficientScopes:
                  summary: Missing required scope
                  value:
                    error:
                      code: 403
                      key: scopes.insufficient
                      message: The access token does not include the required scope.
                      details:
                        requiredScopes:
                          - required:scope
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '404':
          description: Session not found in the caller's scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                notFound:
                  summary: Resource not found
                  value:
                    error:
                      code: 404
                      key: resource.not_found
                      message: The requested Public API resource was not found.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '409':
          description: >-
            Session is awaiting confirmation, or one or more exercises already
            have a performance record in this session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                idempotencyConflict:
                  summary: Idempotency-Key conflict
                  value:
                    error:
                      code: 409
                      key: idempotency.conflict
                      message: >-
                        Idempotency-Key was already used with a different
                        request.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                idempotencyInProgress:
                  summary: Idempotency-Key in progress
                  value:
                    error:
                      code: 409
                      key: idempotency.in_progress
                      message: >-
                        Idempotency-Key is already processing for this Public
                        API client.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '422':
          $ref: '#/components/schemas/ErrorResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                emptyBody:
                  summary: Empty or invalid JSON object body
                  value:
                    error:
                      code: 422
                      key: EMPTY_BODY
                      message: Request body must be a non-empty JSON object.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '429':
          $ref: '#/components/schemas/ErrorResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                rateLimitExceeded:
                  summary: Rate limit exceeded
                  value:
                    error:
                      code: 429
                      key: rate_limit.exceeded
                      message: Too many Public API requests. Retry later.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 0
                        resetSeconds: 1
                        retryAfterSeconds: 1
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                serverInternalError:
                  summary: Unexpected server error
                  value:
                    error:
                      code: 500
                      key: server.internal_error
                      message: An unexpected Public API server error occurred.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
      security:
        - PublicBearerAuth: []
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: >-
            curl -X POST
            "https://api.fitsociety.io/public/v1/workout/performance/sessions/{sessionId}/exercises/bulk"
            \
              -H "Authorization: Bearer <access_token>" \
              -H "Idempotency-Key: <stable_request_key>" \
              -H "Content-Type: application/json" \
              -d '{}'
components:
  schemas:
    PublicWorkoutSourcePOSTAppV1WorkoutPerformanceSessionsSessionIdExercisesBulkRequest:
      type: object
      required:
        - exercises
      properties:
        exercises:
          type: array
          minItems: 1
          description: Array of exercise performance objects to create.
          items:
            type: object
            required:
              - exerciseId
            properties:
              exerciseId:
                type: string
                example: 67f1234567890abcdef1234
              sets:
                type: array
                items:
                  $ref: >-
                    #/components/schemas/PublicWorkoutSourceWorkoutPerformanceSet
              notes:
                type: string
                example: Felt strong.
              isCompleted:
                type: boolean
                example: false
              planId:
                type: string
                example: 67f1234567890abcdef1234
              planMomentId:
                type: string
                example: 67f1234567890abcdef1234
              planExerciseId:
                type: string
                example: 67f1234567890abcdef1234
              startedAt:
                type: string
                format: date-time
                example: '2026-04-12T10:00:00.000Z'
              endedAt:
                type: string
                format: date-time
                example: '2026-04-12T10:00:00.000Z'
              date:
                type: string
                format: date-time
                example: '2026-04-12T10:00:00.000Z'
              order:
                type: number
                example: 0
              nextSessionTarget:
                $ref: '#/components/schemas/PublicWorkoutSourceWorkoutExerciseTarget'
              setupFields:
                type: array
                maxItems: 8
                items:
                  $ref: >-
                    #/components/schemas/PublicWorkoutSourceWorkoutExerciseSetupField
              setupValues:
                type: object
                additionalProperties: true
              nextSetupValues:
                type: object
                additionalProperties: true
    PublicApiWriteSuccessResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          type: object
        meta:
          $ref: '#/components/schemas/PublicApiWriteMeta'
    PublicWorkoutSourcePOSTAppV1WorkoutPerformanceSessionsSessionIdExercisesBulkResponse201:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                example: 67f1234567890abcdef1234
              exerciseId:
                type: string
                example: 67f1234567890abcdef1234
              planExerciseId:
                type:
                  - string
                  - 'null'
                example: null
              order:
                type: number
                example: 0
              isCompleted:
                type: boolean
                example: false
              date:
                type: string
                format: date-time
                example: '2026-04-12T10:00:00.000Z'
              startedAt:
                type:
                  - string
                  - 'null'
                format: date-time
                example: null
              endedAt:
                type:
                  - string
                  - 'null'
                format: date-time
                example: null
              totals:
                type: object
                properties:
                  totalReps:
                    type: number
                    example: 24
                  totalTimeSeconds:
                    type: number
                    example: 0
                  totalDistanceMeters:
                    type: number
                    example: 0
                  totalVolume:
                    type: number
                    example: 1680
                  maxWeight:
                    type: number
                    example: 75
                  estimated1RM:
                    type: number
                    example: 95
                  averageRPE:
                    type: number
                    example: 8.2
              sets:
                type: array
                items:
                  type: object
                  required:
                    - setNumber
                  properties:
                    setNumber:
                      type: integer
                      minimum: 1
                      example: 1
                    setType:
                      type: string
                      enum:
                        - 'N'
                        - W
                        - D
                        - F
                      example: 'N'
                      description: >-
                        Planned or performed set type: N normal, W warm-up, D
                        drop set, F to failure.
                    displayNumber:
                      type:
                        - integer
                        - 'null'
                      example: 1
                      description: >-
                        Sequential number for normal sets only. Warm-up, drop
                        set, and failure sets return null.
                    displayLabel:
                      type: string
                      example: '1'
                      description: >-
                        Backend-ready display string: normal sets use their
                        sequential number, other set types use W, D, or F.
                    targetReps:
                      type: string
                      example: 8-12
                    targetWeight:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      example: 60
                    targetTimeSeconds:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                    targetDistanceMeters:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                    targetRestSeconds:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 90
                    weight:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      example: 70
                    reps:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 8
                    timeSeconds:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                    distanceMeters:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                    restSeconds:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 120
                    rpe:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      maximum: 10
                      example: 8
                    completed:
                      type: boolean
                      example: true
                    notes:
                      type: string
                      example: Felt strong.
                    isPersonalBest:
                      type: boolean
                      example: false
                    repsPlaceholder:
                      type:
                        - number
                        - string
                        - 'null'
                      example: 22
                      description: >-
                        The client's OWN greyed hint for an empty reps input on
                        this set — accepted on upsert and stored as sent. A
                        range string (`"8-12"`) is allowed. It is a FALLBACK,
                        not an override: the coach's `repsPlaceholderCoach` wins
                        wherever it exists, so this value is only used for sets
                        the coach never prescribed (per-set tracking off, extra
                        sets, metrics left blank). Sending it for a prescribed
                        set is accepted but has no effect. **Never logged
                        work**: placeholders are excluded from every total,
                        volume, PR and completion check, so leaving a hint after
                        clearing a value does not make the set count. To COMMIT
                        a hint the athlete accepted, send it as the real `reps`
                        value with `completed: true` — but never submit a RANGE
                        on a completed set, which is stored as its minimum
                        (`"12-24"` → 12) and under-credits the athlete.
                    weightPlaceholder:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      example: 30
                      description: >-
                        The client's own hint for an empty weight input. See
                        `repsPlaceholder`.
                    timeSecondsPlaceholder:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 55
                      description: >-
                        The client's own hint for an empty time input. See
                        `repsPlaceholder`.
                    distanceMetersPlaceholder:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 40
                      description: >-
                        The client's own hint for an empty distance input. See
                        `repsPlaceholder`.
                    rpePlaceholder:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      maximum: 10
                      example: 3
                      description: >-
                        The client's own hint for an empty RPE input. See
                        `repsPlaceholder`.
                    restSecondsPlaceholder:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 60
                      description: >-
                        The client's own hint for an empty rest input. See
                        `repsPlaceholder`.
                    repsActual:
                      type:
                        - number
                        - string
                        - 'null'
                      example: 30
                      description: >-
                        The athlete's ACTUALLY-logged reps for this set (mirrors
                        `reps`), or `null` when they have not logged it — the
                        visible `reps` may be a coach-prescription prefill.
                        Additive: `reps` is unchanged.
                    weightActual:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      example: 42
                      description: >-
                        Actually-logged weight (mirrors `weight`), or `null` if
                        not logged. See `repsActual`.
                    timeSecondsActual:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                      description: >-
                        Actually-logged time (mirrors `timeSeconds`), or `null`
                        if not logged. See `repsActual`.
                    distanceMetersActual:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                      description: >-
                        Actually-logged distance (mirrors `distanceMeters`), or
                        `null` if not logged. See `repsActual`.
                    rpeActual:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      maximum: 10
                      example: 8
                      description: >-
                        Actually-logged RPE (mirrors `rpe`), or `null` if not
                        logged or RPE is hidden. See `repsActual`.
                    restSecondsActual:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 90
                      description: >-
                        Actually-logged rest in seconds (mirrors `restSeconds`),
                        or `null` if not logged. See `repsActual`.
              notes:
                type: string
                example: Last set was slow.
              nextSessionTarget:
                type: object
                properties:
                  reps:
                    type: string
                    example: 5-8
                  weight:
                    type:
                      - number
                      - 'null'
                    example: 80
                  weightUnit:
                    type: string
                    example: kg
                  timeSeconds:
                    type:
                      - number
                      - 'null'
                    example: null
                  distanceMeters:
                    type:
                      - number
                      - 'null'
                    example: null
                  restSeconds:
                    type:
                      - number
                      - 'null'
                    example: 120
                  rpe:
                    type:
                      - number
                      - 'null'
                    example: 8
                  notes:
                    type: string
                    example: Control the descent.
                  sets:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
              targetSnapshot:
                oneOf:
                  - type: object
                    description: >-
                      The prescription and tracking type captured for this
                      execution, independent of later plan edits.
                    properties:
                      exerciseType:
                        type: string
                        enum:
                          - reps
                          - time
                          - distance
                          - bodyweight
                          - weighted_bodyweight
                          - assisted_bodyweight
                          - mixed
                        description: >-
                          Original execution type. Legacy records capture this
                          before the linked prescription changes.
                      todayTarget:
                        type: object
                        properties:
                          reps:
                            type: string
                            example: 5-8
                          weight:
                            type:
                              - number
                              - 'null'
                            example: 80
                          weightUnit:
                            type: string
                            example: kg
                          timeSeconds:
                            type:
                              - number
                              - 'null'
                            example: null
                          distanceMeters:
                            type:
                              - number
                              - 'null'
                            example: null
                          restSeconds:
                            type:
                              - number
                              - 'null'
                            example: 120
                          rpe:
                            type:
                              - number
                              - 'null'
                            example: 8
                          notes:
                            type: string
                            example: Control the descent.
                          sets:
                            type: array
                            items:
                              type: object
                              additionalProperties: true
                      lastSessionTarget:
                        oneOf:
                          - type: object
                            properties:
                              reps:
                                type: string
                                example: 5-8
                              weight:
                                type:
                                  - number
                                  - 'null'
                                example: 80
                              weightUnit:
                                type: string
                                example: kg
                              timeSeconds:
                                type:
                                  - number
                                  - 'null'
                                example: null
                              distanceMeters:
                                type:
                                  - number
                                  - 'null'
                                example: null
                              restSeconds:
                                type:
                                  - number
                                  - 'null'
                                example: 120
                              rpe:
                                type:
                                  - number
                                  - 'null'
                                example: 8
                              notes:
                                type: string
                                example: Control the descent.
                              sets:
                                type: array
                                items:
                                  type: object
                                  additionalProperties: true
                          - type: 'null'
                      personalRecord:
                        type:
                          - object
                          - 'null'
                        description: >-
                          For assisted_bodyweight, metric weight is kilograms of
                          assistance: lower is better, equal assistance compares
                          higher reps, and zero is valid. Only completed sets
                          with finite nonnegative assistance and positive reps
                          qualify. Assistance is never lifted-load volume or
                          estimated 1RM.
                        properties:
                          metric:
                            type: string
                            enum:
                              - weight
                              - volume
                              - reps
                              - time
                              - distance
                              - ''
                            example: weight
                          value:
                            type:
                              - number
                              - 'null'
                            example: 82.5
                          unit:
                            type: string
                            example: kg
                          reps:
                            type:
                              - number
                              - 'null'
                            example: 6
                          performedAt:
                            type:
                              - string
                              - 'null'
                            format: date-time
                            example: null
                          sessionId:
                            type:
                              - string
                              - 'null'
                            example: null
                          performanceId:
                            type:
                              - string
                              - 'null'
                            example: null
                      todaySetupValues:
                        type: object
                        additionalProperties: true
                      lastSetupValues:
                        type: object
                        additionalProperties: true
                      sourceSessionId:
                        type:
                          - string
                          - 'null'
                        example: null
                      sourcePerformanceId:
                        type:
                          - string
                          - 'null'
                        example: null
                  - type: 'null'
              setupFields:
                type: array
                maxItems: 8
                items:
                  type: object
                  required:
                    - key
                    - label
                  properties:
                    key:
                      type: string
                      example: seat
                    label:
                      type: string
                      example: Seat
                    type:
                      type: string
                      enum:
                        - text
                        - number
                        - select
                      example: text
                    unit:
                      type: string
                      example: ''
                    options:
                      type: array
                      items:
                        type: string
                      example:
                        - '1'
                        - '2'
                        - '3'
                        - '4'
                    order:
                      type: number
                      example: 1
                    required:
                      type: boolean
                      example: false
                    enabled:
                      type: boolean
                      example: true
                    defaultValue:
                      oneOf:
                        - type: string
                        - type: number
                      example: 3
              setupValues:
                type: object
                additionalProperties: true
              nextSetupValues:
                type: object
                additionalProperties: true
              showSetRpe:
                type: boolean
                example: true
                description: >-
                  Resolved RPE-input visibility. Freestyle performances inherit
                  the session setting; planned performances inherit their
                  plan-exercise setting.
              hasMachineSettings:
                type: boolean
                example: true
                description: >-
                  True when the performance has at least one setupField
                  configured.
              setupSettingsEnabled:
                type: boolean
                example: true
                description: >-
                  Explicit machine-settings toggle for this performance. If
                  unset, the backend falls back to resolved setup field
                  availability.
              isMachineSetting:
                type: boolean
                example: true
                description: >-
                  Alias of hasMachineSettings for clients that use the machine
                  setting flag name.
              provenance:
                type: object
                properties:
                  recordedByActorType:
                    type: string
                    enum:
                      - ''
                      - client
                      - coach
                      - system
                    example: coach
                  recordedByClientId:
                    type:
                      - string
                      - 'null'
                    example: null
                  recordedByCoachId:
                    type:
                      - string
                      - 'null'
                    example: null
                  lastUpdatedByActorType:
                    type: string
                    enum:
                      - ''
                      - client
                      - coach
                      - system
                    example: coach
                  lastUpdatedByClientId:
                    type:
                      - string
                      - 'null'
                    example: null
                  lastUpdatedByCoachId:
                    type:
                      - string
                      - 'null'
                    example: null
              addedSets:
                type: array
                description: >-
                  The saved sets shaped EXACTLY as the day / WOD / freestyle GET
                  screens return them, so the client can re-render straight off
                  this response without re-fetching. Unlike `sets` above (the
                  stored rows), each entry also carries the per-set `previous`
                  column, the `target*` chips for a plan-linked save, and the
                  ten per-set placeholder fields.
                items:
                  type: object
                  properties:
                    setNumber:
                      type: integer
                      example: 1
                    setType:
                      type: string
                      enum:
                        - 'N'
                        - W
                        - D
                        - F
                      example: 'N'
                      description: Normal / Warm-up / Drop / Failure.
                    displayNumber:
                      type:
                        - integer
                        - 'null'
                      example: 1
                      description: Sequential number for Normal sets only; null for W/D/F.
                    displayLabel:
                      type:
                        - string
                        - 'null'
                      example: '1'
                    previous:
                      type:
                        - string
                        - 'null'
                      example: 12 × 22kg
                      description: >-
                        The 'Previous' column for this set index, resolved
                        against the client's most recent COMPLETED session (this
                        session excluded).
                    reps:
                      type:
                        - number
                        - string
                        - 'null'
                      example: 8
                    weight:
                      type:
                        - number
                        - 'null'
                      example: 50
                    timeSeconds:
                      type:
                        - number
                        - 'null'
                      example: null
                    distanceMeters:
                      type:
                        - number
                        - 'null'
                      example: null
                    rpe:
                      type:
                        - number
                        - 'null'
                      example: 8
                    rest:
                      type:
                        - number
                        - 'null'
                      example: 60
                    restSeconds:
                      type:
                        - number
                        - 'null'
                      example: 60
                    completed:
                      type: boolean
                      example: true
                    targetReps:
                      type: string
                      example: '8'
                    targetWeight:
                      type:
                        - number
                        - 'null'
                      example: 50
                    targetTimeSeconds:
                      type:
                        - number
                        - 'null'
                      example: null
                    targetDistanceMeters:
                      type:
                        - number
                        - 'null'
                      example: null
                    targetRestSeconds:
                      type:
                        - number
                        - 'null'
                      example: 60
                    repsPlaceholderCoach:
                      type:
                        - number
                        - string
                        - 'null'
                      example: 12
                      description: >-
                        The greyed hint an EMPTY reps input shows, as prescribed
                        by the coach for this set index (WorkoutPlan
                        `setsData`). A reps range stays a string (`"8-12"`).
                        Never logged work: placeholders are excluded from every
                        total, volume, PR and completion check. `null` when this
                        exercise type does not track reps, when nothing is
                        prescribed for that set, and for a freestyle save, which
                        has no prescription at all.
                    weightPlaceholderCoach:
                      type:
                        - number
                        - 'null'
                      example: 40
                    timeSecondsPlaceholderCoach:
                      type:
                        - number
                        - 'null'
                      example: null
                    distanceMetersPlaceholderCoach:
                      type:
                        - number
                        - 'null'
                      example: null
                    rpePlaceholderCoach:
                      type:
                        - number
                        - 'null'
                      example: 7
                    restSecondsPlaceholderCoach:
                      type:
                        - number
                        - 'null'
                      example: 90
                      description: >-
                        The greyed hint an EMPTY rest input shows, as prescribed
                        by the coach for this set index (WorkoutPlan
                        `setsData`). Rest applies to every exercise type, so it
                        is not type-masked. Never logged work. `null` when
                        nothing is prescribed for that set, and for a freestyle
                        save, which has no prescription at all.
                    repsPlaceholder:
                      type:
                        - number
                        - string
                        - 'null'
                      example: 12
                      description: >-
                        The hint to ACTUALLY show. **The coach wins**: this is
                        the `*PlaceholderCoach` value beside it whenever there
                        is one, falling back to the client's own stored
                        placeholder only where the coach prescribed nothing
                        (per-set tracking off, an extra set, a metric left
                        blank, or any freestyle set). So it is identical to
                        `*PlaceholderCoach` on any prescribed set and always
                        reflects the CURRENT prescription. Emitted on logged
                        rows too, because clearing a logged value is exactly
                        when the hint has to reappear.
                    weightPlaceholder:
                      type:
                        - number
                        - 'null'
                      example: 40
                    timeSecondsPlaceholder:
                      type:
                        - number
                        - 'null'
                      example: null
                    distanceMetersPlaceholder:
                      type:
                        - number
                        - 'null'
                      example: null
                    rpePlaceholder:
                      type:
                        - number
                        - 'null'
                      example: 7
                    restSecondsPlaceholder:
                      type:
                        - number
                        - 'null'
                      example: 90
                    repsActual:
                      type:
                        - number
                        - string
                        - 'null'
                      example: 30
                      description: >-
                        The athlete's ACTUALLY-logged reps for this set (mirrors
                        `reps`), or `null` when they have not logged it — the
                        visible `reps` may be a coach-prescription prefill.
                        Additive: `reps` is unchanged.
                    weightActual:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      example: 42
                      description: >-
                        Actually-logged weight (mirrors `weight`), or `null` if
                        not logged. See `repsActual`.
                    timeSecondsActual:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                      description: >-
                        Actually-logged time (mirrors `timeSeconds`), or `null`
                        if not logged. See `repsActual`.
                    distanceMetersActual:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: null
                      description: >-
                        Actually-logged distance (mirrors `distanceMeters`), or
                        `null` if not logged. See `repsActual`.
                    rpeActual:
                      type:
                        - number
                        - 'null'
                      minimum: 0
                      maximum: 10
                      example: 8
                      description: >-
                        Actually-logged RPE (mirrors `rpe`), or `null` if not
                        logged or RPE is hidden. See `repsActual`.
                    restSecondsActual:
                      type:
                        - integer
                        - 'null'
                      minimum: 0
                      example: 90
                      description: >-
                        Actually-logged rest in seconds (mirrors `restSeconds`),
                        or `null` if not logged. See `repsActual`.
        count:
          type: integer
          example: 3
      required:
        - data
        - count
    PublicApiError:
      type: object
      additionalProperties: false
      required:
        - error
        - meta
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - key
            - message
          properties:
            code:
              type: integer
              example: 401
            key:
              type: string
              example: auth.invalid_token
            message:
              type: string
              example: The access token is invalid.
            details:
              type: object
              additionalProperties: true
        meta:
          $ref: '#/components/schemas/PublicApiMeta'
    ErrorResponse:
      allOf:
        - $ref: '#/components/schemas/StandardResponse'
        - example:
            status: 401
            error: true
            message: MISSING_AUTH
    PublicWorkoutSourceWorkoutPerformanceSet:
      type: object
      required:
        - setNumber
      properties:
        setNumber:
          type: integer
          minimum: 1
          example: 1
        setType:
          type: string
          enum:
            - 'N'
            - W
            - D
            - F
          example: 'N'
          description: >-
            Planned or performed set type: N normal, W warm-up, D drop set, F to
            failure.
        displayNumber:
          type:
            - integer
            - 'null'
          example: 1
          description: >-
            Sequential number for normal sets only. Warm-up, drop set, and
            failure sets return null.
        displayLabel:
          type: string
          example: '1'
          description: >-
            Backend-ready display string: normal sets use their sequential
            number, other set types use W, D, or F.
        targetReps:
          type: string
          example: 8-12
        targetWeight:
          type:
            - number
            - 'null'
          minimum: 0
          example: 60
        targetTimeSeconds:
          type:
            - integer
            - 'null'
          minimum: 0
          example: null
        targetDistanceMeters:
          type:
            - integer
            - 'null'
          minimum: 0
          example: null
        targetRestSeconds:
          type:
            - integer
            - 'null'
          minimum: 0
          example: 90
        weight:
          type:
            - number
            - 'null'
          minimum: 0
          example: 70
        reps:
          type:
            - integer
            - 'null'
          minimum: 0
          example: 8
        timeSeconds:
          type:
            - integer
            - 'null'
          minimum: 0
          example: null
        distanceMeters:
          type:
            - integer
            - 'null'
          minimum: 0
          example: null
        restSeconds:
          type:
            - integer
            - 'null'
          minimum: 0
          example: 120
        rpe:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 10
          example: 8
        completed:
          type: boolean
          example: true
        notes:
          type: string
          example: Felt strong.
        isPersonalBest:
          type: boolean
          example: false
        repsPlaceholder:
          type:
            - number
            - string
            - 'null'
          example: 22
          description: >-
            The client's OWN greyed hint for an empty reps input on this set —
            accepted on upsert and stored as sent. A range string (`"8-12"`) is
            allowed. It is a FALLBACK, not an override: the coach's
            `repsPlaceholderCoach` wins wherever it exists, so this value is
            only used for sets the coach never prescribed (per-set tracking off,
            extra sets, metrics left blank). Sending it for a prescribed set is
            accepted but has no effect. **Never logged work**: placeholders are
            excluded from every total, volume, PR and completion check, so
            leaving a hint after clearing a value does not make the set count.
            To COMMIT a hint the athlete accepted, send it as the real `reps`
            value with `completed: true` — but never submit a RANGE on a
            completed set, which is stored as its minimum (`"12-24"` → 12) and
            under-credits the athlete.
        weightPlaceholder:
          type:
            - number
            - 'null'
          minimum: 0
          example: 30
          description: >-
            The client's own hint for an empty weight input. See
            `repsPlaceholder`.
        timeSecondsPlaceholder:
          type:
            - integer
            - 'null'
          minimum: 0
          example: 55
          description: >-
            The client's own hint for an empty time input. See
            `repsPlaceholder`.
        distanceMetersPlaceholder:
          type:
            - integer
            - 'null'
          minimum: 0
          example: 40
          description: >-
            The client's own hint for an empty distance input. See
            `repsPlaceholder`.
        rpePlaceholder:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 10
          example: 3
          description: The client's own hint for an empty RPE input. See `repsPlaceholder`.
        restSecondsPlaceholder:
          type:
            - integer
            - 'null'
          minimum: 0
          example: 60
          description: >-
            The client's own hint for an empty rest input. See
            `repsPlaceholder`.
        repsActual:
          type:
            - number
            - string
            - 'null'
          example: 30
          description: >-
            The athlete's ACTUALLY-logged reps for this set (mirrors `reps`), or
            `null` when they have not logged it — the visible `reps` may be a
            coach-prescription prefill. Additive: `reps` is unchanged.
        weightActual:
          type:
            - number
            - 'null'
          minimum: 0
          example: 42
          description: >-
            Actually-logged weight (mirrors `weight`), or `null` if not logged.
            See `repsActual`.
        timeSecondsActual:
          type:
            - integer
            - 'null'
          minimum: 0
          example: null
          description: >-
            Actually-logged time (mirrors `timeSeconds`), or `null` if not
            logged. See `repsActual`.
        distanceMetersActual:
          type:
            - integer
            - 'null'
          minimum: 0
          example: null
          description: >-
            Actually-logged distance (mirrors `distanceMeters`), or `null` if
            not logged. See `repsActual`.
        rpeActual:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 10
          example: 8
          description: >-
            Actually-logged RPE (mirrors `rpe`), or `null` if not logged or RPE
            is hidden. See `repsActual`.
        restSecondsActual:
          type:
            - integer
            - 'null'
          minimum: 0
          example: 90
          description: >-
            Actually-logged rest in seconds (mirrors `restSeconds`), or `null`
            if not logged. See `repsActual`.
    PublicWorkoutSourceWorkoutExerciseTarget:
      type: object
      properties:
        reps:
          type: string
          example: 5-8
        weight:
          type:
            - number
            - 'null'
          example: 80
        weightUnit:
          type: string
          example: kg
        timeSeconds:
          type:
            - number
            - 'null'
          example: null
        distanceMeters:
          type:
            - number
            - 'null'
          example: null
        restSeconds:
          type:
            - number
            - 'null'
          example: 120
        rpe:
          type:
            - number
            - 'null'
          example: 8
        notes:
          type: string
          example: Control the descent.
        sets:
          type: array
          items:
            type: object
            additionalProperties: true
    PublicWorkoutSourceWorkoutExerciseSetupField:
      type: object
      required:
        - key
        - label
      properties:
        key:
          type: string
          example: seat
        label:
          type: string
          example: Seat
        type:
          type: string
          enum:
            - text
            - number
            - select
          example: text
        unit:
          type: string
          example: ''
        options:
          type: array
          items:
            type: string
          example:
            - '1'
            - '2'
            - '3'
            - '4'
        order:
          type: number
          example: 1
        required:
          type: boolean
          example: false
        enabled:
          type: boolean
          example: true
        defaultValue:
          oneOf:
            - type: string
            - type: number
          example: 3
    PublicApiWriteMeta:
      type: object
      additionalProperties: false
      required:
        - requestId
      properties:
        requestId:
          type: string
          description: Stable request correlation id. Mirrors `X-Request-Id` when supplied.
          example: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
        rateLimit:
          $ref: '#/components/schemas/PublicApiRateLimitMeta'
        idempotency:
          type: object
          additionalProperties: false
          properties:
            replayed:
              type: boolean
              description: >-
                True when the response was replayed from a previous request with
                the same `Idempotency-Key`.
              example: true
    PublicApiMeta:
      type: object
      additionalProperties: false
      required:
        - requestId
      properties:
        requestId:
          type: string
          description: Stable request correlation id. Mirrors `X-Request-Id` when supplied.
          example: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
        rateLimit:
          $ref: '#/components/schemas/PublicApiRateLimitMeta'
    StandardResponse:
      type: object
      properties:
        status:
          type: integer
          example: 200
        error:
          type: boolean
          example: false
        message:
          type: string
          example: SUCCESS
      required:
        - status
        - error
        - message
    PublicApiRateLimitMeta:
      type: object
      additionalProperties: false
      properties:
        limit:
          type: integer
          example: 10
        remaining:
          type: integer
          example: 9
        resetSeconds:
          type: integer
          description: Seconds until the current rate limit window resets.
          example: 1
        retryAfterSeconds:
          type: integer
          description: Present when the request was rate limited.
          example: 1
  securitySchemes:
    PublicBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Opaque
      description: >-
        Public API access token issued by `/public/v1/oauth/token`. Example:
        `Authorization: Bearer fspt_...`. Each resource request rechecks the
        token company's current provider access. Disabling access blocks
        existing tokens with `403 auth.provider_unavailable`.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.