> ## 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.

# Get an assigned plan with per-day completion progress

> Requires the workout_client_plans:read scope. This operation maps to /app/v1/workout/plans/client/template-with-progress/:templateId and retains its Workout V2 permission, feature-flag, and resource-scope checks.

The clientId path parameter identifies the client represented by the request context.



## OpenAPI

````yaml /openapi/public-v1.json get /public/v1/workout/clients/{clientId}/plans/template-with-progress/{templateId}
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/clients/{clientId}/plans/template-with-progress/{templateId}:
    get:
      tags:
        - Workout
      summary: Get an assigned plan with per-day completion progress
      description: >-
        Requires the workout_client_plans:read scope. This operation maps to
        /app/v1/workout/plans/client/template-with-progress/:templateId and
        retains its Workout V2 permission, feature-flag, and resource-scope
        checks.


        The clientId path parameter identifies the client represented by the
        request context.
      operationId: >-
        publicWorkoutgetPublicV1WorkoutClientsClientIdPlansTemplateWithProgressTemplateId
      parameters:
        - in: path
          name: templateId
          required: true
          description: Assigned workout plan id.
          schema:
            type: string
            example: 67f1234567890abcdef1234
        - in: query
          name: startDate
          required: false
          description: >-
            Selects which week the week-at-a-time view returns. Any date INSIDE
            a week resolves to that week, so send a `planWeeks[].startDate` or
            simply the day the user tapped. Omit to get the week containing
            today (the first week before the programme starts, the last once it
            is over).
          schema:
            type: string
            format: date
            pattern: ^\d{4}-\d{2}-\d{2}$
            example: '2026-09-21'
        - name: clientId
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
          description: Client in the company bound to the Public API token.
      responses:
        '200':
          description: Assigned plan detail with progress.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PublicApiSuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: >-
                          #/components/schemas/PublicWorkoutSourceGETAppV1WorkoutPlansClientTemplateWithProgressTemplateIdResponse200
              examples:
                success:
                  summary: Successful response
                  value:
                    data:
                      _id: 67f1234567890abcdef1234
                      name: Full Body Strength Program
                      description: ''
                      status: Active
                      media:
                        - type: image
                          url: https://cdn.example.com/exercises/bench.jpg
                          platform: null
                          videoId: null
                          thumbnail: null
                          duration: null
                          width: 1280
                          height: 720
                      difficulty: Intermediate
                      intensity: High
                      category: Strength
                      goal: General Fitness
                      duration:
                        startDate: '2026-05-01'
                        endDate: '2026-05-30'
                        hasEndDate: true
                      schedule:
                        mode: multi_week
                        releasePolicy: weekly_from_start
                        startDate: '2026-09-08'
                        timeZone: Europe/Amsterdam
                        totalWeeks: 4
                        layout: calendar_week
                        weeks:
                          - weekNumber: 1
                            days:
                              - weekday: 1
                                isRestDay: false
                                items:
                                  - _id: 67f1234567890abcdef1234
                                    order: 1
                                    startTime: '08:30'
                                    plannedDurationMinutes: 45
                                    status: today
                                    plannedDate: '2026-09-08'
                                    scheduledDate: '2026-09-08'
                                    source: programme
                                    clientAdjustment:
                                      action: moved
                                      occurrenceDate: '2026-09-08'
                                      scheduledDate: '2026-09-08'
                                      changedAt: null
                                    canClientRestore: true
                                    type: workout
                                    planMomentId: 67f1234567890abcdef1234
                      scheduleMode: multi_week
                      progress:
                        total: 6
                        completed: 2
                        progressPercentage: 33
                        isCompleted: false
                      days:
                        - _id: 67f1234567890abcdef1234
                          sessionId: 67f1234567890abcdef1234
                          status: paused
                          isCompleted: false
                          name: Day 1
                          description: ''
                          dayOrder: 1
                          order: 0
                          weekNumber: 1
                          sessionOrder: 1
                          exerciseCount: 6
                          exercises:
                            - _id: 67f1234567890abcdef1234
                              exerciseId: 67f1234567890abcdef1234
                              exerciseName: Incline Dumbbell Press
                              thumbnail: https://cdn.example.com/ex.jpg
                              primaryMuscle: Chest
                              equipment:
                                - string
                              sets: 4
                              minReps: 8
                              maxReps: 12
                              rest: 90
                              intensity: Medium
                              difficulty: Intermediate
                              notes: ''
                              isFavourite: false
                          scoring:
                            scoringType: forTime
                            descriptionOnly: true
                            acceptsManualResult: true
                            targetValue: null
                            timeDomain:
                              windowSeconds: null
                              timeCapSeconds: 900
                              rounds: 3
                              intervalSeconds: null
                              intervalCount: null
                              workSeconds: null
                              restSeconds: null
                            scoreValidation:
                              timeCapSeconds: 900
                              maxReps: null
                              expectedIntervals: null
                            result:
                              roundsCompleted: 5
                              repsPerRound: 30
                              extraReps: 12
                              totalReps: 162
                              intervalReps:
                                - 12
                                - 11
                                - 10
                              elapsedSeconds: 742
                              finishedBeforeCap: true
                              timeCapSeconds: 900
                              maxLoadKg: 120
                          progress:
                            total: 6
                            completed: 2
                            progressPercentage: 33
                            isCompleted: false
                          completedSessionsDate:
                            - sessionId: 67f1234567890abcdef1234
                              dayLabel: do.
                              dateLabel: sep 10
                              dateText: donderdag, 10 september, 2026
                      weeks:
                        - weekNumber: 2
                          availableFrom: '2026-09-08'
                          isAvailable: false
                          progressPercentage: 50
                          isCompleted: false
                          days:
                            - _id: 67f1234567890abcdef1234
                              sessionId: 67f1234567890abcdef1234
                              status: paused
                              isCompleted: false
                              name: Day 1
                              description: ''
                              dayOrder: 1
                              order: 0
                              weekNumber: 1
                              sessionOrder: 1
                              exerciseCount: 6
                              exercises:
                                - _id: 67f1234567890abcdef1234
                                  exerciseId: 67f1234567890abcdef1234
                                  exerciseName: Incline Dumbbell Press
                                  thumbnail: https://cdn.example.com/ex.jpg
                                  primaryMuscle: Chest
                                  equipment:
                                    - string
                                  sets: 4
                                  minReps: 8
                                  maxReps: 12
                                  rest: 90
                                  intensity: Medium
                                  difficulty: Intermediate
                                  notes: ''
                                  isFavourite: false
                              scoring:
                                scoringType: forTime
                                descriptionOnly: true
                                acceptsManualResult: true
                                targetValue: null
                                timeDomain:
                                  windowSeconds: null
                                  timeCapSeconds: 900
                                  rounds: 3
                                  intervalSeconds: null
                                  intervalCount: null
                                  workSeconds: null
                                  restSeconds: null
                                scoreValidation:
                                  timeCapSeconds: 900
                                  maxReps: null
                                  expectedIntervals: null
                                result:
                                  roundsCompleted: 5
                                  repsPerRound: 30
                                  extraReps: 12
                                  totalReps: 162
                                  intervalReps:
                                    - 12
                                    - 11
                                    - 10
                                  elapsedSeconds: 742
                                  finishedBeforeCap: true
                                  timeCapSeconds: 900
                                  maxLoadKg: 120
                              progress:
                                total: 6
                                completed: 2
                                progressPercentage: 33
                                isCompleted: false
                              completedSessionsDate:
                                - sessionId: 67f1234567890abcdef1234
                                  dayLabel: do.
                                  dateLabel: sep 10
                                  dateText: donderdag, 10 september, 2026
                      planWeeks:
                        - weekNumber: 1
                          startDate: '2026-09-14'
                          endDate: '2026-09-20'
                          isSkipWeek: false
                          isCurrentWeek: true
                      week:
                        weekNumber: 1
                        startDate: '2026-09-14'
                        endDate: '2026-09-20'
                        label: This Week
                        isCurrentWeek: true
                        status: active
                        isAvailable: true
                        isSkipWeek: false
                        isCompleted: false
                        progressPercentage: 50
                        days:
                          - weekday: 1
                            date: '2026-09-14'
                            isRestDay: false
                            isSkipDay: false
                            items:
                              - _id: 67f1234567890abcdef1234
                                type: workout
                                order: 1
                                startTime: ''
                                plannedDurationMinutes: 60
                                status: completed
                                name: Upper Body
                                description: ''
                                activityType: running
                                targetDistanceMeters: 5000
                                planMomentId: 67f1234567890abcdef1234
                                workout:
                                  planMomentId: 67f1234567890abcdef1234
                                  name: Upper Body
                                  description: ''
                                  exerciseCount: 6
                                  exercises:
                                    - _id: 67f1234567890abcdef1234
                                      exerciseId: 67f1234567890abcdef1234
                                      exerciseName: Incline Dumbbell Press
                                      thumbnail: https://cdn.example.com/ex.jpg
                                      primaryMuscle: Chest
                                      equipment:
                                        - string
                                      sets: 4
                                      minReps: 8
                                      maxReps: 12
                                      rest: 90
                                      intensity: Medium
                                      difficulty: Intermediate
                                      notes: ''
                                      isFavourite: false
                                  progress:
                                    total: 6
                                    completed: 6
                                    progressPercentage: 100
                                    isCompleted: true
                                  sessionId: 67f1234567890abcdef1234
                                  sessionStatus: completed
                                  isCompleted: true
                                  scoring:
                                    scoringType: forTime
                                    descriptionOnly: true
                                    acceptsManualResult: true
                                    targetValue: null
                                    timeDomain:
                                      windowSeconds: null
                                      timeCapSeconds: 900
                                      rounds: 3
                                      intervalSeconds: null
                                      intervalCount: null
                                      workSeconds: null
                                      restSeconds: null
                                    scoreValidation:
                                      timeCapSeconds: 900
                                      maxReps: null
                                      expectedIntervals: null
                                    result:
                                      roundsCompleted: 5
                                      repsPerRound: 30
                                      extraReps: 12
                                      totalReps: 162
                                      intervalReps:
                                        - 12
                                        - 11
                                        - 10
                                      elapsedSeconds: 742
                                      finishedBeforeCap: true
                                      timeCapSeconds: 900
                                      maxLoadKg: 120
                                activity:
                                  performanceId: 67f1234567890abcdef1234
                                  status: in_progress
                                  startedAt: '2026-04-12T10:00:00.000Z'
                                  completedAt: '2026-04-12T10:00:00.000Z'
                                  actualDurationMinutes: 32
                                  actualDistanceMeters: 5100
                                  rpe: 7
                                  notes: ''
                      weekDates:
                        - weekday: 1
                          date: '2026-09-14'
                          dayLabel: Mon
                          dayName: Monday
                          dayOfMonth: 14
                          isRestDay: false
                          isSkipDay: false
                          isToday: false
                          itemCount: 2
                          statuses:
                            - completed
                            - not_started
                      totalDays: 6
                      totalExercises: 36
                      isTemplateFavourite: false
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '400':
          description: >-
            Company context missing (`COMPANY_ID_REQUIRED`), invalid id
            (`INVALID_TEMPLATE_ID`), or a `startDate` that is not a canonical
            `YYYY-MM-DD` date (`INVALID_DATE_FORMAT`). A well-formed `startDate`
            outside every week is NOT an error — see the description.
          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
        '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: Template not found (`TEMPLATE_NOT_FOUND`).
          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':
          $ref: '#/components/schemas/ErrorResponse'
        '422':
          $ref: '#/components/schemas/ErrorResponse'
        '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 GET
            "https://api.fitsociety.io/public/v1/workout/clients/{clientId}/plans/template-with-progress/{templateId}"
            \
              -H "Authorization: Bearer <access_token>"
