Update a training plan

PATCH/api/public/training-plans/{planId}

Partially update a training plan. Pass only fields you want to change.

BREAKING CHANGE the plan name is now "title"; the previous "name" field has been removed. Unknown fields are dropped during validation, so a PATCH sending only "name" is rejected with 400 "No updatable fields were supplied" rather than silently doing nothing.

If the plan is attached to more than one trainee, editing it changes the plan for all of them, so the request returns 409 CONFLICT until you confirm with applyToAllSharedTrainees.

Authentication

Requires an API key, sent as Authorization: Bearer cp_live_....

API keys carry scopes. Get the catalog of scopes and webhook events lists every scope.

Path parameters

Path parameters
NameTypeDescription
planIdrequiredstring

Header parameters

Header parameters
NameTypeDescription
Idempotency-Keystring

Optional. Send a unique key (e.g. a UUID) to make this POST safe to retry. The same key within 24h returns the original result instead of creating a duplicate.

Request body

application/json, required

Request body fields
NameTypeDescription
titlestring
descriptionstring
levelstring

Allowed values: Beginner Intermediate Advanced

maxDurationnumber
workoutsobject[]

Maximum items: 50

workouts[].trainingNamestring

Display name of the day, e.g. "Day A — Push".

workouts[].trainingTypestring

Day label, e.g. "A", "B", "FullBody".

Allowed values: A B C D E FullBody CrossFit Tabata HIIT EMOM AMRAP Circuit ForTime

workouts[].exerciseOrderstring

How exercises are performed. Defaults to Sequential.

Allowed values: Sequential Circuit Superset Complex

workouts[].notesstring

Free-text note for the whole day.

workouts[].timeBasedDetailsobject

Timing configuration for circuit-style days (Tabata, HIIT, EMOM, AMRAP). Seconds unless noted.

workouts[].timeBasedDetails.totalRoundsnumber
workouts[].timeBasedDetails.timeLimitnumber
workouts[].timeBasedDetails.workIntervalnumber
workouts[].timeBasedDetails.restIntervalnumber
workouts[].timeBasedDetails.restBetweenRoundsnumber
workouts[].exercisesobject[]
workouts[].exercises[].exerciseDetailsstring

Exercise catalog id, taken from GET /exercises.

workouts[].exercises[].setsNumberstring

Number of sets, e.g. "3".

workouts[].exercises[].repsNumberstring

Reps per set, e.g. "10" or "8-12".

workouts[].exercises[].restTimestring

Rest between sets in seconds, e.g. "90".

workouts[].exercises[].isDurationBasedboolean

True for timed exercises (e.g. plank) instead of reps.

workouts[].exercises[].setDurationstring

Duration per set in seconds when isDurationBased is true.

workouts[].exercises[].weightPercentagenumber

Working weight as % of 1RM, e.g. 75.

workouts[].exercises[].customNotesstring

Free-text note shown to the trainee for this exercise.

workouts[].exercises[].weightnumber

Working weight in kg for the exercise.

workouts[].exercises[].setsobject[]

Per-set prescription. Takes precedence over setsNumber/repsNumber when present.

workouts[].exercises[].sets[].setNumbernumber

1-based position of the set.

workouts[].exercises[].sets[].repsstring

Reps for this set, e.g. "8".

workouts[].exercises[].sets[].weightnumber

Working weight for this set.

workouts[].exercises[].sets[].restTimestring

Rest after this set in seconds.

workouts[].exercises[].sets[].intensityValuenumber

Intensity for this set, read against intensityType.

workouts[].exercises[].sets[].dropSetstring

Allowed values: DropSet DoubleDropSet TripleDropSet

workouts[].exercises[].sets[].isWarmupSetboolean

Warmup sets are not counted towards working volume.

workouts[].exercises[].superSetboolean

True when this exercise belongs to a superset.

workouts[].exercises[].superSetGroupstring

Shared identifier grouping the exercises performed together in one superset.

workouts[].exercises[].dropSetstring

Allowed values: DropSet DoubleDropSet TripleDropSet

workouts[].exercises[].restPauseboolean

Rest-pause technique.

workouts[].exercises[].clusterboolean

Cluster-set technique.

workouts[].exercises[].trackingTypestring

What the trainee logs for this exercise.

Allowed values: weight_reps reps_only duration completion

