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 '{}'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({saveAsTemplate: false})
};
fetch('https://api.fitsociety.io/public/v1/workout/plans/duplicate/{templateId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.fitsociety.io/public/v1/workout/plans/duplicate/{templateId}"
payload = { "saveAsTemplate": False }
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"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
}
}
}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.
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 '{}'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({saveAsTemplate: false})
};
fetch('https://api.fitsociety.io/public/v1/workout/plans/duplicate/{templateId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.fitsociety.io/public/v1/workout/plans/duplicate/{templateId}"
payload = { "saveAsTemplate": False }
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"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
}
}
}Authorizations
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.
Headers
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.
1 - 200"booking-create-20260714-001"
Path Parameters
Source template or assigned workout plan id.
"67f1234567890abcdef1234"
Body
Copy an assigned member plan into a standalone template.