IDs and relations
How resources reference each other by id, which endpoint resolves each id field, and the role ids.
Every resource has a UUID id, and resources point at each other with id fields. The API returns ids rather than embedding every related object, so a workout stays small and you resolve names only when you need them.
Resolving id fields
| Field | Appears on | Resolve with |
|---|---|---|
userId | workouts, metric values, equations, wellness tests, injury reports | GET /athletes/{id} |
createdBy, recordedByUserId | programs, templates, profile versions, injury reports | GET /coaches/{id} |
exerciseId | prescriptions, metric values, equations | GET /exercises/{id} |
metricId | metric values, equations, leaderboards | GET /metrics/{id} |
prescriptionTypeId | seriePrescriptions | GET /prescription-types/{id} |
unitId, baseUnitId, pairUnitId | metrics, units, series parameters | GET /units/{id} |
teams[].id | athletes | GET /teams/{id} |
workoutId | scheduled workouts, wellness tests | GET /workouts/{id} |
# A workout carries ids, not names. Resolve the athlete and the exercise:
curl "https://api-next.vitruve.fit/v1/athletes/ATHLETE_ID" -H "X-API-Key: YOUR_API_KEY"
curl "https://api-next.vitruve.fit/v1/exercises/EXERCISE_ID" -H "X-API-Key: YOUR_API_KEY"const headers = { 'X-API-Key': process.env.VITRUVE_API_KEY }
const get = async (path) =>
(await fetch(`https://api-next.vitruve.fit/v1${path}`, { headers }).then((r) => r.json())).data
// Catalogs change rarely: load once, look up locally.
const exercises = new Map()
for await (const exercise of listAll('/exercises')) exercises.set(exercise.id, exercise)
const workout = await get('/workouts/WORKOUT_ID')
for (const block of workout.blocks)
for (const group of block.prescriptionsGroups)
for (const prescription of group.prescriptions)
console.log(exercises.get(prescription.exerciseId)?.name)headers = {"X-API-Key": os.environ["VITRUVE_API_KEY"]}
def get(path):
response = requests.get(f"https://api-next.vitruve.fit/v1{path}", headers=headers, timeout=30)
response.raise_for_status()
return response.json()["data"]
# Catalogs change rarely: load once, look up locally.
exercises = {e["id"]: e for e in list_all("/exercises")}
workout = get("/workouts/WORKOUT_ID")
for block in workout["blocks"]:
for group in block["prescriptionsGroups"]:
for prescription in group["prescriptions"]:
print(exercises[prescription["exerciseId"]]["name"])Role ids
roleId identifies a membership role. These three values are fixed:
| Role | roleId |
|---|---|
| Owner | 4aa018e6-8b22-40b1-8d07-ab4f1af51330 |
| Coach | ef736bad-c98e-410e-bf26-af834332153b |
| Athlete | dc82d01f-1136-4fcb-97dc-11a8b2203d98 |
Use them to filter GET /coaches?roleId=….
Global and organization data
Most data belongs to your organization. Reference data is shared:
- Units, metrics, prescription types and muscles are global and identical for everyone.
- Exercises include Vitruve's global library (
organizationId: null) and your organization's own. Filter withsource=global|organization|all.
An id that is not found
Ids from another organization, deleted rows, and ids of a different resource type all answer 404. A template id sent to GET /workouts/{id} is not found: templates live under /templates/workouts/{id}.