workouts[].exercises[].intensityTypestring

How intensityValue is interpreted.

Allowed values: Percentage RPE RIR

workouts[].exercises[].intensityValuenumber

Intensity target.

workouts[].exercises[].tempoobject

Tempo in seconds per phase of the lift.

workouts[].exercises[].tempo.eccentricnumber
workouts[].exercises[].tempo.holdnumber
workouts[].exercises[].tempo.concentricnumber
workouts[].exercises[].tempo.restnumber
workouts[].exercises[].distancenumber

Distance for cardio exercises.

workouts[].exercises[].distanceUnitstring

Allowed values: meters km miles yards

workouts[].exercises[].specificAlternativeExercisesstring[]

Catalog ids the trainee may swap in for this exercise.

applyToAllSharedTraineesboolean

Required (true) to edit a plan shared by multiple trainees; the change then applies to all of them. Without it, a shared plan returns 409.

Request examples

cURL
curl --request PATCH \
  --url 'https://api.coach-platform.com/api/public/training-plans/<planId>' \
  --header 'Authorization: Bearer cp_live_...' \
  --header 'Content-Type: application/json' \
  --data '{
  "title": "<string>"
}'
JavaScript (fetch)
const response = await fetch('https://api.coach-platform.com/api/public/training-plans/<planId>', {
  method: 'PATCH',
  headers: {
    Authorization: 'Bearer cp_live_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "title": "<string>"
  }),
});

const data = await response.json();

Responses

200 OK

Successful response

Example body (application/json)
{
  "data": {
    "id": "<string>",
    "coach": "<string>",
    "escorts": [
      "<string>"
    ],
    "title": "<string>",
    "notes": "<string>",
    "level": "Beginner",
    "maxDuration": 123,
    "isTemplate": true,
    "workouts": [
      {
        "trainingName": "<string>",
        "trainingType": "A",
        "exerciseOrder": "Sequential",
        "notes": "<string>",
        "timeBasedDetails": {
          "totalRounds": 123,
          "timeLimit": 123,
          "workInterval": 123,
          "restInterval": 123,
          "restBetweenRounds": 123
        },
        "exercises": [
          {
            "exerciseDetails": {
              "id": "<string>",
              "name": "<string>"
            },
            "setsNumber": "<string>",
            "repsNumber": "<string>",
            "restTime": "<string>",
            "isDurationBased": true,
            "setDuration": "<string>",
            "weightPercentage": 123,
            "customNotes": "<string>",
            "weight": 123,
            "sets": [
              {
                "setNumber": 123,
                "reps": "<string>",
                "weight": 123,
                "restTime": "<string>",
                "intensityValue": 123,
                "dropSet": "DropSet",
                "isWarmupSet": true
              }
            ],
            "superSet": true,
            "superSetGroup": "<string>",
            "dropSet": "DropSet",
            "restPause": true,
            "cluster": true,
            "trackingType": "weight_reps",
            "intensityType": "Percentage",
            "intensityValue": 123,
            "tempo": {
              "eccentric": 123,
              "hold": 123,
              "concentric": 123,
              "rest": 123
            },
            "distance": 123,
            "distanceUnit": "meters",
            "specificAlternativeExercises": [
              "<string>"
            ]
          }
        ]
      }
    ],
    "createdAt": "<date-time>"
  }
}
Response fields (57)
Response fields
NameTypeDescription
dataobject
data.idstring
data.coachstring
data.escortsstring[]
data.titlestring
data.notesstring
data.levelstring

Allowed values: Beginner Intermediate Advanced

data.maxDurationnumber

Target session length in minutes.

data.isTemplateboolean

True when the plan is a reusable template.

data.workoutsobject[]
data.workouts[].trainingNamestring

Display name of the day, e.g. "Day A — Push".

data.workouts[].trainingTypestring

Day label, e.g. "A", "B", "FullBody".

Allowed values: A B C D E FullBody CrossFit Tabata HIIT EMOM AMRAP Circuit ForTime

data.workouts[].exerciseOrderstring

How exercises are performed. Defaults to Sequential.

Allowed values: Sequential Circuit Superset Complex

data.workouts[].notesstring

Free-text note for the whole day.

data.workouts[].timeBasedDetailsobject

