curl -X POST "https://api.fitsociety.io/public/v1/workout/groups/start" \
-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({
source: 'manual',
participants: [
{
clientId: '67f1234567890abcdef1234',
workoutSource: 'auto_assigned_plan',
planId: null,
planMomentId: null,
resumeAction: 'continue',
confirmationToken: 'a0eb661b-bf61-4af6-98f0-d7d7ff8a3e75'
}
]
})
};
fetch('https://api.fitsociety.io/public/v1/workout/groups/start', 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/groups/start"
payload = {
"source": "manual",
"participants": [
{
"clientId": "67f1234567890abcdef1234",
"workoutSource": "auto_assigned_plan",
"planId": None,
"planMomentId": None,
"resumeAction": "continue",
"confirmationToken": "a0eb661b-bf61-4af6-98f0-d7d7ff8a3e75"
}
]
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"run": {
"runId": "67f1234567890abcdef1234",
"source": "calendar_event",
"status": "active",
"calendarEventId": null,
"manualSessionKey": "small-group-2026-06-04-09",
"startedAt": null,
"endedAt": null
},
"results": [
{
"clientId": "67f1234567890abcdef1234",
"ok": true,
"sessionId": null,
"sessionStatus": "planned",
"workoutSource": "auto_assigned_plan",
"sourceLabel": "Full Body - Day 2",
"templateId": null,
"errorCode": "",
"requiresConfirmation": false,
"confirmationToken": null,
"confirmationTokenExpiresAt": null,
"timerStartedAt": null,
"timerPausedAt": null,
"timerElapsedMs": 90000,
"runTimer": {
"timerStartedAt": null,
"timerPausedAt": null,
"timerElapsedMs": 300000
}
}
],
"counts": {
"total": 2,
"started": 1,
"pendingConfirmation": 1,
"failed": 0
}
},
"meta": {
"requestId": "4f849d7d-f4f1-45cc-b4b7-3984a3d17f83",
"rateLimit": {
"limit": 10,
"remaining": 9,
"resetSeconds": 1
},
"idempotency": {
"replayed": false
}
}
}Start or resume a multi-client coach group workout
Starts or resumes only the participants submitted in the request body. For calendar events, submitted clients must belong to the active event booking list, but unselected booked clients are not started automatically.
Requires the workout_groups:write scope. This operation maps to /app/v1/coach/workout/group/start 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/groups/start" \
-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({
source: 'manual',
participants: [
{
clientId: '67f1234567890abcdef1234',
workoutSource: 'auto_assigned_plan',
planId: null,
planMomentId: null,
resumeAction: 'continue',
confirmationToken: 'a0eb661b-bf61-4af6-98f0-d7d7ff8a3e75'
}
]
})
};
fetch('https://api.fitsociety.io/public/v1/workout/groups/start', 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/groups/start"
payload = {
"source": "manual",
"participants": [
{
"clientId": "67f1234567890abcdef1234",
"workoutSource": "auto_assigned_plan",
"planId": None,
"planMomentId": None,
"resumeAction": "continue",
"confirmationToken": "a0eb661b-bf61-4af6-98f0-d7d7ff8a3e75"
}
]
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"run": {
"runId": "67f1234567890abcdef1234",
"source": "calendar_event",
"status": "active",
"calendarEventId": null,
"manualSessionKey": "small-group-2026-06-04-09",
"startedAt": null,
"endedAt": null
},
"results": [
{
"clientId": "67f1234567890abcdef1234",
"ok": true,
"sessionId": null,
"sessionStatus": "planned",
"workoutSource": "auto_assigned_plan",
"sourceLabel": "Full Body - Day 2",
"templateId": null,
"errorCode": "",
"requiresConfirmation": false,
"confirmationToken": null,
"confirmationTokenExpiresAt": null,
"timerStartedAt": null,
"timerPausedAt": null,
"timerElapsedMs": 90000,
"runTimer": {
"timerStartedAt": null,
"timerPausedAt": null,
"timerElapsedMs": 300000
}
}
],
"counts": {
"total": 2,
"started": 1,
"pendingConfirmation": 1,
"failed": 0
}
},
"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
calendar_event, manual "manual"
1Show child attributes
Show child attributes
null
null
Optional occurrence start date for recurring calendar events. The backend also accepts calendarOccurrenceStartDate for the same value.
"2026-04-12T10:00:00.000Z"
Optional occurrence start date for recurring calendar events. Used to scope the group run to one recurring occurrence.
"2026-04-12T10:00:00.000Z"
"small-group-2026-06-04-09"