Reference
Endpointy
Všechno žije pod https://api.gravl.ai a vrací JSON. Uživatelská data mají stabilní GUID, zdroje z katalogu celočíselná id. Hodnoty enumů jsou camelCase řetězce; s neznámými hodnotami počítej jako s dopředně kompatibilními.
Všechny endpointy
| Metoda | Cesta | Scope |
|---|---|---|
| GET | /api/v1/user | profile:read |
| GET | /api/v1/workouts | workouts:read |
| GET | /api/v1/workouts/{id} | workouts:read |
| GET | /api/v1/workout-templates | workouts:read |
| GET | /api/v1/workout-templates/{id} | workouts:read |
| GET | /api/v1/splits | splits:read |
| GET | /api/v1/splits/{id} | splits:read |
| GET | /api/v1/personal-records | records:read |
| GET | /api/v1/measurements | measurements:read |
| GET | /api/v1/stats | stats:read |
| GET | /api/v1/trophies | stats:read |
| GET | /api/v1/exercises | exercises:read |
| GET | /api/v1/exercises/{id} | exercises:read |
| GET | /api/v1/equipment | exercises:read |
| GET | /api/v1/equipment/{id} | exercises:read |
| GET | /oauth/authorize | — |
| POST | /oauth/token | — |
| POST | /oauth/revoke | — |
Stránkovací obálka
Endpointy níže označené jako paginated balí svoje položky do jedné standardní obálky:
{
"items": [ … ], // the page of results
"pageNumber": 2,
"totalPages": 9,
"totalCount": 174,
"hasPreviousPage": true,
"hasNextPage": true
}Každý endpoint vyžaduje svůj scope; chybějící scope vrací 403 s problem details. Chyby a rate limity popisují konvence.
Uživatel
/api/v1/userprofile:readProfil autorizujícího uživatele: zobrazovaná pole, preferované jednotky a tréninkový cíl.
Response: Profil uživatele
| Pole | Typ | Popis |
|---|---|---|
| id | string | Stabilní identifikátor uživatele; klíčuj podle něj svoje úložiště. |
| displayName | string | null | |
| username | string | null | |
| weightUnit | "kilograms" | "pounds" | Preferovaná zobrazovací jednotka; váhy v API jsou vždy libry. |
| distanceUnit | "kilometers" | "miles" | |
| gender | "male" | "female" | "other" | null | |
| height | number | null | V jednotce dané polem heightUnit. |
| heightUnit | "centimeters" | "feetInches" | |
| goal | "stronger" | "muscleMass" | "lean" | "generalFitness" | null |
{
"id": "8fKq2ZbXvNc…",
"displayName": "Alex",
"username": "alexlifts",
"weightUnit": "pounds",
"distanceUnit": "kilometers",
"gender": "male",
"height": 183,
"heightUnit": "centimeters",
"goal": "muscleMass"
}Tréninky
/api/v1/workoutsworkouts:readpaginatedHistorie tréninků uživatele, od nejnovějších.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
startDate | query | string (ISO 8601) | Jen tréninky začínající v tento okamžik nebo po něm (včetně). |
endDate | query | string (ISO 8601) | Jen tréninky začínající v tento okamžik nebo před ním (včetně). |
Response: Souhrn tréninku (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| id | string (uuid) | |
| name | string | |
| notes | string | null | |
| startDate | string (ISO 8601, UTC) | |
| endDate | string (ISO 8601, UTC) | |
| durationMinutes | integer | |
| type | string | Původ tréninku, např. "custom", "external". Můžou přibýt nové hodnoty. |
| volume | number | Celkový zvednutý objem, v librách. |
| calories | number | |
| personalRecordCount | integer | Osobní rekordy zaznamenané v tomto tréninku. |
| exerciseCount | integer |
{
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Push Day",
"notes": null,
"startDate": "2026-07-12T17:03:00Z",
"endDate": "2026-07-12T18:05:00Z",
"durationMinutes": 62,
"type": "custom",
"volume": 24150,
"calories": 410,
"personalRecordCount": 1,
"exerciseCount": 6
}
],
"pageNumber": 1,
"totalPages": 87,
"totalCount": 174,
"hasPreviousPage": false,
"hasNextPage": true
}/api/v1/workouts/{id}workouts:readJeden trénink v plném detailu: každý cvik se zalogovanými sériemi, v pořadí provedení.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
idpovinný | path | string (uuid) | Id tréninku z endpointu se seznamem. |
Response: Detail tréninku
| Pole | Typ | Popis |
|---|---|---|
| …všechna pole souhrnu tréninku | Stejná jako položka seznamu, bez exerciseCount. | |
| exercises[].exerciseId | integer | Id cviku v katalogu. |
| exercises[].exerciseName | string | |
| exercises[].supersetId | integer | null | Cviky se stejným id byly provedeny jako superset. |
| exercises[].sets[].order | integer | Pozice v rámci cviku, od 1. |
| exercises[].sets[].reps | integer | Provedená opakování. |
| exercises[].sets[].weight | number | Libry. |
| exercises[].sets[].duration | integer | null | Sekundy, u sérií na čas. |
| exercises[].sets[].distance | integer | null | Metry, u sérií na vzdálenost. |
| exercises[].sets[].rpe | integer | null | Subjektivně vnímaná námaha (RPE, 1–10). |
| exercises[].sets[].setType | "normal" | "warmup" | "dropSet" | "failure" |
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Push Day",
"startDate": "2026-07-12T17:03:00Z",
"endDate": "2026-07-12T18:05:00Z",
"durationMinutes": 62,
"type": "custom",
"volume": 24150,
"calories": 410,
"personalRecordCount": 1,
"exercises": [
{
"exerciseId": 42,
"exerciseName": "Bench Press",
"supersetId": null,
"sets": [
{ "order": 1, "reps": 10, "weight": 155, "duration": null,
"distance": null, "rpe": 7, "setType": "normal" },
{ "order": 2, "reps": 8, "weight": 175, "duration": null,
"distance": null, "rpe": 9, "setType": "normal" }
]
}
]
}/api/v1/workout-templatesworkouts:readpaginatedUložené tréninkové šablony uživatele, od nejnovějších. Série jsou plánované hodnoty, ne záznamy.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
Response: Souhrn šablony (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| id | string (uuid) | |
| name | string | |
| description | string | null | |
| exerciseCount | integer | |
| exercises | array | Ve výpisu vždy prázdné; použij detailový endpoint. |
/api/v1/workout-templates/{id}workouts:readJedna šablona s plánovanými cviky a sériemi.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
idpovinný | path | string (uuid) | Id šablony z endpointu se seznamem. |
Response: Detail šablony
| Pole | Typ | Popis |
|---|---|---|
| …všechna pole souhrnu šablony | ||
| exercises[].exerciseId | integer | |
| exercises[].exerciseName | string | |
| exercises[].supersetId | integer | null | |
| exercises[].sets[].order | integer | Pozice v rámci cviku, od 1. |
| exercises[].sets[].reps | integer | null | Plánovaná opakování. |
| exercises[].sets[].weight | number | null | Libry. |
| exercises[].sets[].duration | integer | null | Sekundy, u sérií na čas. |
| exercises[].sets[].distance | integer | null | Metry, u sérií na vzdálenost. |
| exercises[].sets[].rpe | integer | null | Subjektivně vnímaná námaha (RPE, 1–10). |
| exercises[].sets[].setType | "normal" | "warmup" | "dropSet" | "failure" |
/api/v1/splitssplits:readpaginatedVlastní tréninkové splity uživatele, od nejnovějších.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
Response: Souhrn splitu (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| id | string (uuid) | |
| name | string | null | |
| description | string | null | |
| sessionCount | integer | |
| sessions | array | Ve výpisu vždy prázdné; použij detailový endpoint. |
/api/v1/splits/{id}splits:readJeden split s jeho sessions v pořadí.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
idpovinný | path | string (uuid) | Id splitu z endpointu se seznamem. |
Response: Detail splitu
| Pole | Typ | Popis |
|---|---|---|
| …všechna pole souhrnu splitu | ||
| sessions[].order | integer | |
| sessions[].type | "muscles" | "workoutTemplate" | |
| sessions[].muscles | string[] | Cílové svaly (camelCase, např. "chest"); vyplněno, když je type muscles. |
| sessions[].workoutTemplateId | string (uuid) | null | Navázaná šablona; vyplněno, když je type workoutTemplate. |
Rekordy a míry
/api/v1/personal-recordsrecords:readpaginatedOsobní rekordy uživatele, od nejnovějších.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
exerciseId | query | integer | Filtr na jeden cvik z katalogu. |
Response: Osobní rekord (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| exerciseId | integer | |
| exerciseName | string | |
| type | "oneRepMax" | "weightMax" | "volumeMax" | "repetitionMax" | "durationMax" | "distanceMax" | |
| reps | integer | null | |
| weight | number | null | Libry. |
| duration | integer | null | Sekundy. |
| distance | integer | null | Metry. |
| workoutId | string (uuid) | null | Trénink, ve kterém rekord padl, pokud je znám. |
| achievedAt | string (ISO 8601, UTC) |
{
"exerciseId": 87,
"exerciseName": "Deadlift",
"type": "oneRepMax",
"reps": 1,
"weight": 405,
"duration": null,
"distance": null,
"workoutId": "a1b2c3d4-…",
"achievedAt": "2026-06-28T18:41:00Z"
}/api/v1/measurementsmeasurements:readpaginatedZáznamy tělesné váhy a obvodů, od nejnovějších.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
startDate | query | string (ISO 8601) | Jen záznamy s datem v tento den nebo po něm (včetně). |
endDate | query | string (ISO 8601) | Jen záznamy s datem v tento den nebo před ním (včetně). |
Response: Měření (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| id | string (uuid) | |
| date | string (ISO 8601 date) | |
| weightUnit | "kilograms" | "pounds" | Systém jednotek tohoto záznamu: obvody jsou v palcích u liber a v centimetrech u kilogramů. |
| weight | number | null | |
| bodyFat | number | null | Procenta. |
| neck / shoulder / chest / bicep / forearm / abdomen / waist / glutes / thigh / calf | number | null | Obvody v systému jednotek daného záznamu. |
Statistiky a trofeje
/api/v1/statsstats:readHlavní tréninkové statistiky uživatele v jednom volání.
Response: Statistiky
| Pole | Typ | Popis |
|---|---|---|
| streak.current | integer | Aktuální streak, v týdnech. |
| streak.longest | integer | Nejdelší streak vůbec, v týdnech. |
| streak.workoutsThisWeek | integer | |
| totalXp | integer | |
| trophyCount | integer |
{
"streak": { "current": 6, "longest": 14, "workoutsThisWeek": 3 },
"totalXp": 12480,
"trophyCount": 22
}/api/v1/trophiesstats:readpaginatedZískané trofeje, od nejnovějších.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
Response: Trofej (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| type | string | Druh trofeje, camelCase, např. "workout100", "streak5", "calendar15". Můžou přibýt nové hodnoty. |
| exerciseId | integer | null | Vyplněno u trofejí vázaných na konkrétní cvik. |
| exerciseName | string | null | |
| achievedAt | string (ISO 8601, UTC) |
Katalog cviků a vybavení
Globální referenční data se stejnými stabilními celočíselnými id, jaká používá aplikace Gravl. Výpisy cviků obsahují i vlastní cviky autorizujícího uživatele, nikdy cviky ostatních.
/api/v1/exercisesexercises:readpaginatedKatalog cviků plus vlastní cviky uživatele, abecedně.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
search | query | string | Hledání v názvu bez ohledu na velikost písmen (obsahuje). Max 200 znaků. |
Response: Cvik (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| id | integer | Stabilní id v katalogu; odkazují na něj tréninky, rekordy a šablony. |
| name | string | |
| description | string | null | |
| category | string | camelCase enum, např. "barbell", "dumbbell". |
| type | string | camelCase enum, např. "weight", "duration". |
| muscle | string | Hlavní sval, camelCase, např. "chest". |
| secondaryMuscles | string[] | |
| equipment | string[] | Názvy vybavení použitelného pro tento cvik. |
/api/v1/exercises/{id}exercises:readJeden cvik podle id v katalogu.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
idpovinný | path | integer | Id cviku v katalogu. |
Response: Cvik
| Pole | Typ | Popis |
|---|---|---|
| …stejné jako položka seznamu |
/api/v1/equipmentexercises:readpaginatedGlobální katalog vybavení, abecedně.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
page | query | integer | Číslo stránky, od 1. Výchozí: 1 |
pageSize | query | integer | Počet položek na stránku, 1–100. Výchozí: 30 |
Response: Vybavení (jako items[] ve stránkovací obálce)
| Pole | Typ | Popis |
|---|---|---|
| id | integer | Stabilní id v katalogu. |
| name | string | |
| subtitle | string | null | |
| category | string | camelCase enum. |
/api/v1/equipment/{id}exercises:readJedna položka vybavení podle id v katalogu.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
idpovinný | path | integer | Id vybavení v katalogu. |
Response: Vybavení
| Pole | Typ | Popis |
|---|---|---|
| …stejné jako položka seznamu |
OAuth
Token endpointy přijímají form-encoded data (application/x-www-form-urlencoded) a řídí se RFC 6749/7009. Celý flow je popsaný na stránce OAuth aplikací.
/oauth/tokenVýměna autorizačního kódu, nebo rotace refresh tokenu, za pár access/refresh tokenů.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
grant_typepovinný | form | string | "authorization_code" nebo "refresh_token". |
client_idpovinný | form | string | |
client_secret | form | string | Jen confidential klienti; public klienti ho vynechávají (místo něj PKCE). |
code | form | string | Grant authorization_code: kód gac_…. |
redirect_uri | form | string | Grant authorization_code: musí odpovídat URI, pro které byl kód vydán. |
code_verifier | form | string | Grant authorization_code: PKCE verifier k tvé challenge. |
refresh_token | form | string | Grant refresh_token: token grt_… (na jedno použití, při úspěchu rotuje). |
Response: Token response
| Pole | Typ | Popis |
|---|---|---|
| access_token | string | Bearer token (gat_…), platný 6 hodin. |
| token_type | "Bearer" | |
| expires_in | integer | Sekundy do expirace access tokenu. |
| refresh_token | string | Nový rotující refresh token (grt_…), platný až 180 dní. |
| scope | string | Udělené scopes oddělené mezerami. |
{
"access_token": "gat_…",
"token_type": "Bearer",
"expires_in": 21600,
"refresh_token": "grt_…",
"scope": "workouts:read stats:read"
}/oauth/revokeZneplatnění tokenu (RFC 7009). Zneplatnění refresh tokenu odvolá celý grant. Vrací 200 i pro neznámé tokeny.
Parametry
| Název | Kde | Typ | Popis |
|---|---|---|---|
tokenpovinný | form | string | Access nebo refresh token ke zneplatnění. |
client_idpovinný | form | string | |
client_secret | form | string | Jen confidential klienti. |
Response: Prázdná
| Pole | Typ | Popis |
|---|---|---|
| (bez těla) | 200 při úspěchu; 401 invalid_client při špatných credentials. |