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

MetodaCestaScope
GET/api/v1/userprofile:read
GET/api/v1/workoutsworkouts:read
GET/api/v1/workouts/{id}workouts:read
GET/api/v1/workout-templatesworkouts:read
GET/api/v1/workout-templates/{id}workouts:read
GET/api/v1/splitssplits:read
GET/api/v1/splits/{id}splits:read
GET/api/v1/personal-recordsrecords:read
GET/api/v1/measurementsmeasurements:read
GET/api/v1/statsstats:read
GET/api/v1/trophiesstats:read
GET/api/v1/exercisesexercises:read
GET/api/v1/exercises/{id}exercises:read
GET/api/v1/equipmentexercises: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

GET/api/v1/userprofile:read

Profil autorizujícího uživatele: zobrazovaná pole, preferované jednotky a tréninkový cíl.

Response: Profil uživatele

PoleTypPopis
idstringStabilní identifikátor uživatele; klíčuj podle něj svoje úložiště.
displayNamestring | null
usernamestring | null
weightUnit"kilograms" | "pounds"Preferovaná zobrazovací jednotka; váhy v API jsou vždy libry.
distanceUnit"kilometers" | "miles"
gender"male" | "female" | "other" | null
heightnumber | nullV jednotce dané polem heightUnit.
heightUnit"centimeters" | "feetInches"
goal"stronger" | "muscleMass" | "lean" | "generalFitness" | null
Příklad response
{
  "id": "8fKq2ZbXvNc…",
  "displayName": "Alex",
  "username": "alexlifts",
  "weightUnit": "pounds",
  "distanceUnit": "kilometers",
  "gender": "male",
  "height": 183,
  "heightUnit": "centimeters",
  "goal": "muscleMass"
}

Tréninky

GET/api/v1/workoutsworkouts:readpaginated

Historie tréninků uživatele, od nejnovějších.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30
startDatequerystring (ISO 8601)Jen tréninky začínající v tento okamžik nebo po něm (včetně).
endDatequerystring (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)

PoleTypPopis
idstring (uuid)
namestring
notesstring | null
startDatestring (ISO 8601, UTC)
endDatestring (ISO 8601, UTC)
durationMinutesinteger
typestringPůvod tréninku, např. "custom", "external". Můžou přibýt nové hodnoty.
volumenumberCelkový zvednutý objem, v librách.
caloriesnumber
personalRecordCountintegerOsobní rekordy zaznamenané v tomto tréninku.
exerciseCountinteger
Příklad response
{
  "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
}
GET/api/v1/workouts/{id}workouts:read

Jeden trénink v plném detailu: každý cvik se zalogovanými sériemi, v pořadí provedení.

Parametry

NázevKdeTypPopis
idpovinnýpathstring (uuid)Id tréninku z endpointu se seznamem.

Response: Detail tréninku

PoleTypPopis
…všechna pole souhrnu tréninkuStejná jako položka seznamu, bez exerciseCount.
exercises[].exerciseIdintegerId cviku v katalogu.
exercises[].exerciseNamestring
exercises[].supersetIdinteger | nullCviky se stejným id byly provedeny jako superset.
exercises[].sets[].orderintegerPozice v rámci cviku, od 1.
exercises[].sets[].repsintegerProvedená opakování.
exercises[].sets[].weightnumberLibry.
exercises[].sets[].durationinteger | nullSekundy, u sérií na čas.
exercises[].sets[].distanceinteger | nullMetry, u sérií na vzdálenost.
exercises[].sets[].rpeinteger | nullSubjektivně vnímaná námaha (RPE, 1–10).
exercises[].sets[].setType"normal" | "warmup" | "dropSet" | "failure"
Příklad response
{
  "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" }
      ]
    }
  ]
}
GET/api/v1/workout-templatesworkouts:readpaginated

Uložené tréninkové šablony uživatele, od nejnovějších. Série jsou plánované hodnoty, ne záznamy.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30

Response: Souhrn šablony (jako items[] ve stránkovací obálce)

PoleTypPopis
idstring (uuid)
namestring
descriptionstring | null
exerciseCountinteger
exercisesarrayVe výpisu vždy prázdné; použij detailový endpoint.
GET/api/v1/workout-templates/{id}workouts:read

Jedna šablona s plánovanými cviky a sériemi.

Parametry

NázevKdeTypPopis
idpovinnýpathstring (uuid)Id šablony z endpointu se seznamem.

Response: Detail šablony