components:
  schemas:
    PublicApiSuccessResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          type: object
        meta:
          $ref: '#/components/schemas/PublicApiMeta'
    PublicWorkoutSourceGETAppV1WorkoutPlansClientTemplateWithProgressTemplateIdResponse200:
      type: object
      properties:
        _id:
          type: string
          example: 67f1234567890abcdef1234
        name:
          type: string
          example: Full Body Strength Program
        description:
          type: string
          example: ''
        status:
          type: string
          enum:
            - Active
            - Inactive
          example: Active
          description: >-
            Whether the plan is switched on. A switched-off (`Inactive`) plan
            still opens here and returns its full data — it is a reversible
            pause, so the screen renders normally and only reflects the state in
            its switch. Change it with `PATCH
            /app/v1/workout/plans/client/template/{templateId}/status`. A
            `Draft` plan is not returned by this endpoint at all (404).
        media:
          type: array
          items:
            $ref: '#/components/schemas/PublicWorkoutSourceWorkoutExerciseSearchMedia'
        difficulty:
          type: string
          example: Intermediate
        intensity:
          type: string
          example: High
        category:
          type: string
          example: Strength
        goal:
          type: string
          example: General Fitness
        duration:
          type: object
          properties:
            startDate:
              type: string
              example: '2026-05-01'
            endDate:
              type: string
              example: '2026-05-30'
            hasEndDate:
              type: boolean
              example: true
        schedule:
          type: object
          required:
            - mode
            - releasePolicy
            - startDate
            - timeZone
            - totalWeeks
          properties:
            mode:
              type: string
              enum:
                - recurring
                - multi_week
              example: multi_week
            releasePolicy:
              type: string
              enum:
                - all_at_once
                - weekly_from_start
              example: weekly_from_start
            startDate:
              oneOf:
                - type: string
                  format: date
                  pattern: ^\d{4}-\d{2}-\d{2}$
                  example: '2026-09-08'
                - type: string
                  enum:
                    - ''
                  example: ''
              description: >-
                Blank for templates and legacy sequence recurring schedules.
                Assigned recurring calendar_week schedules require the canonical
                company-local Monday anchor. Assigned multi-week schedules
                accept any canonical date; for calendar_week, week 1 is the
                Monday-Sunday week containing it and days before it are not
                scheduled (`isBeforeStart`).
            timeZone:
              type: string
              example: Europe/Amsterdam
              description: Company time zone used to evaluate weekly availability.
            totalWeeks:
              type: integer
              minimum: 0
              example: 4
            layout:
              type: string
              enum:
                - sequence
                - calendar_week
              example: calendar_week
            weeks:
              type: array
              items:
                type: object
                required:
                  - weekNumber
                  - days
                properties:
                  weekNumber:
                    type: integer
                    minimum: 1
                    example: 1
                  days:
                    type: array
                    items:
                      type: object
                      required:
                        - weekday
                        - isRestDay
                        - items
                      properties:
                        weekday:
                          type: integer
                          minimum: 1
                          maximum: 7
                          example: 1
                          description: 'ISO weekday number: 1 is Monday and 7 is Sunday.'
                        isRestDay:
                          type: boolean
                          example: false
                        items:
                          type: array
                          items:
                            oneOf:
                              - type: object
                                additionalProperties: false
                                required:
                                  - type
                                  - order
                                  - planMomentId
                                properties:
                                  _id:
                                    type: string
                                    example: 67f1234567890abcdef1234
                                  order:
                                    type: integer
                                    minimum: 1
                                    example: 1
                                  startTime:
                                    type: string
                                    pattern: ^(?:[01]\d|2[0-3]):[0-5]\d$|^$
                                    example: '08:30'
                                  plannedDurationMinutes:
                                    type:
                                      - number
                                      - 'null'
                                    minimum: 0
                                    example: 45
                                    description: >-
                                      Planned duration in minutes; null when no
                                      target is set.
                                  status:
                                    type:
                                      - string
                                      - 'null'
                                    enum:
                                      - in_progress
                                      - completed
                                      - overdue
                                      - today
                                      - upcoming
                                      - null
                                    readOnly: true
                                    example: today
                                    description: >-
                                      Occurrence presentation status. Template
                                      items use null; cancelled occurrences fall
                                      back to overdue, today, or upcoming.
                                  plannedDate:
                                    oneOf:
                                      - type: string
                                        format: date
                                        pattern: ^\d{4}-\d{2}-\d{2}$
                                        example: '2026-09-08'
                                      - type: string
                                        enum:
                                          - ''
                                        example: ''
                                    readOnly: true
                                    description: >-
                                      Original authored occurrence date for a
                                      calendar response item.
                                  scheduledDate:
                                    oneOf:
                                      - type: string
                                        format: date
                                        pattern: ^\d{4}-\d{2}-\d{2}$
                                        example: '2026-09-08'
                                      - type: string
                                        enum:
                                          - ''
                                        example: ''
                                    readOnly: true
                                    description: >-
                                      Effective calendar date after a client
                                      move; otherwise plannedDate.
                                  source:
                                    type: string
                                    enum:
                                      - programme
                                    readOnly: true
                                  clientAdjustment:
                                    oneOf:
                                      - type: object
                                        required:
                                          - action
                                          - occurrenceDate
                                          - scheduledDate
                                          - changedAt
                                        properties:
                                          action:
                                            type: string
                                            enum:
                                              - moved
                                              - skipped
                                          occurrenceDate:
                                            type: string
                                            format: date
                                            pattern: ^\d{4}-\d{2}-\d{2}$
                                            example: '2026-09-08'
                                          scheduledDate:
                                            oneOf:
                                              - type: string
                                                format: date
                                                pattern: ^\d{4}-\d{2}-\d{2}$
                                                example: '2026-09-08'
                                              - type: string
                                                enum:
                                                  - ''
                                                example: ''
                                          changedAt:
                                            type:
                                              - string
                                              - 'null'
                                            format: date-time
                                            example: null
                                      - type: 'null'
                                    readOnly: true
                                  canClientRestore:
                                    type: boolean
                                    readOnly: true
                                  type:
                                    type: string
                                    enum:
                                      - workout
                                    example: workout
                                  planMomentId:
                                    type: string
                                    example: 67f1234567890abcdef1234
                              - type: object
                                additionalProperties: false
                                required:
                                  - type
                                  - order
                                  - activityType
                                  - name
                                properties:
                                  _id:
                                    type: string
                                    example: 67f1234567890abcdef1234
                                  order:
                                    type: integer
                                    minimum: 1
                                    example: 1
                                  startTime:
                                    type: string
                                    pattern: ^(?:[01]\d|2[0-3]):[0-5]\d$|^$
                                    example: '08:30'
                                  plannedDurationMinutes:
                                    type:
                                      - number
                                      - 'null'
                                    minimum: 0
                                    example: 45
                                    description: >-
                                      Planned duration in minutes; null when no
                                      target is set.
                                  status:
                                    type:
                                      - string
                                      - 'null'
                                    enum:
                                      - in_progress
                                      - completed
                                      - overdue
                                      - today
                                      - upcoming
                                      - null
                                    readOnly: true
                                    example: today
                                    description: >-
                                      Occurrence presentation status. Template
                                      items use null; cancelled occurrences fall
                                      back to overdue, today, or upcoming.
                                  plannedDate:
                                    oneOf:
                                      - type: string
                                        format: date
                                        pattern: ^\d{4}-\d{2}-\d{2}$
                                        example: '2026-09-08'
                                      - type: string
                                        enum:
                                          - ''
                                        example: ''
                                    readOnly: true
                                    description: >-
                                      Original authored occurrence date for a
                                      calendar response item.
                                  scheduledDate:
                                    oneOf:
                                      - type: string
                                        format: date
                                        pattern: ^\d{4}-\d{2}-\d{2}$
                                        example: '2026-09-08'
                                      - type: string
                                        enum:
                                          - ''
                                        example: ''
                                    readOnly: true
                                    description: >-
                                      Effective calendar date after a client
                                      move; otherwise plannedDate.
                                  source:
                                    type: string
                                    enum:
                                      - programme
                                    readOnly: true
                                  clientAdjustment:
                                    oneOf:
                                      - type: object
                                        required:
                                          - action
                                          - occurrenceDate
                                          - scheduledDate
                                          - changedAt
                                        properties:
                                          action:
                                            type: string
                                            enum:
                                              - moved
                                              - skipped
                                          occurrenceDate:
                                            type: string
                                            format: date
                                            pattern: ^\d{4}-\d{2}-\d{2}$
                                            example: '2026-09-08'
                                          scheduledDate:
                                            oneOf:
                                              - type: string
                                                format: date
                                                pattern: ^\d{4}-\d{2}-\d{2}$
                                                example: '2026-09-08'
                                              - type: string
                                                enum:
                                                  - ''
                                                example: ''
                                          changedAt:
                                            type:
                                              - string
                                              - 'null'
                                            format: date-time
                                            example: null
                                      - type: 'null'
                                    readOnly: true
                                  canClientRestore:
                                    type: boolean
                                    readOnly: true
                                  type:
                                    type: string
                                    enum:
                                      - activity
                                    example: activity
                                  activityType:
                                    type: string
                                    enum:
                                      - running
                                      - cycling
                                      - walking
                                      - swimming
                                      - rowing
                                      - padel
                                      - mobility
                                      - other
                                    example: running
                                  name:
                                    type: array
                                    minItems: 1
                                    items:
                                      type: object
                                      required:
                                        - lang
                                        - value
                                      properties:
                                        lang:
                                          type: string
                                          enum:
                                            - en
                                            - nl
                                            - fr
                                            - de
                                            - es
                                          example: en
                                        value:
                                          type: string
                                          example: Full Body Strength
                                    example:
                                      - lang: en
                                        value: Full Body Strength
                                      - lang: nl
                                        value: Full Body Kracht
                                  description:
                                    type: array
                                    items:
                                      type: object
                                      required:
                                        - lang
                                        - value
                                      properties:
                                        lang:
                                          type: string
                                          enum:
                                            - en
                                            - nl
                                            - fr
                                            - de
                                            - es
                                          example: en
                                        value:
                                          type: string
                                          example: Full Body Strength
                                  cardio:
                                    type: object
                                    additionalProperties: false
                                    description: >-
                                      Optional structured activity prescription.
                                      Requires workoutsectionv2 +
                                      workoutV2ProgrammeCalendar +
                                      workoutV2CardioBuilder. Omitted from
                                      disabled reads. Supported sports: running
                                      (paceZone E/T/I/R/RP, absolute pace,
                                      absolute HR, RPE), walking (pace, HR,
                                      RPE), cycling (power, HR, RPE), rowing
                                      (pace, power, HR, RPE), swimming (pace,
                                      RPE). Rejected for workout, padel,
                                      mobility and other items. Totals overwrite
                                      planned duration/distance; unknown time or
                                      distance contributes zero.
                                    properties:
                                      slotKey:
                                        type: string
                                        description: >-
                                          Generated when missing; retain across
                                          edits, copies and weeks.
                                      sessionType:
                                        type: string
                                        enum:
                                          - easy
                                          - zone2
                                          - long_run
                                          - threshold_continuous
                                          - threshold_reps
                                          - interval
                                          - race_pace
                                          - time_trial
                                          - race
                                        description: >-
                                          Running only. Template session codes:
                                          threshold_continuous = drempel_c,
                                          threshold_reps = drempel_r, interval,
                                          race_pace = doeltempo, zone2. A typed
                                          session with empty blocks is a skeleton
                                          to be filled later.
                                      controlMode:
                                        type: string
                                        enum:
                                          - pace
                                          - hr
                                        description: >-
                                          Binding signal. Defaults from
                                          sessionType: hr for easy, zone2 and
                                          long_run; pace for threshold_continuous,
                                          threshold_reps, interval and race_pace;
                                          none for time_trial and race. A
                                          contradicting value is rejected.
                                      ladderStep:
                                        type: integer
                                        minimum: 1
                                        description: >-
                                          Ladder position: interval 1–8,
                                          threshold_reps 1–7, threshold_continuous
                                          1–5 (15/18/20/22/25 min). Rejected for
                                          other session types.
                                      poolLengthM:
                                        type: number
                                        enum:
                                          - 25
                                          - 50
                                        description: Swimming only.
                                      blocks:
                                        type: array
                                        maxItems: 10
                                        items:
                                          type: object
                                          additionalProperties: false
                                          properties:
                                            _id:
                                              type: string
                                              example: 67f1234567890abcdef1234
                                            repeat:
                                              type: integer
                                              minimum: 1
                                              maximum: 50
                                              default: 1
                                            steps:
                                              type: array
                                              maxItems: 20
                                              items:
                                                type: object
                                                additionalProperties: false
                                                required:
                                                  - kind
                                                  - durationType
                                                properties:
                                                  _id:
                                                    type: string
                                                    example: 67f1234567890abcdef1234
                                                  kind:
                                                    type: string
                                                    enum:
                                                      - warmup
                                                      - work
                                                      - recovery
                                                      - rest
                                                      - cooldown
                                                  durationType:
                                                    type: string
                                                    enum:
                                                      - time
                                                      - distance
                                                      - open
                                                  durationValue:
                                                    type:
                                                      - number
                                                      - 'null'
                                                    minimum: 0
                                                    default: null
                                                    description: >-
                                                      Required seconds for time or metres for
                                                      distance; null/omitted for open.
                                                  target:
                                                    type: object
                                                    additionalProperties: false
                                                    required:
                                                      - kind
                                                    description: >-
                                                      Allowed kinds and zones depend on the
                                                      sport. Zone targets require zone; other
                                                      targets require ordered low/high bounds.
                                                      Pace is seconds/km for running/walking,
                                                      seconds/500m for rowing, seconds/100m
                                                      for swimming. Percentages are percent
                                                      values; HR is bpm, power is watts, RPE
                                                      is 1–10. Numeric strings with dot or
                                                      comma decimals are accepted.
                                                    properties:
                                                      kind:
                                                        type: string
                                                        enum:
                                                          - paceZone
                                                          - pacePct
                                                          - pace
                                                          - hrZone
                                                          - hrPct
                                                          - hr
                                                          - powerZone
                                                          - powerPct
                                                          - power
                                                          - rpe
                                                      zone:
                                                        type: string
                                                        enum:
                                                          - E
                                                          - T
                                                          - I
                                                          - R
                                                          - RP
                                                          - Z1
                                                          - Z2
                                                          - Z3
                                                          - Z4
                                                          - Z5
                                                          - Z6
                                                          - Z7
                                                          - easy
                                                          - endurance
                                                          - threshold
                                                          - speed
                                                        description: >-
                                                          Run pace: E (easy), T (threshold), I
                                                          (interval), R (repetition), RP (race
                                                          pace = goal time / goal distance, never
                                                          derived from T). Running has no HR
                                                          zones; HR bands are absolute bpm from
                                                          athlete data. Walking/rowing pace and
                                                          five-zone HR: Z1–Z5. Bike/row power:
                                                          Z1–Z7. Swimming pace: easy, endurance,
                                                          threshold, speed.
                                                      basis:
                                                        type: string
                                                        enum:
                                                          - lthr
                                                          - maxHr
                                                        description: HR zone/percentage targets only.
                                                      low:
                                                        type: number
                                                        minimum: 0
                                                      high:
                                                        type: number
                                                        minimum: 0
                                                  secondaryTarget:
                                                    type: object
                                                    additionalProperties: false
                                                    required:
                                                      - kind
                                                    description: >-
                                                      Allowed kinds and zones depend on the
                                                      sport. Zone targets require zone; other
                                                      targets require ordered low/high bounds.
                                                      Pace is seconds/km for running/walking,
                                                      seconds/500m for rowing, seconds/100m
                                                      for swimming. Percentages are percent
                                                      values; HR is bpm, power is watts, RPE
                                                      is 1–10. Numeric strings with dot or
                                                      comma decimals are accepted.
                                                    properties:
                                                      kind:
                                                        type: string
                                                        enum:
                                                          - paceZone
                                                          - pacePct
                                                          - pace
                                                          - hrZone
                                                          - hrPct
                                                          - hr
                                                          - powerZone
                                                          - powerPct
                                                          - power
                                                          - rpe
                                                      zone:
                                                        type: string
                                                        enum:
                                                          - E
                                                          - T
                                                          - I
                                                          - R
                                                          - RP
                                                          - Z1
                                                          - Z2
                                                          - Z3
                                                          - Z4
                                                          - Z5
                                                          - Z6
                                                          - Z7
                                                          - easy
                                                          - endurance
                                                          - threshold
                                                          - speed
                                                        description: >-
                                                          Run pace: E (easy), T (threshold), I
                                                          (interval), R (repetition), RP (race
                                                          pace = goal time / goal distance, never
                                                          derived from T). Running has no HR
                                                          zones; HR bands are absolute bpm from
                                                          athlete data. Walking/rowing pace and
                                                          five-zone HR: Z1–Z5. Bike/row power:
                                                          Z1–Z7. Swimming pace: easy, endurance,
                                                          threshold, speed.
                                                      basis:
                                                        type: string
                                                        enum:
                                                          - lthr
                                                          - maxHr
                                                        description: HR zone/percentage targets only.
                                                      low:
                                                        type: number
                                                        minimum: 0
                                                      high:
                                                        type: number
                                                        minimum: 0
                                                  notes:
                                                    type: string
                                                    default: ''
                                                  stroke:
                                                    type: string
                                                    enum:
                                                      - free
                                                      - back
                                                      - breast
                                                      - fly
                                                      - im
                                                      - choice
                                                      - kick
                                                      - drill
                                                    description: Swimming only.
                                                  equipment:
                                                    type: array
                                                    items:
                                                      type: string
                                                      enum:
                                                        - pullBuoy
                                                        - paddles
                                                        - fins
                                                        - kickboard
                                                        - snorkel
                                                    description: Swimming only.
                                                  restMode:
                                                    type: string
                                                    enum:
                                                      - rest
                                                      - sendOff
                                                    description: >-
                                                      Swimming only. sendOff requires
                                                      sendOffSec.
                                                  sendOffSec:
                                                    type: number
                                                    minimum: 1
                                                    description: Swimming sendOff only.
                                                  cadence:
                                                    type: object
                                                    additionalProperties: false
                                                    required:
                                                      - low
                                                      - high
                                                    properties:
                                                      low:
                                                        type: number
                                                        minimum: 0
                                                      high:
                                                        type: number
                                                        minimum: 0
                                              default: []
                                        default: []
                                      sourceTemplateId:
                                        type:
                                          - string
                                          - 'null'
                                        example: null
                                  targetDistanceMeters:
                                    type:
                                      - number
                                      - 'null'
                                    minimum: 0
                                    example: 5000
                                    description: >-
                                      Planned distance in meters; null when no
                                      target is set.
                            discriminator:
                              propertyName: type
                              mapping:
                                workout: >-
                                  #/components/schemas/WorkoutProgrammeCalendarWorkoutItem
                                activity: >-
                                  #/components/schemas/WorkoutProgrammeCalendarActivityItem
        scheduleMode:
          type: string
          enum:
            - recurring
            - multi_week
          example: multi_week
          description: >-
            Which kind of programme this is, the same field the library and
            dashboard cards carry. Defaults to `recurring` when the plan never
            stored a mode. The `week` / `planWeeks` / `weekDates` trio is
            populated only when this is `multi_week` on a calendar-layout plan.
        progress:
          type: object
          description: >-
            Plan-wide progress across every active moment: recurring sequence
            days use active-attempt progress; multi-week/calendar programmes
            retain permanent completion, including locked future weeks. `total`
            can therefore exceed legacy `totalDays` for an assigned multi-week
            programme.
          properties:
            total:
              type: integer
              example: 6
            completed:
              type: integer
              example: 2
            progressPercentage:
              type: integer
              example: 33
            isCompleted:
              type: boolean
              example: false
        days:
          type: array
          description: >-
            Backward-compatible flat list. Assigned multi-week programmes
            include only currently available/startable days; recurring plans
            preserve all active days.
          items:
            type: object
            properties:
              _id:
                type: string
                example: 67f1234567890abcdef1234
              sessionId:
                type: string
                example: 67f1234567890abcdef1234
                nullable: true
                description: >-
                  Selected session for navigation: newest active
                  in_progress/paused attempt. Recurring sequence days return
                  null after completion so the same day can be trained again.
                  Multi-week/calendar programmes otherwise select the latest
                  completed attempt.
              status:
                type: string
                nullable: true
                enum:
                  - in_progress
                  - paused
                  - completed
                example: paused
                description: Status of the selected navigation session.
              isCompleted:
                type: boolean
                example: false
                description: >-
                  Selected/current session terminal state used by older mobile
                  navigation. Recurring sequence days are false after completion
                  and ready to train again. Multi-week/calendar programmes
                  retain permanent completion in `progress.isCompleted`.
              name:
                type: string
                example: Day 1
              description:
                type: string
                example: ''
              dayOrder:
                type: integer
                example: 1
              order:
                type: integer
                example: 0
              weekNumber:
                type: integer
                minimum: 1
                example: 1
                description: Programme week number; legacy recurring days use week 1.
              sessionOrder:
                type: integer
                minimum: 1
                example: 1
                description: Training position within the programme week.
              exerciseCount:
                type: integer
                example: 6
              exercises:
                type: array
                items:
                  type: object
                  properties:
                    _id:
                      type: string
                      example: 67f1234567890abcdef1234
                    exerciseId:
                      type: string
                      example: 67f1234567890abcdef1234
                    exerciseName:
                      type: string
                      example: Incline Dumbbell Press
                    thumbnail:
                      type: string
                      example: https://cdn.example.com/ex.jpg
                    primaryMuscle:
                      type: string
                      example: Chest
                    equipment:
                      type: array
                      items:
                        type: string
                    sets:
                      type: integer
                      example: 4
                    minReps:
                      type: integer
                      example: 8
                    maxReps:
                      type: integer
                      example: 12
                    rest:
                      type: integer
                      example: 90
                    intensity:
                      type: string
                      example: Medium
                    difficulty:
                      type: string
                      example: Intermediate
                    notes:
                      type: string
                      example: ''
                    isFavourite:
                      type: boolean
                      example: false
              scoring:
                type: object
                nullable: true
                description: >-
                  Scoring of a plan day. Null for an ordinary standard day.
                  `acceptsManualResult` is true only for exercise-free scored
                  days (`descriptionOnly`), which take a `wodResult` on PATCH
                  /app/v1/workout/performance/sessions/{sessionId}/complete.
                  `result` is the saved score of the selected session, or null.
                properties:
                  scoringType:
                    type: string
                    enum:
                      - standard
                      - forTime
                      - amrap
                      - emom
                      - tabata
                      - maxLoad
                    example: forTime
                  descriptionOnly:
                    type: boolean
                    example: true
                  acceptsManualResult:
                    type: boolean
                    example: true
                  targetValue:
                    type: number
                    nullable: true
                    example: null
                  timeDomain:
                    type: object
                    nullable: true
                    description: >-
                      Clock prescription, same contract as a WOD `timeDomain`.
                      Allowed fields depend on `scoringType`: amrap →
                      windowSeconds (required); forTime → timeCapSeconds, rounds
                      (defaults to 1); emom → intervalSeconds, intervalCount
                      (both required); tabata → workSeconds, restSeconds,
                      intervalCount (all required); standard/maxLoad → none.
                    properties:
                      windowSeconds:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: null
                      timeCapSeconds:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: 900
                      rounds:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: 3
                      intervalSeconds:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: null
                      intervalCount:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: null
                      workSeconds:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: null
                      restSeconds:
                        type: integer
                        nullable: true
                        minimum: 0
                        example: null
                  scoreValidation:
                    type: object
                    nullable: true
                    description: >-
                      Optional result caps. timeCapSeconds and expectedIntervals
                      are derived from `timeDomain` when one is set.
                    properties:
                      timeCapSeconds:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: 900
                      maxReps:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: null
                      expectedIntervals:
                        type: integer
                        nullable: true
                        minimum: 1
                        example: null
                  result:
                    allOf:
                      - type: object
                        description: >-
                          Type-specific manual score, the same shape as the WOD
                          `wodResult`. Only the fields of the day's scoringType
                          are accepted: amrap → roundsCompleted, repsPerRound,
                          extraReps, totalReps (total = rounds × repsPerRound +
                          extraReps, extraReps < repsPerRound); forTime →
                          elapsedSeconds, finishedBeforeCap, totalReps (reps
                          required when not finished before the cap);
                          emom/tabata → intervalReps, totalReps (sum of
                          intervals; count must match intervalCount when set);
                          maxLoad → maxLoadKg. The stored copy of a forTime
                          result also carries timeCapSeconds.
                        properties:
                          roundsCompleted:
                            type: integer
                            minimum: 0
                            example: 5
                          repsPerRound:
                            type: integer
                            minimum: 1
                            example: 30
                          extraReps:
                            type: integer
                            minimum: 0
                            example: 12
                          totalReps:
                            type: integer
                            minimum: 0
                            example: 162
                          intervalReps:
                            type: array
                            items:
                              type: integer
                              minimum: 0
                            example:
                              - 12
                              - 11
                              - 10
                          elapsedSeconds:
                            type: number
                            minimum: 0
                            example: 742
                          finishedBeforeCap:
                            type: boolean
                            example: true
                          timeCapSeconds:
                            type: integer
                            nullable: true
                            readOnly: true
                            example: 900
                          maxLoadKg:
                            type: number
                            minimum: 0
                            example: 120
                    nullable: true
              progress:
                type: object
                description: >-
                  Recurring sequence days use only the selected active attempt;
                  after completion progress returns to zero without changing
                  history. Permanent programme progress applies to
                  multi-week/calendar programmes: a non-deleted completed
                  session for this exact plan and moment makes `isCompleted`
                  true all-time, including zero-exercise moments.
                properties:
                  total:
                    type: integer
                    example: 6
                  completed:
                    type: integer
                    example: 2
                  progressPercentage:
                    type: integer
                    example: 33
                  isCompleted:
                    type: boolean
                    example: false
              completedSessionsDate:
                type: array
                description: >-
                  Every calendar date this day has been completed on — the
                  "Completed On" chips. A RECURRING day becomes available again
                  the moment it is finished, so `progress` resets each time and
                  these dates are the only record of that history. **Most recent
                  FIRST**, so `[0]` is the latest completion (e.g. Sep 16, Sep
                  13, Sep 12, Sep 9). Deduped per date: finishing the same day
                  twice on one date yields ONE entry, whose `sessionId` is the
                  LATEST-finishing attempt of that date, so the chip opens the
                  most recent run. Empty (never null) on a day never completed.
                items:
                  type: object
                  properties:
                    sessionId:
                      type: string
                      example: 67f1234567890abcdef1234
                      description: >-
                        The completed session this date was read from, so the
                        chip can open that attempt. Distinct from the day's own
                        `sessionId`, which is the currently relevant session and
                        is null for a finished recurring day.
                    dayLabel:
                      type: string
                      example: do.
                      description: >-
                        Abbreviated weekday in the client's language (en, nl,
                        de, fr, es; English fallback). Render as supplied.
                    dateLabel:
                      type: string
                      example: sep 10
                      description: >-
                        Abbreviated month and day in the client's language,
                        using the existing display timezone.
                    dateText:
                      type: string
                      example: donderdag, 10 september, 2026
                      description: >-
                        Full date in the client's language for the
                        completed-dates sheet.
        weeks:
          type: array
          description: >-
            Complete week-aware programme preview. Locked future weeks remain
            present with `isAvailable=false`.
          items:
            allOf:
              - type: object
                required:
                  - weekNumber
                  - availableFrom
                  - isAvailable
                  - progressPercentage
                  - isCompleted
                properties:
                  weekNumber:
                    type: integer
                    minimum: 1
                    example: 2
                  availableFrom:
                    oneOf:
                      - type: string
                        format: date
                        pattern: ^\d{4}-\d{2}-\d{2}$
                        example: '2026-09-08'
                      - type: string
                        enum:
                          - ''
                        example: ''
                    description: >-
                      Canonical company-local release date; blank for recurring
                      plans and templates.
                  isAvailable:
                    type: boolean
                    example: false
                  progressPercentage:
                    type: integer
                    minimum: 0
                    maximum: 100
                    example: 50
                    description: >-
                      Percentage of active moments in this programme week with a
                      completed session. Ordinary recurring sequence days remain
                      reusable and do not retain historical completion in
                      progress.
                  isCompleted:
                    type: boolean
                    example: false
              - type: object
                required:
                  - days
                properties:
                  days:
                    type: array
                    description: >-
                      Every active training in this week, including locked
                      preview days.
                    items:
                      type: object
                      properties:
                        _id:
                          type: string
                          example: 67f1234567890abcdef1234
                        sessionId:
                          type: string
                          example: 67f1234567890abcdef1234
                          nullable: true
                          description: >-
                            Selected session for navigation: newest active
                            in_progress/paused attempt. Recurring sequence days
                            return null after completion so the same day can be
                            trained again. Multi-week/calendar programmes
                            otherwise select the latest completed attempt.
                        status:
                          type: string
                          nullable: true
                          enum:
                            - in_progress
                            - paused
                            - completed
                          example: paused
                          description: Status of the selected navigation session.
                        isCompleted:
                          type: boolean
                          example: false
                          description: >-
                            Selected/current session terminal state used by
                            older mobile navigation. Recurring sequence days are
                            false after completion and ready to train again.
                            Multi-week/calendar programmes retain permanent
                            completion in `progress.isCompleted`.
                        name:
                          type: string
                          example: Day 1
                        description:
                          type: string
                          example: ''
                        dayOrder:
                          type: integer
                          example: 1
                        order:
                          type: integer
                          example: 0
                        weekNumber:
                          type: integer
                          minimum: 1
                          example: 1
                          description: >-
                            Programme week number; legacy recurring days use
                            week 1.
                        sessionOrder:
                          type: integer
                          minimum: 1
                          example: 1
                          description: Training position within the programme week.
                        exerciseCount:
                          type: integer
                          example: 6
                        exercises:
                          type: array
                          items:
                            type: object
                            properties:
                              _id:
                                type: string
                                example: 67f1234567890abcdef1234
                              exerciseId:
                                type: string
                                example: 67f1234567890abcdef1234
                              exerciseName:
                                type: string
                                example: Incline Dumbbell Press
                              thumbnail:
                                type: string
                                example: https://cdn.example.com/ex.jpg
                              primaryMuscle:
                                type: string
                                example: Chest
                              equipment:
                                type: array
                                items:
                                  type: string
                              sets:
                                type: integer
                                example: 4
                              minReps:
                                type: integer
                                example: 8
                              maxReps:
                                type: integer
                                example: 12
                              rest:
                                type: integer
                                example: 90
                              intensity:
                                type: string
                                example: Medium
                              difficulty:
                                type: string
                                example: Intermediate
                              notes:
                                type: string
                                example: ''
                              isFavourite:
                                type: boolean
                                example: false
                        scoring:
                          type: object
                          nullable: true
                          description: >-
                            Scoring of a plan day. Null for an ordinary standard
                            day. `acceptsManualResult` is true only for
                            exercise-free scored days (`descriptionOnly`), which
                            take a `wodResult` on PATCH
                            /app/v1/workout/performance/sessions/{sessionId}/complete.
                            `result` is the saved score of the selected session,
                            or null.
                          properties:
                            scoringType:
                              type: string
                              enum:
                                - standard
                                - forTime
                                - amrap
                                - emom
                                - tabata
                                - maxLoad
                              example: forTime
                            descriptionOnly:
                              type: boolean
                              example: true
                            acceptsManualResult:
                              type: boolean
                              example: true
                            targetValue:
                              type: number
                              nullable: true
                              example: null
                            timeDomain:
                              type: object
                              nullable: true
                              description: >-
                                Clock prescription, same contract as a WOD
                                `timeDomain`. Allowed fields depend on
                                `scoringType`: amrap → windowSeconds (required);
                                forTime → timeCapSeconds, rounds (defaults to
                                1); emom → intervalSeconds, intervalCount (both
                                required); tabata → workSeconds, restSeconds,
                                intervalCount (all required); standard/maxLoad →
                                none.
                              properties:
                                windowSeconds:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: null
                                timeCapSeconds:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: 900
                                rounds:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: 3
                                intervalSeconds:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: null
                                intervalCount:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: null
                                workSeconds:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: null
                                restSeconds:
                                  type: integer
                                  nullable: true
                                  minimum: 0
                                  example: null
                            scoreValidation:
                              type: object
                              nullable: true
                              description: >-
                                Optional result caps. timeCapSeconds and
                                expectedIntervals are derived from `timeDomain`
                                when one is set.
                              properties:
                                timeCapSeconds:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: 900
                                maxReps:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: null
                                expectedIntervals:
                                  type: integer
                                  nullable: true
                                  minimum: 1
                                  example: null
                            result:
                              allOf:
                                - type: object
                                  description: >-
                                    Type-specific manual score, the same shape
                                    as the WOD `wodResult`. Only the fields of
                                    the day's scoringType are accepted: amrap →
                                    roundsCompleted, repsPerRound, extraReps,
                                    totalReps (total = rounds × repsPerRound +
                                    extraReps, extraReps < repsPerRound);
                                    forTime → elapsedSeconds, finishedBeforeCap,
                                    totalReps (reps required when not finished
                                    before the cap); emom/tabata → intervalReps,
                                    totalReps (sum of intervals; count must
                                    match intervalCount when set); maxLoad →
                                    maxLoadKg. The stored copy of a forTime
                                    result also carries timeCapSeconds.
                                  properties:
                                    roundsCompleted:
                                      type: integer
                                      minimum: 0
                                      example: 5
                                    repsPerRound:
                                      type: integer
                                      minimum: 1
                                      example: 30
                                    extraReps:
                                      type: integer
                                      minimum: 0
                                      example: 12
                                    totalReps:
                                      type: integer
                                      minimum: 0
                                      example: 162
                                    intervalReps:
                                      type: array
                                      items:
                                        type: integer
                                        minimum: 0
                                      example:
                                        - 12
                                        - 11
                                        - 10
                                    elapsedSeconds:
                                      type: number
                                      minimum: 0
                                      example: 742
                                    finishedBeforeCap:
                                      type: boolean
                                      example: true
                                    timeCapSeconds:
                                      type: integer
                                      nullable: true
                                      readOnly: true
                                      example: 900
                                    maxLoadKg:
                                      type: number
                                      minimum: 0
                                      example: 120
                              nullable: true
                        progress:
                          type: object
                          description: >-
                            Recurring sequence days use only the selected active
                            attempt; after completion progress returns to zero
                            without changing history. Permanent programme
                            progress applies to multi-week/calendar programmes:
                            a non-deleted completed session for this exact plan
                            and moment makes `isCompleted` true all-time,
                            including zero-exercise moments.
                          properties:
                            total:
                              type: integer
                              example: 6
                            completed:
                              type: integer
                              example: 2
                            progressPercentage:
                              type: integer
                              example: 33
                            isCompleted:
                              type: boolean
                              example: false
                        completedSessionsDate:
                          type: array
                          description: >-
                            Every calendar date this day has been completed on —
                            the "Completed On" chips. A RECURRING day becomes
                            available again the moment it is finished, so
                            `progress` resets each time and these dates are the
                            only record of that history. **Most recent FIRST**,
                            so `[0]` is the latest completion (e.g. Sep 16, Sep
                            13, Sep 12, Sep 9). Deduped per date: finishing the
                            same day twice on one date yields ONE entry, whose
                            `sessionId` is the LATEST-finishing attempt of that
                            date, so the chip opens the most recent run. Empty
                            (never null) on a day never completed.
                          items:
                            type: object
                            properties:
                              sessionId:
                                type: string
                                example: 67f1234567890abcdef1234
                                description: >-
                                  The completed session this date was read from,
                                  so the chip can open that attempt. Distinct
                                  from the day's own `sessionId`, which is the
                                  currently relevant session and is null for a
                                  finished recurring day.
                              dayLabel:
                                type: string
                                example: do.
                                description: >-
                                  Abbreviated weekday in the client's language
                                  (en, nl, de, fr, es; English fallback). Render
                                  as supplied.
                              dateLabel:
                                type: string
                                example: sep 10
                                description: >-
                                  Abbreviated month and day in the client's
                                  language, using the existing display timezone.
                              dateText:
                                type: string
                                example: donderdag, 10 september, 2026
                                description: >-
                                  Full date in the client's language for the
                                  completed-dates sheet.
        planWeeks:
          type: array
          description: >-
            Every week of the programme with its real date span — the week
            picker's data, and the authority for which weeks exist (disable the
            ‹ › arrows at the first and last entry). Empty for a plan whose
            weeks have no resolvable dates.
          items:
            type: object
            properties:
              weekNumber:
                type: integer
                minimum: 1
                example: 1
              startDate:
                type: string
                format: date
                pattern: ^\d{4}-\d{2}-\d{2}$
                example: '2026-09-14'
              endDate:
                type: string
                format: date
                pattern: ^\d{4}-\d{2}-\d{2}$
                example: '2026-09-20'
              isSkipWeek:
                type: boolean
                example: false
                description: >-
                  Whether the client skipped this whole week (Skip Week / Resume
                  Week). The week keeps its days and items; it just renders as
                  skipped.
              isCurrentWeek:
                type: boolean
                example: true
                description: >-
                  Whether today falls inside this week's span, in the
                  programme's own timezone. At most ONE entry is `true`; none
                  are once the programme is over or before it starts, so do not
                  assume a match exists. A week with no resolvable `startDate`
                  is never current; one with a `startDate` but no `endDate` (a
                  sequence-layout week) is current from its start onwards.
        week:
          type: object
          nullable: true
          properties:
            weekNumber:
              type: integer
              minimum: 1
              example: 1
            startDate:
              type: string
              format: date
              pattern: ^\d{4}-\d{2}-\d{2}$
              example: '2026-09-14'
            endDate:
              type: string
              format: date
              pattern: ^\d{4}-\d{2}-\d{2}$
              example: '2026-09-20'
            label:
              type: string
              example: This Week
              description: >-
                "This Week" when today falls inside it, otherwise `Week
                {weekNumber}`.
            isCurrentWeek:
              type: boolean
              example: true
            status:
              type: string
              enum:
                - active
                - upcoming
                - done
                - overdue
              example: active
              description: >-
                Where the week sits relative to today decides first; completion
                only separates the two PAST outcomes. `active` = today falls
                inside the week, whatever its progress (a fully finished current
                week stays active). `upcoming` = it has not started. `done` = it
                is over and EVERY item is completed. `overdue` = it is over with
                work outstanding. Computed from the items — including activities
                — not from `isCompleted`, which counts workouts only.
            isAvailable:
              type: boolean
              example: true
            isSkipWeek:
              type: boolean
              example: false
              description: >-
                Whether the client skipped this whole week (Skip Week / Resume
                Week). The week keeps its days and items; it just renders as
                skipped.
            isCompleted:
              type: boolean
              example: false
              description: >-
                Whether every plan MOMENT (workout) in the week is complete.
                Ignores activity items, so it can be true while `status` is
                `overdue`.
            progressPercentage:
              type: integer
              example: 50
            days:
              type: array
              description: >-
                All seven days, so the screen renders the whole week from one
                request. Deep-equal to the same week's `days` in `weeks[]`.
              items:
                type: object
                properties:
                  weekday:
                    type: integer
                    minimum: 1
                    maximum: 7
                    example: 1
                  date:
                    type: string
                    format: date
                    pattern: ^\d{4}-\d{2}-\d{2}$
                    example: '2026-09-14'
                  isRestDay:
                    type: boolean
                    example: false
                  isSkipDay:
                    type: boolean
                    example: false
                    description: >-
                      Whether the client skipped this day (Skip Day / Resume
                      Day). The day keeps its `items`; it just renders as
                      skipped. Never `true` on a rest day.
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        _id:
                          type: string
                          example: 67f1234567890abcdef1234
                        type:
                          type: string
                          enum:
                            - workout
                            - activity
                          example: workout
                        order:
                          type: integer
                          example: 1
                        startTime:
                          type: string
                          example: ''
                          description: e.g. "10:45".
                        plannedDurationMinutes:
                          type: integer
                          nullable: true
                          example: 60
                        status:
                          type: string
                          nullable: true
                          enum:
                            - completed
                            - in_progress
                            - overdue
                            - today
                            - upcoming
                          example: completed
                          description: >-
                            The OCCURRENCE state for this date — not the
                            session's, which is `workout.sessionStatus`.
                        name:
                          type: string
                          example: Upper Body
                        description:
                          type: string
                          example: ''
                        activityType:
                          type: string
                          nullable: true
                          example: running
                          description: Activity items only; null on a workout item.
                        targetDistanceMeters:
                          type: integer
                          nullable: true
                          example: 5000
                        planMomentId:
                          type: string
                          example: 67f1234567890abcdef1234
                          nullable: true
                          description: Workout items only; null on an activity item.
                        workout:
                          type: object
                          nullable: true
                          description: >-
                            The plan day's own numbers, so a workout row needs
                            no join back to `days[]`. Null on an activity item.
                          properties:
                            planMomentId:
                              type: string
                              example: 67f1234567890abcdef1234
                            name:
                              type: string
                              example: Upper Body
                            description:
                              type: string
                              example: ''
                            exerciseCount:
                              type: integer
                              example: 6
                            exercises:
                              type: array
                              description: >-
                                The day's prescribed exercises — the same list
                                and shape as `days[].exercises`, so a week item
                                renders its exercise rows without joining back
                                to `days[]` (which is filtered to available
                                weeks and has nothing to find for a locked one).
                              items:
                                type: object
                                properties:
                                  _id:
                                    type: string
                                    example: 67f1234567890abcdef1234
                                  exerciseId:
                                    type: string
                                    example: 67f1234567890abcdef1234
                                  exerciseName:
                                    type: string
                                    example: Incline Dumbbell Press
                                  thumbnail:
                                    type: string
                                    example: https://cdn.example.com/ex.jpg
                                  primaryMuscle:
                                    type: string
                                    example: Chest
                                  equipment:
                                    type: array
                                    items:
                                      type: string
                                  sets:
                                    type: integer
                                    example: 4
                                  minReps:
                                    type: integer
                                    example: 8
                                  maxReps:
                                    type: integer
                                    example: 12
                                  rest:
                                    type: integer
                                    example: 90
                                  intensity:
                                    type: string
                                    example: Medium
                                  difficulty:
                                    type: string
                                    example: Intermediate
                                  notes:
                                    type: string
                                    example: ''
                                  isFavourite:
                                    type: boolean
                                    example: false
                            progress:
                              type: object
                              properties:
                                total:
                                  type: integer
                                  example: 6
                                completed:
                                  type: integer
                                  example: 6
                                progressPercentage:
                                  type: integer
                                  example: 100
                                isCompleted:
                                  type: boolean
                                  example: true
                            sessionId:
                              type: string
                              example: 67f1234567890abcdef1234
                              nullable: true
                            sessionStatus:
                              type: string
                              nullable: true
                              enum:
                                - in_progress
                                - paused
                                - completed
                              example: completed
                            isCompleted:
                              type: boolean
                              example: true
                            scoring:
                              type: object
                              nullable: true
                              description: >-
                                Scoring of a plan day. Null for an ordinary
                                standard day. `acceptsManualResult` is true only
                                for exercise-free scored days
                                (`descriptionOnly`), which take a `wodResult` on
                                PATCH
                                /app/v1/workout/performance/sessions/{sessionId}/complete.
                                `result` is the saved score of the selected
                                session, or null.
                              properties:
                                scoringType:
                                  type: string
                                  enum:
                                    - standard
                                    - forTime
                                    - amrap
                                    - emom
                                    - tabata
                                    - maxLoad
                                  example: forTime
                                descriptionOnly:
                                  type: boolean
                                  example: true
                                acceptsManualResult:
                                  type: boolean
                                  example: true
                                targetValue:
                                  type: number
                                  nullable: true
                                  example: null
                                timeDomain:
                                  type: object
                                  nullable: true
                                  description: >-
                                    Clock prescription, same contract as a WOD
                                    `timeDomain`. Allowed fields depend on
                                    `scoringType`: amrap → windowSeconds
                                    (required); forTime → timeCapSeconds, rounds
                                    (defaults to 1); emom → intervalSeconds,
                                    intervalCount (both required); tabata →
                                    workSeconds, restSeconds, intervalCount (all
                                    required); standard/maxLoad → none.
                                  properties:
                                    windowSeconds:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: null
                                    timeCapSeconds:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: 900
                                    rounds:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: 3
                                    intervalSeconds:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: null
                                    intervalCount:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: null
                                    workSeconds:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: null
                                    restSeconds:
                                      type: integer
                                      nullable: true
                                      minimum: 0
                                      example: null
                                scoreValidation:
                                  type: object
                                  nullable: true
                                  description: >-
                                    Optional result caps. timeCapSeconds and
                                    expectedIntervals are derived from
                                    `timeDomain` when one is set.
                                  properties:
                                    timeCapSeconds:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: 900
                                    maxReps:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: null
                                    expectedIntervals:
                                      type: integer
                                      nullable: true
                                      minimum: 1
                                      example: null
                                result:
                                  allOf:
                                    - type: object
                                      description: >-
                                        Type-specific manual score, the same
                                        shape as the WOD `wodResult`. Only the
                                        fields of the day's scoringType are
                                        accepted: amrap → roundsCompleted,
                                        repsPerRound, extraReps, totalReps
                                        (total = rounds × repsPerRound +
                                        extraReps, extraReps < repsPerRound);
                                        forTime → elapsedSeconds,
                                        finishedBeforeCap, totalReps (reps
                                        required when not finished before the
                                        cap); emom/tabata → intervalReps,
                                        totalReps (sum of intervals; count must
                                        match intervalCount when set); maxLoad →
                                        maxLoadKg. The stored copy of a forTime
                                        result also carries timeCapSeconds.
                                      properties:
                                        roundsCompleted:
                                          type: integer
                                          minimum: 0
                                          example: 5
                                        repsPerRound:
                                          type: integer
                                          minimum: 1
                                          example: 30
                                        extraReps:
                                          type: integer
                                          minimum: 0
                                          example: 12
                                        totalReps:
                                          type: integer
                                          minimum: 0
                                          example: 162
                                        intervalReps:
                                          type: array
                                          items:
                                            type: integer
                                            minimum: 0
                                          example:
                                            - 12
                                            - 11
                                            - 10
                                        elapsedSeconds:
                                          type: number
                                          minimum: 0
                                          example: 742
                                        finishedBeforeCap:
                                          type: boolean
                                          example: true
                                        timeCapSeconds:
                                          type: integer
                                          nullable: true
                                          readOnly: true
                                          example: 900
                                        maxLoadKg:
                                          type: number
                                          minimum: 0
                                          example: 120
                                  nullable: true
                        activity:
                          type: object
                          nullable: true
                          description: >-
                            What the client actually DID for this activity
                            occurrence; the planned values live on the item
                            itself. Null until logged, and always null on a
                            workout item.
                          properties:
                            performanceId:
                              type: string
                              example: 67f1234567890abcdef1234
                            status:
                              type: string
                              enum:
                                - in_progress
                                - completed
                                - cancelled
                              example: in_progress
                            startedAt:
                              type: string
                              format: date-time
                              example: '2026-04-12T10:00:00.000Z'
                              nullable: true
                            completedAt:
                              type: string
                              format: date-time
                              example: '2026-04-12T10:00:00.000Z'
                              nullable: true
                            actualDurationMinutes:
                              type: integer
                              nullable: true
                              example: 32
                            actualDistanceMeters:
                              type: integer
                              nullable: true
                              example: 5100
                            rpe:
                              type: integer
                              nullable: true
                              example: 7
                            notes:
                              type: string
                              example: ''
          description: >-
            The week selected by `?startDate=` (or the week containing today
            when omitted), with all seven days and their items. Null when the
            plan has no dated weeks, or when `startDate` fell outside every
            week.
        weekDates:
          type: array
          description: >-
            `week`'s seven-day strip: one entry per weekday with its label, date
            and status dots. Empty whenever `week` is null.
          items:
            type: object
            properties:
              weekday:
                type: integer
                minimum: 1
                maximum: 7
                example: 1
              date:
                type: string
                format: date
                pattern: ^\d{4}-\d{2}-\d{2}$
                example: '2026-09-14'
              dayLabel:
                type: string
                example: Mon
              dayName:
                type: string
                example: Monday
              dayOfMonth:
                type: integer
                nullable: true
                example: 14
              isRestDay:
                type: boolean
                example: false
              isSkipDay:
                type: boolean
                example: false
                description: >-
                  Whether the client skipped this day (the day keeps its items).
                  Never `true` on a rest day.
              isToday:
                type: boolean
                example: false
              itemCount:
                type: integer
                example: 2
                description: >-
                  How many items this day actually holds. Unaffected by the
                  de-duplication of `statuses` below.
              statuses:
                type: array
                description: >-
                  ONE entry per DISTINCT state, in first-seen order — the strip
                  shows a day's states, not its item count, so a day holding
                  three finished workouts renders a single `completed` dot while
                  a mixed day still shows every state it holds. A rest day is a
                  single `rest`. Empty when the day has nothing scheduled.
                  Everything that is neither finished nor underway is
                  `not_started`. Use `itemCount` for how many items the day
                  actually has.
                items:
                  type: string
                  enum:
                    - completed
                    - in_process
                    - not_started
                    - rest
                example:
                  - completed
                  - not_started
        totalDays:
          type: integer
          example: 6
          description: Count of days in the backward-compatible flat `days` list.
        totalExercises:
          type: integer
          example: 36
          description: Exercise count across the backward-compatible flat `days` list.
        isTemplateFavourite:
          type: boolean
          example: false
    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
    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'
    PublicWorkoutSourceWorkoutExerciseSearchMedia:
      type: object
      properties:
        type:
          type: string
          enum:
            - image
            - video
          example: image
        url:
          type: string
          example: https://cdn.example.com/exercises/bench.jpg
        platform:
          type:
            - string
            - 'null'
          enum:
            - null
            - s3
            - youtube
            - tiktok
          example: null
        videoId:
          type:
            - string
            - 'null'
          example: null
        thumbnail:
          type:
            - string
            - 'null'
          example: null
        duration:
          type:
            - number
            - 'null'
          example: null
        width:
          type:
            - number
            - 'null'
          example: 1280
        height:
          type:
            - number
            - 'null'
          example: 720
    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.