curl -X POST "https://api.fitsociety.io/public/v1/workout/cardio/clients/{clientId}/plans/{planId}/running-plan/generate" \
-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({
goal: {goalTimeSec: 123, raceDate: '2023-12-25', acceptGoalAsChallenge: false},
startDate: '2023-12-25',
runWeekdays: [4],
qualityWeekdays: [4],
longRunWeekday: 4,
intake: {
persistentRunningPain: true,
bonePointTenderness: true,
nightPain: true,
worseningDespiteRest: true
}
})
};
fetch('https://api.fitsociety.io/public/v1/workout/cardio/clients/{clientId}/plans/{planId}/running-plan/generate', 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/cardio/clients/{clientId}/plans/{planId}/running-plan/generate"
payload = {
"goal": {
"goalTimeSec": 123,
"raceDate": "2023-12-25",
"acceptGoalAsChallenge": False
},
"startDate": "2023-12-25",
"runWeekdays": [4],
"qualityWeekdays": [4],
"longRunWeekday": 4,
"intake": {
"persistentRunningPain": True,
"bonePointTenderness": True,
"nightPain": True,
"worseningDespiteRest": True
}
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"planId": "66f7b8b1e13c8d25f4d3d90a",
"schedule": {
"layout": "calendar_week",
"mode": "multi_week",
"releasePolicy": "weekly_from_start",
"startDate": "2026-07-14",
"weeks": [
{
"weekNumber": 1,
"days": [
{
"weekday": 1,
"isRestDay": false,
"items": [
{
"_id": "67f1234567890abcdef1234",
"order": 1,
"startTime": "08:30",
"plannedDurationMinutes": 45,
"status": "today",
"plannedDate": "2026-09-08",
"scheduledDate": "2026-09-08",
"source": "programme",
"clientAdjustment": {
"action": "moved",
"occurrenceDate": "2026-09-08",
"scheduledDate": "2026-09-08",
"changedAt": null
},
"canClientRestore": true,
"type": "workout",
"planMomentId": "67f1234567890abcdef1234"
}
]
}
]
}
]
},
"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": {}
}
}
]
}
]
}
},
"meta": {
"requestId": "4f849d7d-f4f1-45cc-b4b7-3984a3d17f83",
"rateLimit": {
"limit": 10,
"remaining": 9,
"resetSeconds": 1
},
"idempotency": {
"replayed": false
}
}
}Generate into an empty assigned calendar programme
Coach-only; company-scoped, linked-client and member-scope checks apply. Requires training.programmes.update and all three company cardio gates. Preview is read-only and builds the first two/three weeks when explicit prescription inputs are complete. Initial generation fills only an empty Assigned calendar programme, never existing workouts; missing inputs or invalid volume return 422 without writing. Calendar weeks include the race week; training ends before the race date. Hard volume limits override rounded workbook requests and growing weeks stay easy. Goal update replaces only the structured race goal.
Requires the workout_client_plans:write scope. This operation maps to /app/v1/workout/cardio/clients/:clientId/plans/:planId/running-plan/generate 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/cardio/clients/{clientId}/plans/{planId}/running-plan/generate" \
-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({
goal: {goalTimeSec: 123, raceDate: '2023-12-25', acceptGoalAsChallenge: false},
startDate: '2023-12-25',
runWeekdays: [4],
qualityWeekdays: [4],
longRunWeekday: 4,
intake: {
persistentRunningPain: true,
bonePointTenderness: true,
nightPain: true,
worseningDespiteRest: true
}
})
};
fetch('https://api.fitsociety.io/public/v1/workout/cardio/clients/{clientId}/plans/{planId}/running-plan/generate', 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/cardio/clients/{clientId}/plans/{planId}/running-plan/generate"
payload = {
"goal": {
"goalTimeSec": 123,
"raceDate": "2023-12-25",
"acceptGoalAsChallenge": False
},
"startDate": "2023-12-25",
"runWeekdays": [4],
"qualityWeekdays": [4],
"longRunWeekday": 4,
"intake": {
"persistentRunningPain": True,
"bonePointTenderness": True,
"nightPain": True,
"worseningDespiteRest": True
}
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"planId": "66f7b8b1e13c8d25f4d3d90a",
"schedule": {
"layout": "calendar_week",
"mode": "multi_week",
"releasePolicy": "weekly_from_start",
"startDate": "2026-07-14",
"weeks": [
{
"weekNumber": 1,
"days": [
{
"weekday": 1,
"isRestDay": false,
"items": [
{
"_id": "67f1234567890abcdef1234",
"order": 1,
"startTime": "08:30",
"plannedDurationMinutes": 45,
"status": "today",
"plannedDate": "2026-09-08",
"scheduledDate": "2026-09-08",
"source": "programme",
"clientAdjustment": {
"action": "moved",
"occurrenceDate": "2026-09-08",
"scheduledDate": "2026-09-08",
"changedAt": null
},
"canClientRestore": true,
"type": "workout",
"planMomentId": "67f1234567890abcdef1234"
}
]
}
]
}
]
},
"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": {}
}
}
]
}
]
}
},
"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"
Body
Show child attributes
Show child attributes
Monday=1, Sunday=7. Must respect stored availability, fixed days and run-day count.
31 <= x <= 7Two selected running days, ordered by weekday; distinct from the long run.
2 elements1 <= x <= 71 <= x <= 72, 3 Show child attributes
Show child attributes
Explicit coach inputs for the concrete horizon. Missing values are preview diagnostics and block saving. Later weeks stay skeletons.
3Show child attributes
Show child attributes