curl -X POST "https://api.fitsociety.io/public/v1/workout/performance/sessions/{sessionId}/exercises/bulk" \
-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({
exercises: [
{
exerciseId: '67f1234567890abcdef1234',
sets: [
{
setNumber: 1,
setType: 'N',
displayNumber: 1,
displayLabel: '1',
targetReps: '8-12',
targetWeight: 60,
targetTimeSeconds: null,
targetDistanceMeters: null,
targetRestSeconds: 90,
weight: 70,
reps: 8,
timeSeconds: null,
distanceMeters: null,
restSeconds: 120,
rpe: 8,
completed: true,
notes: 'Felt strong.',
isPersonalBest: false,
repsPlaceholder: 22,
weightPlaceholder: 30,
timeSecondsPlaceholder: 55,
distanceMetersPlaceholder: 40,
rpePlaceholder: 3,
restSecondsPlaceholder: 60,
repsActual: 30,
weightActual: 42,
timeSecondsActual: null,
distanceMetersActual: null,
rpeActual: 8,
restSecondsActual: 90
}
],
notes: 'Felt strong.',
isCompleted: false,
planId: '67f1234567890abcdef1234',
planMomentId: '67f1234567890abcdef1234',
planExerciseId: '67f1234567890abcdef1234',
startedAt: '2026-04-12T10:00:00.000Z',
endedAt: '2026-04-12T10:00:00.000Z',
date: '2026-04-12T10:00:00.000Z',
order: 0,
nextSessionTarget: {
reps: '5-8',
weight: 80,
weightUnit: 'kg',
timeSeconds: null,
distanceMeters: null,
restSeconds: 120,
rpe: 8,
notes: 'Control the descent.',
sets: [{}]
},
setupFields: [
{
key: 'seat',
label: 'Seat',
type: 'text',
unit: '',
options: ['1', '2', '3', '4'],
order: 1,
required: false,
enabled: true,
defaultValue: 3
}
],
setupValues: {},
nextSetupValues: {}
}
]
})
};
fetch('https://api.fitsociety.io/public/v1/workout/performance/sessions/{sessionId}/exercises/bulk', 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/performance/sessions/{sessionId}/exercises/bulk"
payload = { "exercises": [
{
"exerciseId": "67f1234567890abcdef1234",
"sets": [
{
"setNumber": 1,
"setType": "N",
"displayNumber": 1,
"displayLabel": "1",
"targetReps": "8-12",
"targetWeight": 60,
"targetTimeSeconds": None,
"targetDistanceMeters": None,
"targetRestSeconds": 90,
"weight": 70,
"reps": 8,
"timeSeconds": None,
"distanceMeters": None,
"restSeconds": 120,
"rpe": 8,
"completed": True,
"notes": "Felt strong.",
"isPersonalBest": False,
"repsPlaceholder": 22,
"weightPlaceholder": 30,
"timeSecondsPlaceholder": 55,
"distanceMetersPlaceholder": 40,
"rpePlaceholder": 3,
"restSecondsPlaceholder": 60,
"repsActual": 30,
"weightActual": 42,
"timeSecondsActual": None,
"distanceMetersActual": None,
"rpeActual": 8,
"restSecondsActual": 90
}
],
"notes": "Felt strong.",
"isCompleted": False,
"planId": "67f1234567890abcdef1234",
"planMomentId": "67f1234567890abcdef1234",
"planExerciseId": "67f1234567890abcdef1234",
"startedAt": "2026-04-12T10:00:00.000Z",
"endedAt": "2026-04-12T10:00:00.000Z",
"date": "2026-04-12T10:00:00.000Z",
"order": 0,
"nextSessionTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": None,
"distanceMeters": None,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [{}]
},
"setupFields": [
{
"key": "seat",
"label": "Seat",
"type": "text",
"unit": "",
"options": ["1", "2", "3", "4"],
"order": 1,
"required": False,
"enabled": True,
"defaultValue": 3
}
],
"setupValues": {},
"nextSetupValues": {}
}
] }
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"data": [
{
"id": "67f1234567890abcdef1234",
"exerciseId": "67f1234567890abcdef1234",
"planExerciseId": null,
"order": 0,
"isCompleted": false,
"date": "2026-04-12T10:00:00.000Z",
"startedAt": null,
"endedAt": null,
"totals": {
"totalReps": 24,
"totalTimeSeconds": 0,
"totalDistanceMeters": 0,
"totalVolume": 1680,
"maxWeight": 75,
"estimated1RM": 95,
"averageRPE": 8.2
},
"sets": [
{
"setNumber": 1,
"setType": "N",
"displayNumber": 1,
"displayLabel": "1",
"targetReps": "8-12",
"targetWeight": 60,
"targetTimeSeconds": null,
"targetDistanceMeters": null,
"targetRestSeconds": 90,
"weight": 70,
"reps": 8,
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"completed": true,
"notes": "Felt strong.",
"isPersonalBest": false,
"repsPlaceholder": 22,
"weightPlaceholder": 30,
"timeSecondsPlaceholder": 55,
"distanceMetersPlaceholder": 40,
"rpePlaceholder": 3,
"restSecondsPlaceholder": 60,
"repsActual": 30,
"weightActual": 42,
"timeSecondsActual": null,
"distanceMetersActual": null,
"rpeActual": 8,
"restSecondsActual": 90
}
],
"notes": "Last set was slow.",
"nextSessionTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [
{}
]
},
"targetSnapshot": {
"exerciseType": "reps",
"todayTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [
{}
]
},
"lastSessionTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [
{}
]
},
"personalRecord": {
"metric": "weight",
"value": 82.5,
"unit": "kg",
"reps": 6,
"performedAt": null,
"sessionId": null,
"performanceId": null
},
"todaySetupValues": {},
"lastSetupValues": {},
"sourceSessionId": null,
"sourcePerformanceId": null
},
"setupFields": [
{
"key": "seat",
"label": "Seat",
"type": "text",
"unit": "",
"options": [
"1",
"2",
"3",
"4"
],
"order": 1,
"required": false,
"enabled": true,
"defaultValue": 3
}
],
"setupValues": {},
"nextSetupValues": {},
"showSetRpe": true,
"hasMachineSettings": true,
"setupSettingsEnabled": true,
"isMachineSetting": true,
"provenance": {
"recordedByActorType": "coach",
"recordedByClientId": null,
"recordedByCoachId": null,
"lastUpdatedByActorType": "coach",
"lastUpdatedByClientId": null,
"lastUpdatedByCoachId": null
},
"addedSets": [
{
"setNumber": 1,
"setType": "N",
"displayNumber": 1,
"displayLabel": "1",
"previous": "12 × 22kg",
"reps": 8,
"weight": 50,
"timeSeconds": null,
"distanceMeters": null,
"rpe": 8,
"rest": 60,
"restSeconds": 60,
"completed": true,
"targetReps": "8",
"targetWeight": 50,
"targetTimeSeconds": null,
"targetDistanceMeters": null,
"targetRestSeconds": 60,
"repsPlaceholderCoach": 12,
"weightPlaceholderCoach": 40,
"timeSecondsPlaceholderCoach": null,
"distanceMetersPlaceholderCoach": null,
"rpePlaceholderCoach": 7,
"restSecondsPlaceholderCoach": 90,
"repsPlaceholder": 12,
"weightPlaceholder": 40,
"timeSecondsPlaceholder": null,
"distanceMetersPlaceholder": null,
"rpePlaceholder": 7,
"restSecondsPlaceholder": 90,
"repsActual": 30,
"weightActual": 42,
"timeSecondsActual": null,
"distanceMetersActual": null,
"rpeActual": 8,
"restSecondsActual": 90
}
]
}
],
"count": 3
},
"meta": {
"requestId": "4f849d7d-f4f1-45cc-b4b7-3984a3d17f83",
"rateLimit": {
"limit": 10,
"remaining": 9,
"resetSeconds": 1
},
"idempotency": {
"replayed": false
}
}
}Bulk-add exercises to a workout v2 session
Assisted plan slots use nonnegative kilograms of assistance and do not contribute lifted-load volume or estimated 1RM. Adds multiple exercise performance records to a session in a single request. All exercise IDs must be unique within the request and must not already exist in the session. If any exercise in the batch already exists the entire request is rejected with 409. Exercise type is resolved from the provided sets; if no metrics are supplied and a planExerciseId is given the type is resolved from the plan exercise definition.
Requires the workout_sessions:write scope. This operation maps to /app/v1/workout/performance/sessions/:sessionId/exercises/bulk 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/performance/sessions/{sessionId}/exercises/bulk" \
-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({
exercises: [
{
exerciseId: '67f1234567890abcdef1234',
sets: [
{
setNumber: 1,
setType: 'N',
displayNumber: 1,
displayLabel: '1',
targetReps: '8-12',
targetWeight: 60,
targetTimeSeconds: null,
targetDistanceMeters: null,
targetRestSeconds: 90,
weight: 70,
reps: 8,
timeSeconds: null,
distanceMeters: null,
restSeconds: 120,
rpe: 8,
completed: true,
notes: 'Felt strong.',
isPersonalBest: false,
repsPlaceholder: 22,
weightPlaceholder: 30,
timeSecondsPlaceholder: 55,
distanceMetersPlaceholder: 40,
rpePlaceholder: 3,
restSecondsPlaceholder: 60,
repsActual: 30,
weightActual: 42,
timeSecondsActual: null,
distanceMetersActual: null,
rpeActual: 8,
restSecondsActual: 90
}
],
notes: 'Felt strong.',
isCompleted: false,
planId: '67f1234567890abcdef1234',
planMomentId: '67f1234567890abcdef1234',
planExerciseId: '67f1234567890abcdef1234',
startedAt: '2026-04-12T10:00:00.000Z',
endedAt: '2026-04-12T10:00:00.000Z',
date: '2026-04-12T10:00:00.000Z',
order: 0,
nextSessionTarget: {
reps: '5-8',
weight: 80,
weightUnit: 'kg',
timeSeconds: null,
distanceMeters: null,
restSeconds: 120,
rpe: 8,
notes: 'Control the descent.',
sets: [{}]
},
setupFields: [
{
key: 'seat',
label: 'Seat',
type: 'text',
unit: '',
options: ['1', '2', '3', '4'],
order: 1,
required: false,
enabled: true,
defaultValue: 3
}
],
setupValues: {},
nextSetupValues: {}
}
]
})
};
fetch('https://api.fitsociety.io/public/v1/workout/performance/sessions/{sessionId}/exercises/bulk', 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/performance/sessions/{sessionId}/exercises/bulk"
payload = { "exercises": [
{
"exerciseId": "67f1234567890abcdef1234",
"sets": [
{
"setNumber": 1,
"setType": "N",
"displayNumber": 1,
"displayLabel": "1",
"targetReps": "8-12",
"targetWeight": 60,
"targetTimeSeconds": None,
"targetDistanceMeters": None,
"targetRestSeconds": 90,
"weight": 70,
"reps": 8,
"timeSeconds": None,
"distanceMeters": None,
"restSeconds": 120,
"rpe": 8,
"completed": True,
"notes": "Felt strong.",
"isPersonalBest": False,
"repsPlaceholder": 22,
"weightPlaceholder": 30,
"timeSecondsPlaceholder": 55,
"distanceMetersPlaceholder": 40,
"rpePlaceholder": 3,
"restSecondsPlaceholder": 60,
"repsActual": 30,
"weightActual": 42,
"timeSecondsActual": None,
"distanceMetersActual": None,
"rpeActual": 8,
"restSecondsActual": 90
}
],
"notes": "Felt strong.",
"isCompleted": False,
"planId": "67f1234567890abcdef1234",
"planMomentId": "67f1234567890abcdef1234",
"planExerciseId": "67f1234567890abcdef1234",
"startedAt": "2026-04-12T10:00:00.000Z",
"endedAt": "2026-04-12T10:00:00.000Z",
"date": "2026-04-12T10:00:00.000Z",
"order": 0,
"nextSessionTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": None,
"distanceMeters": None,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [{}]
},
"setupFields": [
{
"key": "seat",
"label": "Seat",
"type": "text",
"unit": "",
"options": ["1", "2", "3", "4"],
"order": 1,
"required": False,
"enabled": True,
"defaultValue": 3
}
],
"setupValues": {},
"nextSetupValues": {}
}
] }
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"data": [
{
"id": "67f1234567890abcdef1234",
"exerciseId": "67f1234567890abcdef1234",
"planExerciseId": null,
"order": 0,
"isCompleted": false,
"date": "2026-04-12T10:00:00.000Z",
"startedAt": null,
"endedAt": null,
"totals": {
"totalReps": 24,
"totalTimeSeconds": 0,
"totalDistanceMeters": 0,
"totalVolume": 1680,
"maxWeight": 75,
"estimated1RM": 95,
"averageRPE": 8.2
},
"sets": [
{
"setNumber": 1,
"setType": "N",
"displayNumber": 1,
"displayLabel": "1",
"targetReps": "8-12",
"targetWeight": 60,
"targetTimeSeconds": null,
"targetDistanceMeters": null,
"targetRestSeconds": 90,
"weight": 70,
"reps": 8,
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"completed": true,
"notes": "Felt strong.",
"isPersonalBest": false,
"repsPlaceholder": 22,
"weightPlaceholder": 30,
"timeSecondsPlaceholder": 55,
"distanceMetersPlaceholder": 40,
"rpePlaceholder": 3,
"restSecondsPlaceholder": 60,
"repsActual": 30,
"weightActual": 42,
"timeSecondsActual": null,
"distanceMetersActual": null,
"rpeActual": 8,
"restSecondsActual": 90
}
],
"notes": "Last set was slow.",
"nextSessionTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [
{}
]
},
"targetSnapshot": {
"exerciseType": "reps",
"todayTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [
{}
]
},
"lastSessionTarget": {
"reps": "5-8",
"weight": 80,
"weightUnit": "kg",
"timeSeconds": null,
"distanceMeters": null,
"restSeconds": 120,
"rpe": 8,
"notes": "Control the descent.",
"sets": [
{}
]
},
"personalRecord": {
"metric": "weight",
"value": 82.5,
"unit": "kg",
"reps": 6,
"performedAt": null,
"sessionId": null,
"performanceId": null
},
"todaySetupValues": {},
"lastSetupValues": {},
"sourceSessionId": null,
"sourcePerformanceId": null
},
"setupFields": [
{
"key": "seat",
"label": "Seat",
"type": "text",
"unit": "",
"options": [
"1",
"2",
"3",
"4"
],
"order": 1,
"required": false,
"enabled": true,
"defaultValue": 3
}
],
"setupValues": {},
"nextSetupValues": {},
"showSetRpe": true,
"hasMachineSettings": true,
"setupSettingsEnabled": true,
"isMachineSetting": true,
"provenance": {
"recordedByActorType": "coach",
"recordedByClientId": null,
"recordedByCoachId": null,
"lastUpdatedByActorType": "coach",
"lastUpdatedByClientId": null,
"lastUpdatedByCoachId": null
},
"addedSets": [
{
"setNumber": 1,
"setType": "N",
"displayNumber": 1,
"displayLabel": "1",
"previous": "12 × 22kg",
"reps": 8,
"weight": 50,
"timeSeconds": null,
"distanceMeters": null,
"rpe": 8,
"rest": 60,
"restSeconds": 60,
"completed": true,
"targetReps": "8",
"targetWeight": 50,
"targetTimeSeconds": null,
"targetDistanceMeters": null,
"targetRestSeconds": 60,
"repsPlaceholderCoach": 12,
"weightPlaceholderCoach": 40,
"timeSecondsPlaceholderCoach": null,
"distanceMetersPlaceholderCoach": null,
"rpePlaceholderCoach": 7,
"restSecondsPlaceholderCoach": 90,
"repsPlaceholder": 12,
"weightPlaceholder": 40,
"timeSecondsPlaceholder": null,
"distanceMetersPlaceholder": null,
"rpePlaceholder": 7,
"restSecondsPlaceholder": 90,
"repsActual": 30,
"weightActual": 42,
"timeSecondsActual": null,
"distanceMetersActual": null,
"rpeActual": 8,
"restSecondsActual": 90
}
]
}
],
"count": 3
},
"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
Workout session id.
"67f1234567890abcdef1234"
Body
Array of exercise performance objects to create.
1Show child attributes
Show child attributes