PoleTypPopis
…všechna pole souhrnu šablony
exercises[].exerciseIdinteger
exercises[].exerciseNamestring
exercises[].supersetIdinteger | null
exercises[].sets[].orderintegerPozice v rámci cviku, od 1.
exercises[].sets[].repsinteger | nullPlánovaná opakování.
exercises[].sets[].weightnumber | nullLibry.
exercises[].sets[].durationinteger | nullSekundy, u sérií na čas.
exercises[].sets[].distanceinteger | nullMetry, u sérií na vzdálenost.
exercises[].sets[].rpeinteger | nullSubjektivně vnímaná námaha (RPE, 1–10).
exercises[].sets[].setType"normal" | "warmup" | "dropSet" | "failure"
GET/api/v1/splitssplits:readpaginated

Vlastní tréninkové splity uživatele, od nejnovějších.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30

Response: Souhrn splitu (jako items[] ve stránkovací obálce)

PoleTypPopis
idstring (uuid)
namestring | null
descriptionstring | null
sessionCountinteger
sessionsarrayVe výpisu vždy prázdné; použij detailový endpoint.
GET/api/v1/splits/{id}splits:read

Jeden split s jeho sessions v pořadí.

Parametry

NázevKdeTypPopis
idpovinnýpathstring (uuid)Id splitu z endpointu se seznamem.

Response: Detail splitu

PoleTypPopis
…všechna pole souhrnu splitu
sessions[].orderinteger
sessions[].type"muscles" | "workoutTemplate"
sessions[].musclesstring[]Cílové svaly (camelCase, např. "chest"); vyplněno, když je type muscles.
sessions[].workoutTemplateIdstring (uuid) | nullNavázaná šablona; vyplněno, když je type workoutTemplate.

Rekordy a míry

GET/api/v1/personal-recordsrecords:readpaginated

Osobní rekordy uživatele, od nejnovějších.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30
exerciseIdqueryintegerFiltr na jeden cvik z katalogu.

Response: Osobní rekord (jako items[] ve stránkovací obálce)

PoleTypPopis
exerciseIdinteger
exerciseNamestring
type"oneRepMax" | "weightMax" | "volumeMax" | "repetitionMax" | "durationMax" | "distanceMax"
repsinteger | null
weightnumber | nullLibry.
durationinteger | nullSekundy.
distanceinteger | nullMetry.
workoutIdstring (uuid) | nullTrénink, ve kterém rekord padl, pokud je znám.
achievedAtstring (ISO 8601, UTC)
Příklad response
{
  "exerciseId": 87,
  "exerciseName": "Deadlift",
  "type": "oneRepMax",
  "reps": 1,
  "weight": 405,
  "duration": null,
  "distance": null,
  "workoutId": "a1b2c3d4-…",
  "achievedAt": "2026-06-28T18:41:00Z"
}
GET/api/v1/measurementsmeasurements:readpaginated

Záznamy tělesné váhy a obvodů, od nejnovějších.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30
startDatequerystring (ISO 8601)Jen záznamy s datem v tento den nebo po něm (včetně).
endDatequerystring (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)

PoleTypPopis
idstring (uuid)
datestring (ISO 8601 date)
weightUnit"kilograms" | "pounds"Systém jednotek tohoto záznamu: obvody jsou v palcích u liber a v centimetrech u kilogramů.
weightnumber | null
bodyFatnumber | nullProcenta.
neck / shoulder / chest / bicep / forearm / abdomen / waist / glutes / thigh / calfnumber | nullObvody v systému jednotek daného záznamu.

Statistiky a trofeje

GET/api/v1/statsstats:read

Hlavní tréninkové statistiky uživatele v jednom volání.

Response: Statistiky

PoleTypPopis
streak.currentintegerAktuální streak, v týdnech.
streak.longestintegerNejdelší streak vůbec, v týdnech.
streak.workoutsThisWeekinteger
totalXpinteger
trophyCountinteger
Příklad response
{
  "streak": { "current": 6, "longest": 14, "workoutsThisWeek": 3 },
  "totalXp": 12480,
  "trophyCount": 22
}
GET/api/v1/trophiesstats:readpaginated

Získané trofeje, od nejnovějších.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30

Response: Trofej (jako items[] ve stránkovací obálce)

