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 | GET /athletes/{id} |
exerciseId | prescriptions, metric values | GET /exercises/{id} |
metricId | metric values | 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} |
# A workout carries ids, not names. Resolve the athlete and the exercise:
curl "https://api-integrations-next.vitruve.fit/v1/athletes/ATHLETE_ID" -H "X-API-Key: CUSTOMER_INTEGRATION_KEY" -H "X-Partner-Secret: YOUR_PARTNER_SECRET"
curl "https://api-integrations-next.vitruve.fit/v1/exercises/EXERCISE_ID" -H "X-API-Key: CUSTOMER_INTEGRATION_KEY" -H "X-Partner-Secret: YOUR_PARTNER_SECRET"const headers = { 'X-API-Key': customer.vitruveKey, 'X-Partner-Secret': process.env.VITRUVE_PARTNER_SECRET }
const get = async (path) =>
(await fetch(`https://api-integrations-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": customer.vitruve_key, "X-Partner-Secret": os.environ["VITRUVE_PARTNER_SECRET"]}
def get(path):
response = requests.get(f"https://api-integrations-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 the customer's organization. Reference data is shared by every customer:
- Units, metrics, prescription types and muscles are global and identical for everyone.
- Exercises include Vitruve's global library (
organizationId: null) and each organization's own. Filter withsource=global|organization|all.
An id that is not found
Ids from another customer, deleted rows, and ids of a different resource type all answer 404. Ids are unique across Vitruve, so you can store them without prefixing them per customer.