Training
Workouts with their full tree of blocks, sets and repetitions, and the programs that schedule them.
Start with the workout data model. List endpoints return workouts and programs without their content. Fetch a single resource for the full tree.
List workouts
Athlete workouts of your organization, without their content. Templates are excluded; they are served by /templates/workouts. Fetch a workout’s blocks, sets and metrics with GET /workouts/{id}.
Request
curl "https://api-next.vitruve.fit/v1/workouts?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62&from=2026-03-01&to=2026-03-31&limit=2" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/workouts?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62&from=2026-03-01&to=2026-03-31&limit=2',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/workouts?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62&from=2026-03-01&to=2026-03-31&limit=2",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
"name": "Lower body strength A",
"isTemplate": false,
"rpe": 8,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"scheduledWorkoutId": null,
"completedAt": "2026-03-17T10:41:52.318Z",
"startedAt": "2026-03-17T09:58:04.902Z",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:41:52.877Z",
"deletedAt": null,
"testType": null
},
{
"id": "5a0e7c3d-91b4-4f26-8d58-c3e2a1f6b097",
"name": "Upper body power",
"isTemplate": false,
"rpe": 7,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"scheduledWorkoutId": null,
"completedAt": "2026-03-14T17:32:10.441Z",
"startedAt": "2026-03-14T16:55:47.019Z",
"createdAt": "2026-03-14T16:55:46.870Z",
"updatedAt": "2026-03-14T17:32:10.902Z",
"deletedAt": null,
"testType": null
}
],
"meta": {
"limit": 2,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
userIduuid- Only workouts of this athlete.
exerciseIduuid- Only workouts with at least one prescription of this exercise.
statuscompleted | pendingdefaultcompletedcompletedfor performed workouts,pendingfor assigned workouts not yet performed.trainingTypevbt | jump | sprint | reaction- Only workouts with at least one prescription of this training type.
fromdate- Inclusive lower bound:
YYYY-MM-DDor an ISO 8601 datetime. A bare date starts at 00:00 intimezone, or at the change on a day whose midnight a daylight-saving change skips. todate- Inclusive upper bound:
YYYY-MM-DDor an ISO 8601 datetime. A bare date covers that whole day intimezone. timezonestringdefaultUTC- IANA time zone, such as
Europe/Madrid. A barefrom/todate covers that whole day in it, and a datetime without an offset is a wall-clock time in it. Datetimes withZor an offset are taken as given. limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- Completed workouts are sorted and filtered by
completedAt; pending workouts bycreatedAt. Both are newest first. from/totherefore apply tocompletedAtwhenstatus=completedand tocreatedAtwhenstatus=pending.trainingType=vbtalso matches prescriptions written beforetrainingTypeexisted, which carrytrainingType: nullandisVbt: true.- With both
exerciseIdandtrainingType, the same prescription must match both: a prescription of that exercise and that training type. - Workouts older than the history your plan includes are left out.
Response fields
iduuid- Workout id. Pass it to
GET /workouts/{id}for the full tree. namestring- Workout name.
userIduuid- Athlete the workout belongs to. Resolve with
GET /athletes/{id}. completedAtdatetimenullable- When the athlete finished the workout.
nullfor pending workouts. startedAtdatetimenullable- When the athlete started the workout.
rpeintegernullable- Session RPE entered by the athlete.
scheduledWorkoutIduuidnullable- Program slot this workout is attached to (
phases[].scheduledWorkouts[].idinGET /programs/{id}). Unique: one workout per slot. testTypestringnullable- Set when the workout is a standardized test (for example
vertical_jump,t_test), otherwisenull. createdAtdatetime- When the workout was created or assigned.
Failures are described in error codes.
Get a workout
One athlete workout with its full tree: blocks → prescriptions groups → prescriptions → serie prescriptions and series, each series with its values, set-level metrics and repetitions. Templates return 404 here; use GET /templates/workouts/{id}.
Request
curl "https://api-next.vitruve.fit/v1/workouts/f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/workouts/f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/workouts/f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
"name": "Lower body strength A",
"isTemplate": false,
"rpe": 8,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"scheduledWorkoutId": null,
"completedAt": "2026-03-17T10:41:52.318Z",
"startedAt": "2026-03-17T09:58:04.902Z",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:41:52.877Z",
"deletedAt": null,
"testType": null,
"blocks": [
{
"id": "0b6d3a9e-7f21-4c58-9d14-e82a5f3c6b79",
"name": "Main lifts",
"isTemplate": false,
"order": 0,
"workoutId": "f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null,
"prescriptionsGroups": [
{
"id": "4d1e8c75-2a93-4f06-b8e7-39c0a6d5f142",
"name": "Back squat",
"isTemplate": false,
"order": 0,
"blockId": "0b6d3a9e-7f21-4c58-9d14-e82a5f3c6b79",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null,
"prescriptions": [
{
"id": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"prescriptionsGroupId": "4d1e8c75-2a93-4f06-b8e7-39c0a6d5f142",
"name": "Back squat",
"order": 0,
"note": "Stop the set if mean velocity drops below 0.55 m/s.",
"isTemplate": false,
"isVbt": true,
"trainingType": "vbt",
"trainingAlgorithm": null,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null,
"seriePrescriptions": [
{
"id": "e3b72d05-6a1c-4f98-b4e3-1d8c9a5f0726",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"prescriptionId": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"prescriptionTypeId": "3e9d7a12-c5b8-4f40-a136-d82e4b6f9c05",
"unitId": "fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78",
"order": 0,
"isRange": false,
"isTopMode": false,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null
},
{
"id": "9d4f1b68-c2e7-4a05-93d1-7b6e0f2a8c34",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"prescriptionId": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"prescriptionTypeId": "62c8a0f4-3d19-4e7b-a5c2-f0b9d14e8a63",
"unitId": "b7e05a92-4f3c-4d18-86a1-2c9e7d3b5f40",
"order": 1,
"isRange": false,
"isTopMode": false,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null
}
],
"series": [
{
"id": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"prescriptionId": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"order": 0,
"completedAt": "2026-03-17T10:06:30.512Z",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null,
"serieValues": [
{
"id": "0f6d2b84-a7e1-4c39-b05d-e9a3c8f1724b",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"seriePrescriptionId": "e3b72d05-6a1c-4f98-b4e3-1d8c9a5f0726",
"valuePrescriptedInf": null,
"valuePrescriptedSup": null,
"valuePrescripted": 100,
"valueExecuted": 100,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null
},
{
"id": "c5a19e37-04bd-4f62-8e7a-3b2d6f0c9a18",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"seriePrescriptionId": "9d4f1b68-c2e7-4a05-93d1-7b6e0f2a8c34",
"valuePrescriptedInf": null,
"valuePrescriptedSup": null,
"valuePrescripted": 5,
"valueExecuted": 5,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null
}
],
"metricValues": [
{
"id": "71e4c0a9-5d36-4b82-a1f7-9c8e2b4d0563",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"value": 0.61,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": null,
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:06:30.512Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null
}
],
"repetitions": [
{
"id": "e19a4d83-5b60-4c27-a8f1-0d7c3e52b946",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"type": "concentric",
"repNumber": 1,
"repIndex": 0,
"createdAt": "2026-03-17T10:05:58.140Z",
"updatedAt": "2026-03-17T10:05:58.140Z",
"deletedAt": null,
"metricValues": [
{
"id": "86a1c4e7-3b05-4d9f-92e8-5f1d7c0a3b94",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"value": 0.64,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": "e19a4d83-5b60-4c27-a8f1-0d7c3e52b946",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:05:58.140Z",
"updatedAt": "2026-03-17T10:05:58.140Z",
"deletedAt": null
},
{
"id": "3c7e9b20-a5f1-4d86-b2c4-71d0e8f3a659",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "2a9c6e41-b0d8-4f73-9e15-84d3c7a0b2f6",
"value": 1.12,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": "e19a4d83-5b60-4c27-a8f1-0d7c3e52b946",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:05:58.140Z",
"updatedAt": "2026-03-17T10:05:58.140Z",
"deletedAt": null
}
]
},
{
"id": "48b3f6d1-e2a0-4c95-b7d8-06f1a9c3e5b2",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"type": "concentric",
"repNumber": 2,
"repIndex": 1,
"createdAt": "2026-03-17T10:06:03.377Z",
"updatedAt": "2026-03-17T10:06:03.377Z",
"deletedAt": null,
"metricValues": [
{
"id": "d9c2a5e0-8b17-4f4d-a36e-5e0b7c1f9d84",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"value": 0.62,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": "48b3f6d1-e2a0-4c95-b7d8-06f1a9c3e5b2",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:06:03.377Z",
"updatedAt": "2026-03-17T10:06:03.377Z",
"deletedAt": null
}
]
}
]
}
]
}
]
}
]
}
]
}
}Path parameters
iduuidrequired- Resource id.
Notes
- Every level is ordered by
order, thenid; repetitions byrepIndex, thenid. Deleted rows are never returned. serieValuesare returned in theunitIdof their serie prescription, as the app shows them (for example lb when the parameter is in lb).metricValues[].valueis in the metric’s own unit, seeGET /metrics/{id}. Repetition metrics appear only underrepetitions[], never in the set’smetricValues.- Prescriptions written before
trainingTypeexisted carrytrainingType: nullandisVbt: truefor VBT. - A workout older than the history your plan includes answers
404. - Pending workouts have
completedAt: nulland usually novalueExecuted, metrics or repetitions yet. - Raw encoder data is not exposed.
Response fields
namestring- Workout name.
userIduuid- Athlete the workout belongs to. Resolve with
GET /athletes/{id}. completedAtdatetimenullable- When the athlete finished the workout.
nullfor pending workouts. startedAtdatetimenullable- When the athlete started the workout.
rpeintegernullable- Session RPE entered by the athlete.
scheduledWorkoutIduuidnullable- Program slot this workout is attached to (
phases[].scheduledWorkouts[].idinGET /programs/{id}). Unique: one workout per slot. testTypestringnullable- Set when the workout is a standardized test (for example
vertical_jump,t_test), otherwisenull. createdAtdatetime- When the workout was created or assigned.
blocks[]object[]- Blocks, ordered by
order. blocks[].prescriptionsGroups[]object[]- Groups, ordered by
order. A group with more than one prescription is a superset. blocks[].prescriptionsGroups[].prescriptions[]object[]- One exercise each, ordered by
order. …prescriptions[].exerciseIduuid- Exercise performed. Resolve with
GET /exercises/{id}. …prescriptions[].trainingTypestringnullablevbt,jump,sprintorreaction.nullon rows written before the field existed; readisVbtthen.…prescriptions[].trainingAlgorithmstringnullable- Variant within the training type, for example
10-5for jump orgo-no-gofor reaction. …prescriptions[].notestringnullable- Coach note for the exercise.
…prescriptions[].seriePrescriptions[]object[]- The prescribed parameters (load, reps, velocity target…), ordered by
order. …seriePrescriptions[].prescriptionTypeIduuid- What the parameter is. Resolve with
GET /prescription-types/{id}. …seriePrescriptions[].unitIduuid- Unit the parameter’s
serieValuesare expressed in. Resolve withGET /units/{id}. …seriePrescriptions[].isRangebooleannullable- When
true, the target isvaluePrescriptedInf–valuePrescriptedSupinstead ofvaluePrescripted. …prescriptions[].series[]object[]- Sets, ordered by
order. …series[].completedAtdatetimenullable- When the set was completed.
…series[].serieValues[]object[]- One value per serie prescription, linked by
seriePrescriptionId. …serieValues[].valuePrescriptednumbernullable- Prescribed value, in the
unitIdof its serie prescription. …serieValues[].valuePrescriptedInfnumbernullable- Lower bound of a prescribed range, same unit.
…serieValues[].valuePrescriptedSupnumbernullable- Upper bound of a prescribed range, same unit.
…serieValues[].valueExecutednumbernullable- Value actually performed, same unit.
nulluntil the set is done. …series[].metricValues[]object[]- Metrics captured for the whole set (
repetitionId: null). …metricValues[].metricIduuid- Metric measured. Resolve with
GET /metrics/{id}. …metricValues[].valuenumber- Measured value, in the metric’s
unitId. …series[].repetitions[]object[]- Repetitions, ordered by
repIndex. …repetitions[].repNumberinteger- Repetition number as shown in the app.
…repetitions[].repIndexinteger- Zero-based position of the repetition in the set.
…repetitions[].typestringnullableconcentricoreccentric.nullon older rows: read it as “not eccentric”.…repetitions[].metricValues[]object[]- Metrics captured for this repetition.
Failures are described in error codes.
List active workouts
Sessions your athletes are performing right now, without their content. A session leaves this list when the athlete finishes it (it is then in GET /workouts) or discards it. Fetch one with GET /active-workouts/{id}.
Request
curl "https://api-next.vitruve.fit/v1/active-workouts?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/active-workouts?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/active-workouts?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"version": 12,
"createdAt": "2026-03-17T09:58:05.114Z",
"updatedAt": "2026-03-17T10:21:37.502Z"
}
],
"meta": {
"limit": 50,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
userIduuid- Only the sessions of this athlete.
limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- Newest first, by
createdAt. - A session the app stopped saving long ago may still be listed until Vitruve discards it; read
updatedAt.
Response fields
iduuid- Session id. It keeps this id as a workout once finished. Pass it to
GET /active-workouts/{id}for its sets so far. userIduuid- Athlete performing the session.
versioninteger- Grows by one every time the app saves the session.
createdAtdatetime- When the app first saved the session.
updatedAtdatetime- When the app last saved the session.
Failures are described in error codes.
Get an active workout
One session in progress as the app last saved it, in the same tree as GET /workouts/{id}: the sets done so far with their values, set-level metrics and repetitions.
Request
curl "https://api-next.vitruve.fit/v1/active-workouts/f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/active-workouts/f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/active-workouts/f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
"name": "Lower body strength A",
"isTemplate": false,
"rpe": 8,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"scheduledWorkoutId": null,
"completedAt": null,
"startedAt": "2026-03-17T09:58:04.902Z",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:21:37.502Z",
"deletedAt": null,
"testType": null,
"blocks": [
{
"id": "0b6d3a9e-7f21-4c58-9d14-e82a5f3c6b79",
"name": "Main lifts",
"isTemplate": false,
"order": 0,
"workoutId": "f2c8e1a6-3d49-4b75-8e02-6a9d4c1b7f35",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null,
"prescriptionsGroups": [
{
"id": "4d1e8c75-2a93-4f06-b8e7-39c0a6d5f142",
"name": "Back squat",
"isTemplate": false,
"order": 0,
"blockId": "0b6d3a9e-7f21-4c58-9d14-e82a5f3c6b79",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null,
"prescriptions": [
{
"id": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"prescriptionsGroupId": "4d1e8c75-2a93-4f06-b8e7-39c0a6d5f142",
"name": "Back squat",
"order": 0,
"note": "Stop the set if mean velocity drops below 0.55 m/s.",
"isTemplate": false,
"isVbt": true,
"trainingType": "vbt",
"trainingAlgorithm": null,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null,
"seriePrescriptions": [
{
"id": "e3b72d05-6a1c-4f98-b4e3-1d8c9a5f0726",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"prescriptionId": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"prescriptionTypeId": "3e9d7a12-c5b8-4f40-a136-d82e4b6f9c05",
"unitId": "fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78",
"order": 0,
"isRange": false,
"isTopMode": false,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null
},
{
"id": "9d4f1b68-c2e7-4a05-93d1-7b6e0f2a8c34",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"prescriptionId": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"prescriptionTypeId": "62c8a0f4-3d19-4e7b-a5c2-f0b9d14e8a63",
"unitId": "b7e05a92-4f3c-4d18-86a1-2c9e7d3b5f40",
"order": 1,
"isRange": false,
"isTopMode": false,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-10T16:20:11.004Z",
"deletedAt": null
}
],
"series": [
{
"id": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"prescriptionId": "a7f35c20-9e18-4d64-8b3a-5c2d0e91f6b8",
"order": 0,
"completedAt": "2026-03-17T10:06:30.512Z",
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null,
"serieValues": [
{
"id": "0f6d2b84-a7e1-4c39-b05d-e9a3c8f1724b",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"seriePrescriptionId": "e3b72d05-6a1c-4f98-b4e3-1d8c9a5f0726",
"valuePrescriptedInf": null,
"valuePrescriptedSup": null,
"valuePrescripted": 100,
"valueExecuted": 100,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null
},
{
"id": "c5a19e37-04bd-4f62-8e7a-3b2d6f0c9a18",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"seriePrescriptionId": "9d4f1b68-c2e7-4a05-93d1-7b6e0f2a8c34",
"valuePrescriptedInf": null,
"valuePrescriptedSup": null,
"valuePrescripted": 5,
"valueExecuted": 5,
"createdAt": "2026-03-10T16:20:11.004Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null
}
],
"metricValues": [
{
"id": "71e4c0a9-5d36-4b82-a1f7-9c8e2b4d0563",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"value": 0.61,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": null,
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:06:30.512Z",
"updatedAt": "2026-03-17T10:06:30.512Z",
"deletedAt": null
}
],
"repetitions": [
{
"id": "e19a4d83-5b60-4c27-a8f1-0d7c3e52b946",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"type": "concentric",
"repNumber": 1,
"repIndex": 0,
"createdAt": "2026-03-17T10:05:58.140Z",
"updatedAt": "2026-03-17T10:05:58.140Z",
"deletedAt": null,
"metricValues": [
{
"id": "86a1c4e7-3b05-4d9f-92e8-5f1d7c0a3b94",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"value": 0.64,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": "e19a4d83-5b60-4c27-a8f1-0d7c3e52b946",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:05:58.140Z",
"updatedAt": "2026-03-17T10:05:58.140Z",
"deletedAt": null
},
{
"id": "3c7e9b20-a5f1-4d86-b2c4-71d0e8f3a659",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "2a9c6e41-b0d8-4f73-9e15-84d3c7a0b2f6",
"value": 1.12,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": "e19a4d83-5b60-4c27-a8f1-0d7c3e52b946",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:05:58.140Z",
"updatedAt": "2026-03-17T10:05:58.140Z",
"deletedAt": null
}
]
},
{
"id": "48b3f6d1-e2a0-4c95-b7d8-06f1a9c3e5b2",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"type": "concentric",
"repNumber": 2,
"repIndex": 1,
"createdAt": "2026-03-17T10:06:03.377Z",
"updatedAt": "2026-03-17T10:06:03.377Z",
"deletedAt": null,
"metricValues": [
{
"id": "d9c2a5e0-8b17-4f4d-a36e-5e0b7c1f9d84",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"metricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"value": 0.62,
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"repetitionId": "48b3f6d1-e2a0-4c95-b7d8-06f1a9c3e5b2",
"serieId": "68c2e5b1-0d47-4a93-9f6e-b13a7d84c250",
"createdAt": "2026-03-17T10:06:03.377Z",
"updatedAt": "2026-03-17T10:06:03.377Z",
"deletedAt": null
}
]
}
]
}
]
}
]
}
]
}
],
"version": 12
}
}Path parameters
iduuidrequired- Resource id.
Notes
- Same units, order and fields as
GET /workouts/{id}:serieValuesin the unit of their serie prescription, deleted rows left out, raw encoder data never exposed. - To poll a session, send the last
ETagyou received back asIf-None-Match: while the app hasn’t saved again the answer is304 Not Modifiedwith no body, and it doesn’t read the session. - A finished or discarded session answers
404; read a finished one withGET /workouts/{id}.
Response fields
versioninteger- Grows by one every time the app saves the session. Also sent as the
ETagheader. updatedAtdatetime- When the app last saved the session.
blocks[]object[]- The same tree as
GET /workouts/{id}. Sets not done yet havecompletedAt: null.
Failures are described in error codes.
List programs
Programs of your organization, newest first, each with the ids of its assigned athletes.
Request
curl "https://api-next.vitruve.fit/v1/programs?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62&isTemplate=false" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/programs?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62&isTemplate=false',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/programs?userId=3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62&isTemplate=false",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53",
"name": "Pre-season strength block",
"isTemplate": false,
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"startedAt": "2026-03-02T00:00:00.000Z",
"createdAt": "2026-02-24T11:05:42.610Z",
"updatedAt": "2026-03-01T18:12:09.233Z",
"deletedAt": null,
"users": [
{
"id": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62"
},
{
"id": "c81d5f07-92ab-4e63-b0d4-7a3e6c1f9e28"
}
]
}
],
"meta": {
"limit": 50,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
isTemplatebooleantruefor program templates only,falsefor programs that are not templates. Omit for both.userIduuid- Only programs assigned to this athlete.
searchstring- Case-insensitive match on the program name.
limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- Sorted by
createdAt, newest first.
Response fields
iduuid- Program id. Pass it to
GET /programs/{id}for its phases. namestring- Program name.
isTemplatebooleannullabletruefor templates. Older rows storenull, which means not a template.startedAtdatetimenullable- Start date of the program’s first phase.
users[].iduuid- Assigned athletes. Resolve with
GET /athletes/{id}.
Failures are described in error codes.
Get a program
One program with its assigned athletes and its phases, each with its scheduled workouts and the workout row they point to.
Request
curl "https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53",
"name": "Pre-season strength block",
"isTemplate": false,
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"startedAt": "2026-03-02T00:00:00.000Z",
"createdAt": "2026-02-24T11:05:42.610Z",
"updatedAt": "2026-03-01T18:12:09.233Z",
"deletedAt": null,
"users": [
{
"id": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62"
},
{
"id": "c81d5f07-92ab-4e63-b0d4-7a3e6c1f9e28"
}
],
"phases": [
{
"id": "b45e0c97-1a68-4d32-9c7f-82f3d6a1e0b4",
"name": "Accumulation",
"isTemplate": false,
"order": 0,
"weeks": 4,
"programId": "2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"createdAt": "2026-02-24T11:05:42.610Z",
"updatedAt": "2026-02-24T11:05:42.610Z",
"deletedAt": null,
"scheduledWorkouts": [
{
"id": "73d9f1c8-e526-4a0b-b4d3-9e8a2c5f6710",
"color": "#4C8BF5",
"phaseId": "b45e0c97-1a68-4d32-9c7f-82f3d6a1e0b4",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"include": [],
"exclude": [
14
],
"recurrence": [
0,
3
],
"createdAt": "2026-02-24T11:05:42.610Z",
"updatedAt": "2026-02-24T11:05:42.610Z",
"deletedAt": null,
"workout": {
"id": "a3d81f6c-07e5-4b29-9c4a-e5f2b0d7c813",
"name": "Lower body strength A",
"isTemplate": false,
"rpe": null,
"userId": null,
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"scheduledWorkoutId": "73d9f1c8-e526-4a0b-b4d3-9e8a2c5f6710",
"completedAt": null,
"startedAt": null,
"createdAt": "2026-02-24T11:05:42.610Z",
"updatedAt": "2026-02-24T11:05:42.610Z",
"deletedAt": null,
"testType": null
}
},
{
"id": "f0a7d3c6-29e8-4b51-9c04-a6b1e8d2f375",
"color": null,
"phaseId": "b45e0c97-1a68-4d32-9c7f-82f3d6a1e0b4",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"include": [
26
],
"exclude": [],
"recurrence": [],
"createdAt": "2026-02-24T11:07:15.032Z",
"updatedAt": "2026-02-24T11:07:15.032Z",
"deletedAt": null,
"workout": {
"id": "e86b2c47-d150-4f3a-a8e9-2f7c4d06b5a1",
"name": "Jump test",
"isTemplate": false,
"rpe": null,
"userId": null,
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"scheduledWorkoutId": "f0a7d3c6-29e8-4b51-9c04-a6b1e8d2f375",
"completedAt": null,
"startedAt": null,
"createdAt": "2026-02-24T11:07:15.032Z",
"updatedAt": "2026-02-24T11:07:15.032Z",
"deletedAt": null,
"testType": "vertical_jump"
}
}
]
}
]
}
}Path parameters
iduuidrequired- Resource id.
Notes
recurrence,includeandexcludeare returned as the Vitruve planner stores them. A slot falls on phase daydwhendis ininclude, or whend % 7is inrecurrenceanddis not inexclude.- A phase starts at
startedAtplus theweeks × 7days of every earlier phase. - Scheduled workouts whose workout was deleted are left out.
GET /workouts/{id}only serves workouts that have auserIdand are not templates, andGET /templates/workouts/{id}only templates. A slot’sworkoutwithuserId: nullandisTemplate: falseis returned here without its content.
Response fields
users[].iduuid- Assigned athletes. Resolve with
GET /athletes/{id}. phases[]object[]- Phases, ordered by
order. They run back to back fromstartedAt. phases[].weeksintegernullable- Phase length in weeks.
phases[].scheduledWorkouts[]object[]- Workouts placed in the phase, oldest first.
phases[].scheduledWorkouts[].includeinteger[]- Single occurrences, as day offsets from the phase start (0 = first day of the phase).
phases[].scheduledWorkouts[].recurrenceinteger[]- Weekly repeat, as days 0–6 of each week counted from the phase start (not calendar weekdays). Repeats for every week of the phase.
phases[].scheduledWorkouts[].excludeinteger[]- Day offsets from the phase start removed from the weekly repeat.
phases[].scheduledWorkouts[].workoutobject- The workout row the slot points to, without its content.
Failures are described in error codes.
Get a program's attendance
How far a program’s athletes are keeping to it: every planned session that is already due, matched to the sessions they performed, per athlete, per team and per planned session. The same figures as the program page of the Vitruve app.
Request
curl "https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53/attendance?timezone=Europe%2FMadrid" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53/attendance?timezone=Europe%2FMadrid',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53/attendance?timezone=Europe%2FMadrid",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"programId": "2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53",
"asOf": "2026-03-25",
"startDateKey": "2026-02-24",
"endDateKey": "2026-04-21",
"overall": {
"athletesCount": 2,
"due": 18,
"fulfilled": 15,
"missed": 3,
"rate": 0.8333
},
"completion": {
"total": 32,
"completed": 15,
"remaining": 17
},
"athletes": [
{
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"due": 9,
"fulfilled": 9,
"missed": 0,
"rate": 1,
"unplanned": 1
},
{
"userId": "c81d5f07-92ab-4e63-b0d4-7a3e6c1f9e28",
"due": 9,
"fulfilled": 6,
"missed": 3,
"rate": 0.6667,
"unplanned": 0
}
],
"teams": [
{
"teamId": "9a4f6d13-8b25-4c7e-93a0-1f5d2e8b7c64",
"name": "First team",
"athletesCount": 2,
"due": 18,
"fulfilled": 15,
"missed": 3,
"rate": 0.8333
}
],
"occurrences": [
{
"scheduledWorkoutId": "73d9f1c8-e526-4a0b-b4d3-9e8a2c5f6710",
"name": "Lower body strength A",
"dateKey": "2026-02-24",
"due": 2,
"fulfilled": 2,
"rate": 1
}
]
}
}Path parameters
iduuidrequired- Resource id.
Query parameters
timezonestringdefaultUTC- IANA time zone, such as
Europe/Madrid. Decides what “today” is, and so which sessions are already due.
Notes
- A planned session counts as performed when the athlete completed it from the program, on its day or one day either side. One that is still today’s, or yesterday’s, is not due yet: it is neither performed nor missed.
- Only athletes still in the organization count, in every figure.
- Templates and programs of another organization answer
404.
Response fields
asOfstring- Today in
timezone(YYYY-MM-DD): the figures count the sessions due up to it. startDateKeystringnullable- First day of the program.
nullwhen it hasn’t been started: nothing is due. endDateKeystringnullable- The day after the last day of its last phase.
overallobject- The program’s athletes together:
due(planned sessions already due),fulfilled(performed),missed, andrate(fulfilled / due,nullwhile nothing is due). completionobject- Progress through the whole plan:
totalplanned sessions, including those not due yet,completedandremaining. athletes[]object[]- One row per athlete of the program, in roster order, with the same counts.
unplannedcounts sessions performed during the program that weren’t started from a program session. teams[]object[]- Each team with at least one of the program’s athletes, totalled over those athletes only.
occurrences[]object[]- Each planned session that is due (
scheduledWorkoutIdfromGET /programs/{id}, its workoutname, itsdateKey), with how many of its athletes performed it. Oldest first.
Failures are described in error codes.
Get a program's effectiveness
How a program’s athletes are progressing towards its goals: each goal (a metric, how it is aggregated, increase, decrease or reach, and a target) evaluated per athlete and for the group, with where the group should be by now. The same figures as the program page of the Vitruve app.
Request
curl "https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53/effectiveness?timezone=Europe%2FMadrid" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53/effectiveness?timezone=Europe%2FMadrid',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/programs/2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53/effectiveness?timezone=Europe%2FMadrid",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"programId": "2f8b6a04-d3c1-4e95-87a2-c64e1b9d0f53",
"asOf": "2026-03-25",
"startDateKey": "2026-02-24",
"endDateKey": "2026-04-21",
"timeShare": 0.5179,
"goals": [
{
"goal": {
"id": "d8c41f27-5a9e-4b03-a6f2-0e7b3c95d184",
"metricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"calculation": "max",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"condition": "increase",
"target": 5
},
"athletes": [
{
"userId": "3b9c1e24-6f7a-4d2b-8c15-a40e9d7f1b62",
"baseline": 0.92,
"current": 0.97,
"changePct": 5.43,
"achieved": true,
"progress": 1,
"ratio": 1.087
},
{
"userId": "c81d5f07-92ab-4e63-b0d4-7a3e6c1f9e28",
"baseline": 0.88,
"current": 0.9,
"changePct": 2.27,
"achieved": false,
"progress": 0.4545,
"ratio": 0.4545
}
],
"summary": {
"evaluated": 2,
"achieved": 1,
"achievedShare": 0.5,
"meanChangePct": 3.85,
"meanProgress": 0.7273
},
"expectedProgress": 0.5179,
"series": {
"points": [
{
"dateKey": "2026-02-24",
"value": 0
},
{
"dateKey": "2026-03-02",
"value": 0.21
},
{
"dateKey": "2026-03-25",
"value": 0.7273
}
],
"projection": [
{
"dateKey": "2026-02-24",
"value": 0
},
{
"dateKey": "2026-04-20",
"value": 1
}
]
}
}
]
}
}Path parameters
iduuidrequired- Resource id.
Query parameters
timezonestringdefaultUTC- IANA time zone, such as
Europe/Madrid. Decides what “today” is and which calendar day each measurement falls on.
Notes
- The baseline is the four weeks before the start. An athlete with no data in them is measured from their first two program weeks instead.
- Only athletes still in the organization count, in every figure.
- Templates and programs of another organization answer
404.
Response fields
asOfstring- Today in
timezone(YYYY-MM-DD). Once the program is over, its goals are judged on its last day. startDateKeystringnullable- First day of the program.
nullwhen it hasn’t been started:goalsis empty. endDateKeystringnullable- The day after the last day of its last phase.
timeSharenumbernullable- Share of the program elapsed by the end of
asOf, from 0 to 1. goals[]object[]- One entry per goal of the program, in its order. Empty before the start, or when the program has no goals.
goals[].athletes[]object[]- One row per athlete of the program, in roster order:
baselineandcurrent(the goal’s calculation over comparable windows, in the metric’s base unit),changePct,achieved,progress(towards the target, from -1 to 1) andratio(unclamped).nullwhere the athlete has no data. goals[].summaryobject- The athletes together:
evaluated,achieved,achievedShare,meanChangePctandmeanProgress(the mean of the clamped progress). goals[].expectedProgressnumber- Where the group should be by now if it progresses evenly.
summary.meanProgressbelow it means behind schedule. goals[].seriesobject- For a chart:
points, the group progress at the end of every program week and on the evaluated day, andprojection, the planned progress from 0 at the start to 1 on the last day. goals[].errorstring- Present when this goal could not be read. The other goals are still evaluated.
Failures are described in error codes.