curl -X PUT "https://api.fitsociety.io/public/v1/workout/groups/runs/{runId}/participants" \
-H "Authorization: Bearer <access_token>" \
-H "Idempotency-Key: <stable_request_key>" \
-H "Content-Type: application/json" \
-d '{}'const options = {
method: 'PUT',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
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/runs/{runId}/participants', 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/runs/{runId}/participants"
payload = { "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.put(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
}
}
}Reconcile participants for an active group workout run
Sets the full desired active participant set for a group workout run in one call. Participants omitted from the request are removed or cancelled according to the run reconciliation rules. Existing completed participants are preserved without resolving or assigning another workout; changing their workout requires an explicit participant workout-source action. A requested member whose workout resolution or session start fails remains in the run as a retryable error participant, with the failure code in the result.
Requires the workout_groups:write scope. This operation maps to /app/v1/coach/workout/group/runs/:runId/participants 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 PUT "https://api.fitsociety.io/public/v1/workout/groups/runs/{runId}/participants" \
-H "Authorization: Bearer <access_token>" \
-H "Idempotency-Key: <stable_request_key>" \
-H "Content-Type: application/json" \
-d '{}'const options = {
method: 'PUT',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
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/runs/{runId}/participants', 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/runs/{runId}/participants"
payload = { "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.put(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"
Path Parameters
Group workout run id.
"67f1234567890abcdef1234"
Body
1Show child attributes
Show child attributes