curl -X POST "https://api.fitsociety.io/public/v1/workout/video-library/presign" \
-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({uploadProtocol: 'multipart-v1', fileSize: 1073741824})
};
fetch('https://api.fitsociety.io/public/v1/workout/video-library/presign', 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/video-library/presign"
payload = {
"uploadProtocol": "multipart-v1",
"fileSize": 1073741824
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"usage": {
"usedBytes": 1,
"reservedBytes": 1,
"availableBytes": 1,
"limitBytes": 1,
"defaultLimitBytes": 1,
"limitBytesOverride": 1,
"measuredAt": "2026-07-14T10:00:00.000Z"
},
"upload": {
"id": "66f7b8b1e13c8d25f4d3d90a",
"protocol": "multipart-v1",
"expiresAt": "2026-07-14T10:00:00.000Z",
"parts": [
{
"partNumber": 1,
"offset": 1,
"size": 1,
"url": "https://example.com"
}
]
}
},
"meta": {
"requestId": "4f849d7d-f4f1-45cc-b4b7-3984a3d17f83",
"rateLimit": {
"limit": 10,
"remaining": 9,
"resetSeconds": 1
},
"idempotency": {
"replayed": false
}
}
}Reserve video capacity and presign multipart upload parts
Coach-only, company-scoped; requires training.media.create. Library and exercise videos share 25 decimal GB by default, adjustable by an admin. Reserves source, bounded output and thumbnail space. Both use the same technical processing ceiling of 2 GiB per source, with no separate exercise-video limit. PUT each file slice to its signed URL with exactly the specified length, then complete. The worker compresses the video and generates a thumbnail; save only the ready result URL. Upload rights expire after one hour. Do not expose signed URLs in logs.
Requires the workout_library:write scope. This operation maps to /app/v1/workout/video-library/presign 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/video-library/presign" \
-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({uploadProtocol: 'multipart-v1', fileSize: 1073741824})
};
fetch('https://api.fitsociety.io/public/v1/workout/video-library/presign', 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/video-library/presign"
payload = {
"uploadProtocol": "multipart-v1",
"fileSize": 1073741824
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"data": {
"usage": {
"usedBytes": 1,
"reservedBytes": 1,
"availableBytes": 1,
"limitBytes": 1,
"defaultLimitBytes": 1,
"limitBytesOverride": 1,
"measuredAt": "2026-07-14T10:00:00.000Z"
},
"upload": {
"id": "66f7b8b1e13c8d25f4d3d90a",
"protocol": "multipart-v1",
"expiresAt": "2026-07-14T10:00:00.000Z",
"parts": [
{
"partNumber": 1,
"offset": 1,
"size": 1,
"url": "https://example.com"
}
]
}
},
"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"