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

# Duplicate a template or save an assigned plan as a template

> Creates an active template with copied days, exercises, supersets and programme structure. By default only templates can be copied and names receive `(Copy)`. With `saveAsTemplate=true`, company staff can copy an accessible assigned member plan: its name is retained while the client link, assignment dates and history are excluded. The source is unchanged. Requires the existing training.programmes.duplicate permission and access to the source member.

Requires the workout_plans:write scope. This operation maps to /app/v1/workout/plans/duplicate/:templateId and retains its Workout V2 permission, feature-flag, and resource-scope checks.

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



## OpenAPI

````yaml /openapi/public-v1.json post /public/v1/workout/plans/duplicate/{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/plans/duplicate/{templateId}:
    post:
      tags:
        - Workout
      summary: Duplicate a template or save an assigned plan as a template
      description: >-
        Creates an active template with copied days, exercises, supersets and
        programme structure. By default only templates can be copied and names
        receive `(Copy)`. With `saveAsTemplate=true`, company staff can copy an
        accessible assigned member plan: its name is retained while the client
        link, assignment dates and history are excluded. The source is
        unchanged. Requires the existing training.programmes.duplicate
        permission and access to the source member.


        Requires the workout_plans:write scope. This operation maps to
        /app/v1/workout/plans/duplicate/:templateId and retains its Workout V2
        permission, feature-flag, and resource-scope checks.


        The clientId path parameter is resolved inside the company bound to the
        Public API token when present.
      operationId: publicWorkoutpostPublicV1WorkoutPlansDuplicateTemplateId
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 200
            example: booking-create-20260714-001
          description: >-
            Required for Public API write requests. Reusing the same key with
            the same method, path, and body replays the stored successful
            response; reusing it with a different request returns `409
            idempotency.conflict`.
        - in: path
          name: templateId
          required: true
          description: Source template or assigned workout plan id.
          schema:
            type: string
            example: 67f1234567890abcdef1234
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: >-
                #/components/schemas/PublicWorkoutSourcePOSTAppV1WorkoutPlansDuplicateTemplateIdRequest
      responses:
        '200':
          description: Full document of the newly created template copy.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PublicApiWriteSuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: >-
                          #/components/schemas/PublicWorkoutSourcePOSTAppV1WorkoutPlansDuplicateTemplateIdResponse200
              examples:
                success:
                  summary: Successful response
                  value:
                    data:
                      workout:
                        runningPlan:
                          goal:
                            distance: 5K
                            goalTimeSec: 1
                            raceDate: '2026-07-14'
                            acceptGoalAsChallenge: true
                          goalUpdatedAt: '2026-07-14T10:00:00.000Z'
                          goalUpdatedBy: string
                          adaptationRevision: 1
                          lastReviewedWeek: 1
                          lastAdaptedAt: '2026-07-14T10:00:00.000Z'
                          generation:
                            templateKey: 5K
                            variant: true
                            runWeekdays:
                              - 1
                            qualityWeekdays:
                              - 1
                            longRunWeekday: 1
                            phases:
                              base: 1
                              threshold: 1
                              sharpen: 1
                            taperWeeks: 1
                            sourceVersion: plansjablonen-v259-1
                            policyVersion: safety-calendar-v1
                            generatedAt: '2026-07-14T10:00:00.000Z'
                            generatedBy: string
                            profileUpdatedAt: '2026-07-14T10:00:00.000Z'
                            totalWeeks: 1
                            concreteWeeks: 1
                            baselineKm: 1
                            peakKm: 1
                            thresholdSecPerKm: 1
                            warnings:
                              - string
                          weekSummaries:
                            - weekNumber: 1
                              weekStart: '2026-07-14'
                              phase: 1
                              phaseName: Base building
                              theme: Base building
                              weekKind: build
                              rotationWeek: 1
                              targetVolumeKm: 1
                              concrete: true
                              requestedVolumeKm: 1
                              allocatedKm: 1
                              remainingKm: 1
                              concreteRequested: true
                              sessions:
                                - weekday: 1
                                  code: drempel_c
                                  sessionType: threshold_continuous
                                  controlMode: hr
                                  longRunTargetMinutes: 1
                                  ladder:
                                    step: 1
                                    reps: 1
                                    kmPerRep: 1
                                  continuousMinutes: 1
                                  recoveryRangeSec:
                                    low: 1
                                    high: 1
                                  cardio:
                                    slotKey: string
                                    sessionType: easy
                                    controlMode: pace
                                    ladderStep: 1
                                    poolLengthM: 25
                                    blocks:
                                      - _id: 67f1234567890abcdef1234
                                        repeat: 1
                                        steps:
                                          - _id: 67f1234567890abcdef1234
                                            kind: warmup
                                            durationType: time
                                            durationValue: 1
                                            target:
                                              kind: paceZone
                                              zone: E
                                              basis: lthr
                                              low: 1
                                              high: 1
                                            secondaryTarget:
                                              kind: paceZone
                                              zone: E
                                              basis: lthr
                                              low: 1
                                              high: 1
                                            notes: string
                                            stroke: free
                                            equipment:
                                              - pullBuoy
                                            restMode: rest
                                            sendOffSec: 1
                                            cadence:
                                              low: 1
                                              high: 1
                                    sourceTemplateId: null
                                  totals:
                                    durationSec: 1
                                    distanceM: 1
                                    plannedDurationMinutes: 1
                                    targetDistanceMeters: 1
                                    zoneTimeSec: {}
                        _id: 67f1234567890abcdef1234
                        companyId: 67f1234567890abcdef1234
                        clientId: null
                        sourceTemplateId: null
                        addedBy: 67f1234567890abcdef1234
                        addedByRole: Coach
                        status: Draft
                        templateType: Template
                        name:
                          - lang: en
                            value: Full Body Strength
                          - lang: nl
                            value: Full Body Kracht
                        description:
                          - lang: en
                            value: Full Body Strength
                          - lang: nl
                            value: Full Body Kracht
                        difficulty: Intermediate
                        intensity: Medium
                        duration:
                          startDate: '2026-04-15'
                          endDate: '2026-06-15'
                          hasEndDate: true
                        images:
                          - url: https://cdn.example.com/workouts/plan-cover.jpg
                            width: 1280
                            height: 720
                        videos:
                          - url: https://cdn.example.com/workouts/demo.mp4
                            platform: s3
                            videoId: ''
                            thumbnail: https://cdn.example.com/workouts/demo-thumb.jpg
                            duration: 84
                            width: 1920
                            height: 1080
                        category: Strength
                        goal: Strength
                        periodizationType: Linear
                        popularity: 0
                        useDefaultExerciseSettings: true
                        defaultExerciseValues:
                          sets: 3
                          reps: 10-12
                          rest: 90
                          weight: 0
                          repTempo: '3010'
                          duration: 0
                        planMoments:
                          - _id: 67f1234567890abcdef1234
                            name:
                              - lang: en
                                value: Full Body Strength
                              - lang: nl
                                value: Full Body Kracht
                            description:
                              - lang: en
                                value: Full Body Strength
                              - lang: nl
                                value: Full Body Kracht
                            dayOrder: 1
                            order: 0
                            weekNumber: 1
                            sessionOrder: 1
                            date: '2026-04-15'
                            scoringType: forTime
                            targetValue: null
                            descriptionOnly: true
                            timeDomain:
                              windowSeconds: null
                              timeCapSeconds: 900
                              rounds: 3
                              intervalSeconds: null
                              intervalCount: null
                              workSeconds: null
                              restSeconds: null
                            scoreValidation:
                              timeCapSeconds: 900
                              maxReps: null
                              expectedIntervals: null
                            exercises:
                              - _id: 67f1234567890abcdef1234
                                exerciseId: 67f1234567890abcdef1234
                                type: reps
                                minReps: 6
                                maxReps: 8
                                notes: Pause 1 second on the chest.
                                sets: 4
                                metric: kg
                                rest: 120
                                intensity: High
                                difficulty: Intermediate
                                groupId: super-a
                                rpe: 8
                                rmPercentage: 75
                                tempo: '3010'
                                perSetTrackingEnabled: true
                                rpeEnabled: false
                                showSetRpe: true
                                setsData:
                                  - setNumber: 1
                                    setType: 'N'
                                    metric: '12'
                                    repetition: 8-12
                                    weight: 60
                                    rest: 90
                                    rpe: 7.5
                                    duration: 45
                                    distance: 250
                                setupSettingsEnabled: true
                                setupFields:
                                  - key: seat
                                    label: Seat
                                    type: text
                                    unit: ''
                                    options:
                                      - '1'
                                      - '2'
                                      - '3'
                                      - '4'
                                    order: 1
                                    required: false
                                    enabled: true
                                    defaultValue: 3
                                order: 0
                                isDeleted: false
                                deletedAt: null
                                createdAt: '2026-04-12T10:00:00.000Z'
                                updatedAt: '2026-04-12T10:00:00.000Z'
                            isDeleted: false
                            deletedAt: null
                            createdAt: '2026-04-12T10:00:00.000Z'
                            updatedAt: '2026-04-12T10:00:00.000Z'
                        visibility: Company
                        isDeleted: false
                        deletedAt: null
                        createdAt: '2026-04-12T10:00:00.000Z'
                        updatedAt: '2026-04-12T10:00:00.000Z'
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                      idempotency:
                        replayed: false
        '400':
          description: Malformed template id or company context is missing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                invalidRequest:
                  summary: Invalid request
                  value:
                    error:
                      code: 400
                      key: request.invalid
                      message: The request is invalid.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                idempotencyRequired:
                  summary: Missing Idempotency-Key
                  value:
                    error:
                      code: 400
                      key: idempotency.required
                      message: >-
                        Idempotency-Key header is required for Public API write
                        requests.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                idempotencyInvalid:
                  summary: Invalid Idempotency-Key
                  value:
                    error:
                      code: 400
                      key: idempotency.invalid
                      message: Idempotency-Key header must be 200 characters or fewer.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '401':
          $ref: '#/components/schemas/ErrorResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                missingBearer:
                  summary: Missing Bearer token
                  value:
                    error:
                      code: 401
                      key: auth.missing_bearer
                      message: Authorization Bearer token is required.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                invalidToken:
                  summary: Invalid or expired token
                  value:
                    error:
                      code: 401
                      key: auth.invalid_token
                      message: The access token is invalid or expired.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                invalidClient:
                  summary: Inactive or revoked client
                  value:
                    error:
                      code: 401
                      key: auth.invalid_client
                      message: The Public API client is inactive or revoked.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '403':
          description: >-
            Workout V2 programme calendar is disabled for the active company
            (`WORKOUT_V2_PROGRAMME_CALENDAR_DISABLED`) or the caller lacks
            access.
          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: Source template was 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'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                idempotencyConflict:
                  summary: Idempotency-Key conflict
                  value:
                    error:
                      code: 409
                      key: idempotency.conflict
                      message: >-
                        Idempotency-Key was already used with a different
                        request.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
                idempotencyInProgress:
                  summary: Idempotency-Key in progress
                  value:
                    error:
                      code: 409
                      key: idempotency.in_progress
                      message: >-
                        Idempotency-Key is already processing for this Public
                        API client.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '422':
          $ref: '#/components/schemas/ErrorResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                emptyBody:
                  summary: Empty or invalid JSON object body
                  value:
                    error:
                      code: 422
                      key: EMPTY_BODY
                      message: Request body must be a non-empty JSON object.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
        '429':
          $ref: '#/components/schemas/ErrorResponse'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                rateLimitExceeded:
                  summary: Rate limit exceeded
                  value:
                    error:
                      code: 429
                      key: rate_limit.exceeded
                      message: Too many Public API requests. Retry later.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 0
                        resetSeconds: 1
                        retryAfterSeconds: 1
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
              examples:
                serverInternalError:
                  summary: Unexpected server error
                  value:
                    error:
                      code: 500
                      key: server.internal_error
                      message: An unexpected Public API server error occurred.
                    meta:
                      requestId: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
                      rateLimit:
                        limit: 10
                        remaining: 9
                        resetSeconds: 1
      security:
        - PublicBearerAuth: []
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: >-
            curl -X POST
            "https://api.fitsociety.io/public/v1/workout/plans/duplicate/{templateId}"
            \
              -H "Authorization: Bearer <access_token>" \
              -H "Idempotency-Key: <stable_request_key>" \
              -H "Content-Type: application/json" \
              -d '{}'
components:
  schemas:
    PublicWorkoutSourcePOSTAppV1WorkoutPlansDuplicateTemplateIdRequest:
      type: object
      properties:
        saveAsTemplate:
          type: boolean
          default: false
          description: Copy an assigned member plan into a standalone template.
    PublicApiWriteSuccessResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          type: object
        meta:
          $ref: '#/components/schemas/PublicApiWriteMeta'
    PublicWorkoutSourcePOSTAppV1WorkoutPlansDuplicateTemplateIdResponse200:
      type: object
      properties:
        workout:
          type: object
          properties:
            runningPlan:
              type: object
              properties:
                goal:
                  type: object
                  additionalProperties: false
                  required:
                    - distance
                    - goalTimeSec
                    - raceDate
                  properties:
                    distance:
                      type: string
                      enum:
                        - 5K
                        - 10K
                        - half
                        - marathon
                    goalTimeSec:
                      type: number
                      exclusiveMinimum: 0
                      description: >-
                        Finish time in seconds. Localized numeric strings are
                        accepted on input.
                    raceDate:
                      type: string
                      format: date
                      description: Canonical YYYY-MM-DD athlete calendar date.
                    acceptGoalAsChallenge:
                      type: boolean
                      default: false
                goalUpdatedAt:
                  type: string
                  format: date-time
                  readOnly: true
                goalUpdatedBy:
                  type: string
                  readOnly: true
                adaptationRevision:
                  type: integer
                  minimum: 0
                  readOnly: true
                lastReviewedWeek:
                  type: integer
                  minimum: 0
                  maximum: 104
                  readOnly: true
                lastAdaptedAt:
                  type: string
                  format: date-time
                  readOnly: true
                generation:
                  type: object
                  readOnly: true
                  properties:
                    templateKey:
                      type: string
                      enum:
                        - 5K
                        - 10K
                        - half
                        - marathon
                    variant:
                      type: boolean
                    runWeekdays:
                      type: array
                      uniqueItems: true
                      items:
                        type: integer
                        minimum: 1
                        maximum: 7
                    qualityWeekdays:
                      type: array
                      uniqueItems: true
                      items:
                        type: integer
                        minimum: 1
                        maximum: 7
                    longRunWeekday:
                      type: integer
                      minimum: 1
                      maximum: 7
                    phases:
                      type: object
                      properties:
                        base:
                          type: integer
                        threshold:
                          type: integer
                        sharpen:
                          type: integer
                    taperWeeks:
                      type: integer
                      enum:
                        - 1
                        - 2
                    sourceVersion:
                      type: string
                      enum:
                        - plansjablonen-v259-1
                    policyVersion:
                      type: string
                      enum:
                        - safety-calendar-v1
                    generatedAt:
                      type: string
                      format: date-time
                    generatedBy:
                      type: string
                    profileUpdatedAt:
                      type: string
                      format: date-time
                    totalWeeks:
                      type: integer
                      minimum: 1
                      maximum: 104
                    concreteWeeks:
                      type: integer
                      minimum: 1
                      maximum: 3
                    baselineKm:
                      type: number
                    peakKm:
                      type: number
                    thresholdSecPerKm:
                      type: number
                    warnings:
                      type: array
                      items:
                        type: string
                weekSummaries:
                  type: array
                  items:
                    type: object
                    properties:
                      weekNumber:
                        type: integer
                      weekStart:
                        type: string
                        format: date
                      phase:
                        type: integer
                        enum:
                          - 1
                          - 2
                          - 3
                      phaseName:
                        type: string
                        enum:
                          - Base building
                          - Threshold development
                          - Sharpening
                      theme:
                        type: string
                        enum:
                          - Base building
                          - Threshold development
                          - Sharpening
                      weekKind:
                        type: string
                        enum:
                          - build
                          - race
                          - taper
                          - sharpen
                          - deload
                      rotationWeek:
                        type: integer
                        enum:
                          - 1
                          - 2
                          - 3
                      targetVolumeKm:
                        type: number
                      concrete:
                        type: boolean
                      requestedVolumeKm:
                        type: number
                      allocatedKm:
                        type: number
                      remainingKm:
                        type: number
                      concreteRequested:
                        type: boolean
                      sessions:
                        type: array
                        items:
                          type: object
                          properties:
                            weekday:
                              type: integer
                              minimum: 1
                              maximum: 7
                            code:
                              type: string
                              enum:
                                - drempel_c
                                - drempel_r
                                - interval
                                - doeltempo
                                - zone2
                                - long_run
                            sessionType:
                              type: string
                              enum:
                                - threshold_continuous
                                - threshold_reps
                                - interval
                                - race_pace
                                - zone2
                                - long_run
                            controlMode:
                              type: string
                              enum:
                                - hr
                                - pace
                            longRunTargetMinutes:
                              type: number
                            ladder:
                              type: object
                              required:
                                - step
                                - reps
                                - kmPerRep
                              properties:
                                step:
                                  type: integer
                                  minimum: 1
                                  maximum: 8
                                reps:
                                  type: integer
                                  minimum: 1
                                kmPerRep:
                                  type: number
                                  exclusiveMinimum: 0
                            continuousMinutes:
                              type: number
                            recoveryRangeSec:
                              type: object
                              properties:
                                low:
                                  type: number
                                high:
                                  type: number
                            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
                            totals:
                              type: object
                              properties:
                                durationSec:
                                  type: number
                                  minimum: 0
                                distanceM:
                                  type: number
                                  minimum: 0
                                plannedDurationMinutes:
                                  type: number
                                  minimum: 0
                                targetDistanceMeters:
                                  type: number
                                  minimum: 0
                                zoneTimeSec:
                                  type: object
                                  additionalProperties:
                                    type: number
                                    minimum: 0
                              additionalProperties: false
            _id:
              type: string
              example: 67f1234567890abcdef1234
            companyId:
              type: string
              example: 67f1234567890abcdef1234
            clientId:
              type:
                - string
                - 'null'
              example: null
            sourceTemplateId:
              type:
                - string
                - 'null'
              example: null
            addedBy:
              type: string
              example: 67f1234567890abcdef1234
            addedByRole:
              type: string
              enum:
                - Coach
                - Client
                - Fitsociety
                - Admin
              example: Coach
            status:
              type: string
              enum:
                - Active
                - Inactive
                - Draft
              example: Draft
            templateType:
              type: string
              enum:
                - Template
                - Assigned
              example: Template
            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
              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
            difficulty:
              type: string
              enum:
                - Beginner
                - Intermediate
                - Advanced
              example: Intermediate
            intensity:
              type: string
              enum:
                - Low
                - Medium
                - High
              example: Medium
            duration:
              type: object
              properties:
                startDate:
                  type: string
                  example: '2026-04-15'
                endDate:
                  type: string
                  example: '2026-06-15'
                hasEndDate:
                  type: boolean
                  example: true
            images:
              type: array
              items:
                type: object
                required:
                  - url
                properties:
                  url:
                    type: string
                    format: uri
                    example: https://cdn.example.com/workouts/plan-cover.jpg
                  width:
                    type:
                      - number
                      - 'null'
                    example: 1280
                  height:
                    type:
                      - number
                      - 'null'
                    example: 720
            videos:
              type: array
              items:
                type: object
                required:
                  - url
                  - platform
                properties:
                  url:
                    type: string
                    format: uri
                    example: https://cdn.example.com/workouts/demo.mp4
                  platform:
                    type: string
                    enum:
                      - s3
                      - youtube
                      - tiktok
                    example: s3
                  videoId:
                    type: string
                    example: ''
                  thumbnail:
                    type: string
                    format: uri
                    example: https://cdn.example.com/workouts/demo-thumb.jpg
                  duration:
                    type:
                      - number
                      - 'null'
                    example: 84
                  width:
                    type:
                      - number
                      - 'null'
                    example: 1920
                  height:
                    type:
                      - number
                      - 'null'
                    example: 1080
            category:
              type: string
              enum:
                - Agility & Speed
                - Strength
                - Hypertrophy
                - Strength - Dynamic
                - Strength - Explosive
                - Strength - Static
                - Cardio
                - Cardio - Duration
                - Cardio - Interval
                - HIIT
                - Crossfit
                - Mobility
                - Stretching
                - Core
                - Coordination & Balance
                - Movement Prep
                - Functional Training
                - Recovery
                - Sports
                - Martial Arts
                - Yoga
                - Other
                - GROUP_WORKOUT_TEMPLATE
                - Plyometric
                - Cycling
                - Cardio - interval
                - Mobilisation
                - Stabilisation
                - Daily Living
                - Target zone
                - Respiration
                - Breathing
                - Massage
                - Outdoor sports
                - Indoor sports
                - Dance
                - Group Lessons
                - Martial arts
                - Material Arts
                - Fight skills
                - Mind and body
                - ''
                - default
              example: Strength
            goal:
              type: string
              enum:
                - Weight Loss
                - Hypertrophy
                - Endurance
                - Flexibility
                - General Fitness
                - Strength
              example: Strength
            periodizationType:
              type: string
              enum:
                - Linear
                - Non-Linear
                - Block
                - Undulating
                - None
              example: Linear
            popularity:
              type: number
              example: 0
            useDefaultExerciseSettings:
              type: boolean
              example: true
            defaultExerciseValues:
              oneOf:
                - type: object
                  required:
                    - sets
                    - reps
                    - rest
                    - repTempo
                  properties:
                    sets:
                      type: number
                      minimum: 1
                      example: 3
                    reps:
                      oneOf:
                        - type: string
                          pattern: ^\d+(-\d+)?$
                        - type: number
                          minimum: 1
                      example: 10-12
                    rest:
                      type: number
                      minimum: 0
                      example: 90
                    weight:
                      type: number
                      example: 0
                    repTempo:
                      type: string
                      example: '3010'
                    duration:
                      type: number
                      example: 0
                - type: 'null'
            planMoments:
              type: array
              items:
                type: object
                properties:
                  _id:
                    type: string
                    example: 67f1234567890abcdef1234
                  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
                    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
                  dayOrder:
                    type: number
                    example: 1
                  order:
                    type: number
                    example: 0
                  weekNumber:
                    type: integer
                    minimum: 1
                    example: 1
                  sessionOrder:
                    type: integer
                    minimum: 1
                    example: 1
                  date:
                    type: string
                    example: '2026-04-15'
                  scoringType:
                    type: string
                    enum:
                      - standard
                      - forTime
                      - amrap
                      - emom
                      - tabata
                      - maxLoad
                    default: standard
                    example: forTime
                    description: >-
                      Scoring mechanism. Must not be `standard` when
                      `descriptionOnly` is true.
                  targetValue:
                    type: number
                    nullable: true
                    minimum: 0
                    example: null
                  descriptionOnly:
                    type: boolean
                    default: false
                    example: true
                    description: >-
                      Marks an exercise-free scored day: the full workout lives
                      in `description` (required), `exercises` must stay empty,
                      `scoringType` must not be standard, and amrap/emom/tabata
                      require their full `timeDomain`. Completing the day
                      requires a manual `wodResult`. Enforced on every write
                      path, including client day edits. Moments that merely have
                      no exercises are NOT scored unless this is true.
                  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
                  exercises:
                    type: array
                    items:
                      type: object
                      properties:
                        _id:
                          type: string
                          example: 67f1234567890abcdef1234
                        exerciseId:
                          type: string
                          example: 67f1234567890abcdef1234
                        type:
                          type: string
                          enum:
                            - reps
                            - time
                            - distance
                          example: reps
                        minReps:
                          type: number
                          example: 6
                        maxReps:
                          type: number
                          example: 8
                        notes:
                          type: string
                          example: Pause 1 second on the chest.
                        sets:
                          type: number
                          example: 4
                        metric:
                          type: string
                          example: kg
                        rest:
                          type: number
                          example: 120
                        intensity:
                          type: string
                          enum:
                            - Low
                            - Medium
                            - High
                          example: High
                        difficulty:
                          type: string
                          enum:
                            - Beginner
                            - Intermediate
                            - Advanced
                          example: Intermediate
                        groupId:
                          type: string
                          example: super-a
                        rpe:
                          type: number
                          example: 8
                        rmPercentage:
                          type: number
                          example: 75
                        tempo:
                          type: string
                          example: '3010'
                        perSetTrackingEnabled:
                          type: boolean
                          example: true
                        rpeEnabled:
                          type: boolean
                          example: false
                          description: >-
                            Master RPE switch for this plan exercise. False
                            hides current targets and inputs without clearing
                            prescriptions or recorded history. Missing legacy
                            values retain RPE when an exercise target or per-set
                            preference exists. Independent from showSetRpe.
                        showSetRpe:
                          type: boolean
                          example: true
                          description: >-
                            Stored per-set RPE preference. Live input is visible
                            only when RPE is enabled; authoring retains this
                            preference while rpeEnabled is false.
                        setsData:
                          type: array
                          items:
                            type: object
                            properties:
                              setNumber:
                                type: integer
                                minimum: 1
                                example: 1
                              setType:
                                type: string
                                enum:
                                  - 'N'
                                  - W
                                  - D
                                  - F
                                example: 'N'
                                description: >-
                                  Planned set type: N normal, W warm-up, D drop
                                  set, F to failure.
                              metric:
                                type: string
                                example: '12'
                              repetition:
                                type: string
                                example: 8-12
                              weight:
                                type: number
                                example: 60
                              rest:
                                type: number
                                example: 90
                              rpe:
                                type:
                                  - number
                                  - 'null'
                                minimum: 0
                                maximum: 10
                                example: 7.5
                                description: >-
                                  Optional target RPE for this set. It does not
                                  inherit the exercise-level RPE value.
                              duration:
                                type: number
                                example: 45
                              distance:
                                type: number
                                example: 250
                        setupSettingsEnabled:
                          type: boolean
                          example: true
                          description: >-
                            Enables the exercise-level machine or equipment
                            setup settings block.
                        setupFields:
                          type: array
                          maxItems: 8
                          items:
                            type: object
                            required:
                              - key
                              - label
                            properties:
                              key:
                                type: string
                                example: seat
                              label:
                                type: string
                                example: Seat
                              type:
                                type: string
                                enum:
                                  - text
                                  - number
                                  - select
                                example: text
                              unit:
                                type: string
                                example: ''
                              options:
                                type: array
                                items:
                                  type: string
                                example:
                                  - '1'
                                  - '2'
                                  - '3'
                                  - '4'
                              order:
                                type: number
                                example: 1
                              required:
                                type: boolean
                                example: false
                              enabled:
                                type: boolean
                                example: true
                              defaultValue:
                                oneOf:
                                  - type: string
                                  - type: number
                                example: 3
                        order:
                          type: number
                          example: 0
                        isDeleted:
                          type: boolean
                          example: false
                        deletedAt:
                          type:
                            - string
                            - 'null'
                          format: date-time
                          example: null
                        createdAt:
                          type: string
                          format: date-time
                          example: '2026-04-12T10:00:00.000Z'
                        updatedAt:
                          type: string
                          format: date-time
                          example: '2026-04-12T10:00:00.000Z'
                  isDeleted:
                    type: boolean
                    example: false
                  deletedAt:
                    type:
                      - string
                      - 'null'
                    format: date-time
                    example: null
                  createdAt:
                    type: string
                    format: date-time
                    example: '2026-04-12T10:00:00.000Z'
                  updatedAt:
                    type: string
                    format: date-time
                    example: '2026-04-12T10:00:00.000Z'
            visibility:
              type: string
              enum:
                - Everyone
                - Company
                - Fitsociety
              example: Company
            isDeleted:
              type: boolean
              example: false
            deletedAt:
              type:
                - string
                - 'null'
              format: date-time
              example: null
            createdAt:
              type: string
              format: date-time
              example: '2026-04-12T10:00:00.000Z'
            updatedAt:
              type: string
              format: date-time
              example: '2026-04-12T10:00:00.000Z'
    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
    PublicApiWriteMeta:
      type: object
      additionalProperties: false
      required:
        - requestId
      properties:
        requestId:
          type: string
          description: Stable request correlation id. Mirrors `X-Request-Id` when supplied.
          example: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
        rateLimit:
          $ref: '#/components/schemas/PublicApiRateLimitMeta'
        idempotency:
          type: object
          additionalProperties: false
          properties:
            replayed:
              type: boolean
              description: >-
                True when the response was replayed from a previous request with
                the same `Idempotency-Key`.
              example: true
    PublicApiMeta:
      type: object
      additionalProperties: false
      required:
        - requestId
      properties:
        requestId:
          type: string
          description: Stable request correlation id. Mirrors `X-Request-Id` when supplied.
          example: 4f849d7d-f4f1-45cc-b4b7-3984a3d17f83
        rateLimit:
          $ref: '#/components/schemas/PublicApiRateLimitMeta'
    StandardResponse:
      type: object
      properties:
        status:
          type: integer
          example: 200
        error:
          type: boolean
          example: false
        message:
          type: string
          example: SUCCESS
      required:
        - status
        - error
        - message
    PublicApiRateLimitMeta:
      type: object
      additionalProperties: false
      properties:
        limit:
          type: integer
          example: 10
        remaining:
          type: integer
          example: 9
        resetSeconds:
          type: integer
          description: Seconds until the current rate limit window resets.
          example: 1
        retryAfterSeconds:
          type: integer
          description: Present when the request was rate limited.
          example: 1
  securitySchemes:
    PublicBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Opaque
      description: >-
        Public API access token issued by `/public/v1/oauth/token`. Example:
        `Authorization: Bearer fspt_...`. Each resource request rechecks the
        token company's current provider access. Disabling access blocks
        existing tokens with `403 auth.provider_unavailable`.

````

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