Riferimento
Endpoint
Tutto vive sotto https://api.gravl.ai e restituisce JSON. I dati utente sono identificati da GUID stabili; le risorse di catalogo da id interi. I valori enum sono stringhe camelCase: tratta i valori sconosciuti come forward-compatible.
Tutti gli endpoint
| Metodo | Percorso | 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 | — |
L'envelope di paginazione
Gli endpoint contrassegnati come paginated qui sotto racchiudono gli elementi in un unico envelope standard:
{
"items": [ … ], // the page of results
"pageNumber": 2,
"totalPages": 9,
"totalCount": 174,
"hasPreviousPage": true,
"hasNextPage": true
}Ogni endpoint richiede il proprio scope: uno scope mancante restituisce 403 con problem details. Errori e rate limit sono documentati nelle convenzioni.
Utente
/api/v1/userprofile:readIl profilo dell'utente che ha autorizzato l'accesso: campi visualizzati, unità preferite e obiettivo di allenamento.
Risposta: Profilo utente
| Campo | Tipo | Descrizione |
|---|---|---|
| id | string | Identificatore stabile dell'utente. Usalo come chiave nel tuo storage. |
| displayName | string | null | |
| username | string | null | |
| weightUnit | "kilograms" | "pounds" | Unità di visualizzazione preferita. I pesi dell'API sono sempre in libbre. |
| distanceUnit | "kilometers" | "miles" | |
| gender | "male" | "female" | "other" | null | |
| height | number | null | Nell'unità indicata da 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"
}Allenamenti
/api/v1/workoutsworkouts:readpaginatedLa cronologia degli allenamenti dell'utente, dal più recente al più vecchio.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
startDate | query | string (ISO 8601) | Solo gli allenamenti iniziati in questo istante o dopo (incluso). |
endDate | query | string (ISO 8601) | Solo gli allenamenti iniziati in questo istante o prima (incluso). |
Risposta: Riepilogo allenamento (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| id | string (uuid) | |
| name | string | |
| notes | string | null | |
| startDate | string (ISO 8601, UTC) | |
| endDate | string (ISO 8601, UTC) | |
| durationMinutes | integer | |
| type | string | Origine dell'allenamento, es. "custom", "external". Potrebbero comparire nuovi valori. |
| volume | number | Volume totale sollevato, in libbre. |
| calories | number | |
| personalRecordCount | integer | Record personali stabiliti in questo allenamento. |
| 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:readUn allenamento completo: ogni esercizio con le serie registrate, nell'ordine di esecuzione.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string (uuid) | L'id dell'allenamento restituito dall'endpoint di elenco. |
Risposta: Dettaglio allenamento
| Campo | Tipo | Descrizione |
|---|---|---|
| …tutti i campi del riepilogo allenamento | Come l'elemento dell'elenco, senza exerciseCount. | |
| exercises[].exerciseId | integer | Id dell'esercizio a catalogo. |
| exercises[].exerciseName | string | |
| exercises[].supersetId | integer | null | Gli esercizi che condividono lo stesso id sono stati eseguiti in superset. |
| exercises[].sets[].order | integer | Posizione all'interno dell'esercizio, a partire da 1. |
| exercises[].sets[].reps | integer | Ripetizioni eseguite. |
| exercises[].sets[].weight | number | Libbre. |
| exercises[].sets[].duration | integer | null | Secondi, per le serie a tempo. |
| exercises[].sets[].distance | integer | null | Metri, per le serie a distanza. |
| exercises[].sets[].rpe | integer | null | Sforzo percepito, 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:readpaginatedI template di allenamento salvati dall'utente, dal più recente al più vecchio. Le serie sono valori pianificati, non registrazioni.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
Risposta: Riepilogo template (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| id | string (uuid) | |
| name | string | |
| description | string | null | |
| exerciseCount | integer | |
| exercises | array | Sempre vuoto nelle risposte di elenco. Usa l'endpoint di dettaglio. |
/api/v1/workout-templates/{id}workouts:readUn template con i suoi esercizi e le sue serie pianificati.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string (uuid) | L'id del template restituito dall'endpoint di elenco. |
Risposta: Dettaglio template
| Campo | Tipo | Descrizione |
|---|---|---|
| …tutti i campi del riepilogo template | ||
| exercises[].exerciseId | integer | |
| exercises[].exerciseName | string | |
| exercises[].supersetId | integer | null | |
| exercises[].sets[].order | integer | Posizione all'interno dell'esercizio, a partire da 1. |
| exercises[].sets[].reps | integer | null | Ripetizioni pianificate. |
| exercises[].sets[].weight | number | null | Libbre. |
| exercises[].sets[].duration | integer | null | Secondi, per le serie a tempo. |
| exercises[].sets[].distance | integer | null | Metri, per le serie a distanza. |
| exercises[].sets[].rpe | integer | null | Sforzo percepito, RPE (1–10). |
| exercises[].sets[].setType | "normal" | "warmup" | "dropSet" | "failure" |
/api/v1/splitssplits:readpaginatedGli split di allenamento personalizzati dell'utente, dal più recente al più vecchio.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
Risposta: Riepilogo split (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| id | string (uuid) | |
| name | string | null | |
| description | string | null | |
| sessionCount | integer | |
| sessions | array | Sempre vuoto nelle risposte di elenco. Usa l'endpoint di dettaglio. |
/api/v1/splits/{id}splits:readUno split con le sue sessioni in ordine.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string (uuid) | L'id dello split restituito dall'endpoint di elenco. |
Risposta: Dettaglio split
| Campo | Tipo | Descrizione |
|---|---|---|
| …tutti i campi del riepilogo split | ||
| sessions[].order | integer | |
| sessions[].type | "muscles" | "workoutTemplate" | |
| sessions[].muscles | string[] | Muscoli target (camelCase, es. "chest"); valorizzato quando type è muscles. |
| sessions[].workoutTemplateId | string (uuid) | null | Template collegato; valorizzato quando type è workoutTemplate. |
Record e misurazioni
/api/v1/personal-recordsrecords:readpaginatedI record personali dell'utente, dal più recente al più vecchio.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
exerciseId | query | integer | Filtra per un singolo esercizio del catalogo. |
Risposta: Record personale (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| exerciseId | integer | |
| exerciseName | string | |
| type | "oneRepMax" | "weightMax" | "volumeMax" | "repetitionMax" | "durationMax" | "distanceMax" | |
| reps | integer | null | |
| weight | number | null | Libbre. |
| duration | integer | null | Secondi. |
| distance | integer | null | Metri. |
| workoutId | string (uuid) | null | Allenamento in cui il record è stato stabilito, se noto. |
| 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:readpaginatedVoci di peso corporeo e circonferenze, dalla più recente alla più vecchia.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
startDate | query | string (ISO 8601) | Solo le voci con data uguale o successiva a questa (inclusa). |
endDate | query | string (ISO 8601) | Solo le voci con data uguale o precedente a questa (inclusa). |
Risposta: Misurazione (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| id | string (uuid) | |
| date | string (ISO 8601 date) | |
| weightUnit | "kilograms" | "pounds" | Sistema di unità di questa voce: le circonferenze sono in pollici con le libbre e in centimetri con i chilogrammi. |
| weight | number | null | |
| bodyFat | number | null | Percentuale. |
| neck / shoulder / chest / bicep / forearm / abdomen / waist / glutes / thigh / calf | number | null | Circonferenze nel sistema di unità della voce. |
Statistiche e trofei
/api/v1/statsstats:readLe statistiche di allenamento principali dell'utente in una sola chiamata.
Risposta: Statistiche
| Campo | Tipo | Descrizione |
|---|---|---|
| streak.current | integer | Streak attuale, in settimane. |
| streak.longest | integer | Streak più lunga di sempre, in settimane. |
| streak.workoutsThisWeek | integer | |
| totalXp | integer | |
| trophyCount | integer |
{
"streak": { "current": 6, "longest": 14, "workoutsThisWeek": 3 },
"totalXp": 12480,
"trophyCount": 22
}/api/v1/trophiesstats:readpaginatedTrofei ottenuti, dal più recente al più vecchio.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
Risposta: Trofeo (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| type | string | Tipo di trofeo, in camelCase, es. "workout100", "streak5", "calendar15". Potrebbero comparire nuovi valori. |
| exerciseId | integer | null | Valorizzato per i trofei legati a un esercizio specifico. |
| exerciseName | string | null | |
| achievedAt | string (ISO 8601, UTC) |
Catalogo esercizi e attrezzatura
Dati di riferimento globali, identificati dagli stessi id interi stabili usati dall'app Gravl. Gli elenchi di esercizi includono anche gli esercizi personalizzati dell'utente che ha autorizzato l'accesso, mai quelli di altri utenti.
/api/v1/exercisesexercises:readpaginatedIl catalogo degli esercizi più gli esercizi personalizzati dell'utente, in ordine alfabetico.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
search | query | string | Corrispondenza sul nome senza distinzione tra maiuscole e minuscole (contiene). Max 200 caratteri. |
Risposta: Esercizio (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| id | integer | Id stabile del catalogo, referenziato da allenamenti, record e template. |
| name | string | |
| description | string | null | |
| category | string | Enum in camelCase, es. "barbell", "dumbbell". |
| type | string | Enum in camelCase, es. "weight", "duration". |
| muscle | string | Muscolo principale, in camelCase, es. "chest". |
| secondaryMuscles | string[] | |
| equipment | string[] | Nomi dell'attrezzatura utilizzabile per questo esercizio. |
/api/v1/exercises/{id}exercises:readUn esercizio per id di catalogo.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | integer | Id dell'esercizio a catalogo. |
Risposta: Esercizio
| Campo | Tipo | Descrizione |
|---|---|---|
| …come l'elemento dell'elenco |
/api/v1/equipmentexercises:readpaginatedIl catalogo globale dell'attrezzatura, in ordine alfabetico.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
page | query | integer | Numero di pagina, a partire da 1. Default: 1 |
pageSize | query | integer | Elementi per pagina, 1–100. Default: 30 |
Risposta: Attrezzatura (come items[] nell'envelope di paginazione)
| Campo | Tipo | Descrizione |
|---|---|---|
| id | integer | Id stabile del catalogo. |
| name | string | |
| subtitle | string | null | |
| category | string | Enum in camelCase. |
/api/v1/equipment/{id}exercises:readUn elemento di attrezzatura per id di catalogo.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | integer | Id dell'attrezzatura a catalogo. |
Risposta: Attrezzatura
| Campo | Tipo | Descrizione |
|---|---|---|
| …come l'elemento dell'elenco |
OAuth
Gli endpoint dei token ricevono dati form-encoded (application/x-www-form-urlencoded) e seguono le RFC 6749/7009. Il flusso completo è documentato nella pagina delle app OAuth.
/oauth/tokenScambia un authorization code, o ruota un refresh token, per una coppia di access/refresh token.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
grant_typeobbligatorio | form | string | "authorization_code" o "refresh_token". |
client_idobbligatorio | form | string | |
client_secret | form | string | Solo per i client confidenziali; i client pubblici lo omettono (PKCE al suo posto). |
code | form | string | Grant authorization_code: il codice gac_…. |
redirect_uri | form | string | Grant authorization_code: deve corrispondere alla URI per cui il codice è stato emesso. |
code_verifier | form | string | Grant authorization_code: il verifier PKCE della tua challenge. |
refresh_token | form | string | Grant refresh_token: il token grt_… (monouso; ruotato in caso di successo). |
Risposta: Risposta token
| Campo | Tipo | Descrizione |
|---|---|---|
| access_token | string | Bearer token (gat_…), valido 6 ore. |
| token_type | "Bearer" | |
| expires_in | integer | Secondi alla scadenza dell'access token. |
| refresh_token | string | Nuovo refresh token a rotazione (grt_…), valido fino a 180 giorni. |
| scope | string | Scope concessi, separati da spazi. |
{
"access_token": "gat_…",
"token_type": "Bearer",
"expires_in": 21600,
"refresh_token": "grt_…",
"scope": "workouts:read stats:read"
}/oauth/revokeRevoca un token (RFC 7009). Revocare un refresh token revoca l'intero grant. Restituisce 200 anche per token sconosciuti.
Parametri
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
tokenobbligatorio | form | string | L'access token o il refresh token da revocare. |
client_idobbligatorio | form | string | |
client_secret | form | string | Solo per i client confidenziali. |
Risposta: Vuota
| Campo | Tipo | Descrizione |
|---|---|---|
| (nessun corpo) | 200 in caso di successo; 401 invalid_client per credenziali errate. |