PoleTypPopis
typestringDruh trofeje, camelCase, např. "workout100", "streak5", "calendar15". Můžou přibýt nové hodnoty.
exerciseIdinteger | nullVyplněno u trofejí vázaných na konkrétní cvik.
exerciseNamestring | null
achievedAtstring (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.

GET/api/v1/exercisesexercises:readpaginated

Katalog cviků plus vlastní cviky uživatele, abecedně.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30
searchquerystringHledání v názvu bez ohledu na velikost písmen (obsahuje). Max 200 znaků.

Response: Cvik (jako items[] ve stránkovací obálce)

PoleTypPopis
idintegerStabilní id v katalogu; odkazují na něj tréninky, rekordy a šablony.
namestring
descriptionstring | null
categorystringcamelCase enum, např. "barbell", "dumbbell".
typestringcamelCase enum, např. "weight", "duration".
musclestringHlavní sval, camelCase, např. "chest".
secondaryMusclesstring[]
equipmentstring[]Názvy vybavení použitelného pro tento cvik.
GET/api/v1/exercises/{id}exercises:read

Jeden cvik podle id v katalogu.

Parametry

NázevKdeTypPopis
idpovinnýpathintegerId cviku v katalogu.

Response: Cvik

PoleTypPopis
…stejné jako položka seznamu
GET/api/v1/equipmentexercises:readpaginated

Globální katalog vybavení, abecedně.

Parametry

NázevKdeTypPopis
pagequeryintegerČíslo stránky, od 1. Výchozí: 1
pageSizequeryintegerPočet položek na stránku, 1–100. Výchozí: 30

Response: Vybavení (jako items[] ve stránkovací obálce)

PoleTypPopis
idintegerStabilní id v katalogu.
namestring
subtitlestring | null
categorystringcamelCase enum.
GET/api/v1/equipment/{id}exercises:read

Jedna položka vybavení podle id v katalogu.

Parametry

NázevKdeTypPopis
idpovinnýpathintegerId vybavení v katalogu.

Response: Vybavení

PoleTypPopis
…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í.

GET/oauth/authorize

Hostovaná obrazovka souhlasu; zahajuje authorization-code flow. Uživatel se přihlásí a potvrdí, jeho prohlížeč je přesměrován zpět s ?code=gac_…&state=….

Parametry

NázevKdeTypPopis
client_idpovinnýquerystringClient id tvé aplikace.
redirect_uripovinnýquerystringMusí přesně odpovídat registrovanému redirect URI.
scopequerystringScopes oddělené mezerami. Vynech pro všechny scopes povolené tvé aplikaci.
statequerystringNáhodná hodnota vrácená zpět; při návratu ji ověř (ochrana proti CSRF).
code_challengequerystringPKCE S256 challenge. Povinné pro public klienty.
code_challenge_methodquerystringPodporováno je pouze "S256".

Response: Přesměrování

PoleTypPopis
codestringJednorázový autorizační kód (gac_…), platný 10 minut. Vrací se v přesměrování, ne jako JSON.
statestringZopakováno z requestu.
POST/oauth/token

Výměna autorizačního kódu, nebo rotace refresh tokenu, za pár access/refresh tokenů.

Parametry

NázevKdeTypPopis
grant_typepovinnýformstring"authorization_code" nebo "refresh_token".
client_idpovinnýformstring
client_secretformstringJen confidential klienti; public klienti ho vynechávají (místo něj PKCE).
codeformstringGrant authorization_code: kód gac_….
redirect_uriformstringGrant authorization_code: musí odpovídat URI, pro které byl kód vydán.
code_verifierformstringGrant authorization_code: PKCE verifier k tvé challenge.
refresh_tokenformstringGrant refresh_token: token grt_… (na jedno použití, při úspěchu rotuje).

Response: Token response

PoleTypPopis
access_tokenstringBearer token (gat_…), platný 6 hodin.
token_type"Bearer"
expires_inintegerSekundy do expirace access tokenu.
refresh_tokenstringNový rotující refresh token (grt_…), platný až 180 dní.
scopestringUdělené scopes oddělené mezerami.
Příklad response
{
  "access_token": "gat_…",
  "token_type": "Bearer",
  "expires_in": 21600,
  "refresh_token": "grt_…",
  "scope": "workouts:read stats:read"
}
POST/oauth/revoke

Zneplatnění tokenu (RFC 7009). Zneplatnění refresh tokenu odvolá celý grant. Vrací 200 i pro neznámé tokeny.

Parametry

NázevKdeTypPopis
tokenpovinnýformstringAccess nebo refresh token ke zneplatnění.
client_idpovinnýformstring
client_secretformstringJen confidential klienti.

Response: Prázdná

PoleTypPopis
(bez těla)200 při úspěchu; 401 invalid_client při špatných credentials.