Timing configuration for circuit-style days (Tabata, HIIT, EMOM, AMRAP). Seconds unless noted.

data.workouts[].timeBasedDetails.totalRoundsnumber
data.workouts[].timeBasedDetails.timeLimitnumber
data.workouts[].timeBasedDetails.workIntervalnumber
data.workouts[].timeBasedDetails.restIntervalnumber
data.workouts[].timeBasedDetails.restBetweenRoundsnumber
data.workouts[].exercisesobject[]
data.workouts[].exercises[].exerciseDetailsobject

The exercise catalog entry. On write, pass exerciseDetails as the catalog id string (from GET /exercises).

data.workouts[].exercises[].exerciseDetails.idstring
data.workouts[].exercises[].exerciseDetails.namestring
data.workouts[].exercises[].setsNumberstring

Number of sets, e.g. "3".

data.workouts[].exercises[].repsNumberstring

Reps per set, e.g. "10" or "8-12".

data.workouts[].exercises[].restTimestring

Rest between sets in seconds, e.g. "90".

data.workouts[].exercises[].isDurationBasedboolean

True for timed exercises (e.g. plank) instead of reps.

data.workouts[].exercises[].setDurationstring

Duration per set in seconds when isDurationBased is true.

data.workouts[].exercises[].weightPercentagenumber

Working weight as % of 1RM, e.g. 75.

data.workouts[].exercises[].customNotesstring

Free-text note shown to the trainee for this exercise.

data.workouts[].exercises[].weightnumber

Working weight in kg for the exercise.

data.workouts[].exercises[].setsobject[]

Per-set prescription. Takes precedence over setsNumber/repsNumber when present.

data.workouts[].exercises[].sets[].setNumbernumber

1-based position of the set.

data.workouts[].exercises[].sets[].repsstring

Reps for this set, e.g. "8".

data.workouts[].exercises[].sets[].weightnumber

Working weight for this set.

data.workouts[].exercises[].sets[].restTimestring

Rest after this set in seconds.

data.workouts[].exercises[].sets[].intensityValuenumber

Intensity for this set, read against intensityType.

data.workouts[].exercises[].sets[].dropSetstring

Allowed values: DropSet DoubleDropSet TripleDropSet

data.workouts[].exercises[].sets[].isWarmupSetboolean

Warmup sets are not counted towards working volume.

data.workouts[].exercises[].superSetboolean

True when this exercise belongs to a superset.

data.workouts[].exercises[].superSetGroupstring

Shared identifier grouping the exercises performed together in one superset.

data.workouts[].exercises[].dropSetstring

Allowed values: DropSet DoubleDropSet TripleDropSet

data.workouts[].exercises[].restPauseboolean

Rest-pause technique.

data.workouts[].exercises[].clusterboolean

Cluster-set technique.

data.workouts[].exercises[].trackingTypestring

What the trainee logs for this exercise.

Allowed values: weight_reps reps_only duration completion

data.workouts[].exercises[].intensityTypestring

How intensityValue is interpreted.

Allowed values: Percentage RPE RIR

data.workouts[].exercises[].intensityValuenumber

Intensity target.

data.workouts[].exercises[].tempoobject

Tempo in seconds per phase of the lift.

data.workouts[].exercises[].tempo.eccentricnumber
data.workouts[].exercises[].tempo.holdnumber
data.workouts[].exercises[].tempo.concentricnumber
data.workouts[].exercises[].tempo.restnumber
data.workouts[].exercises[].distancenumber

Distance for cardio exercises.

data.workouts[].exercises[].distanceUnitstring

Allowed values: meters km miles yards

data.workouts[].exercises[].specificAlternativeExercisesstring[]

Catalog ids the trainee may swap in for this exercise.

data.createdAtstring<date-time>

Error responses

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 409 Conflict
  • 429 Too Many Requests
  • 500 Internal Server Error

These statuses share the same response body.

Example body (application/json)
{
  "error": {
    "code": "<string>",
    "message": "<string>",
    "fix": "<string>",
    "details": {},
    "retryAfterMs": 123
  }
}
Error fields (6)
Error fields
NameTypeDescription
errorobject
error.codestring
error.messagestring
error.fixstring | null
error.detailsobject | null
error.retryAfterMsnumber | null

This page is also available as Markdown. Browse it in the interactive explorer or download the OpenAPI spec.