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

MetodoPercorsoScope
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

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

GET/api/v1/userprofile:read

Il profilo dell'utente che ha autorizzato l'accesso: campi visualizzati, unità preferite e obiettivo di allenamento.

Risposta: Profilo utente

CampoTipoDescrizione
idstringIdentificatore stabile dell'utente. Usalo come chiave nel tuo storage.
displayNamestring | null
usernamestring | null
weightUnit"kilograms" | "pounds"Unità di visualizzazione preferita. I pesi dell'API sono sempre in libbre.
distanceUnit"kilometers" | "miles"
gender"male" | "female" | "other" | null
heightnumber | nullNell'unità indicata da heightUnit.
heightUnit"centimeters" | "feetInches"
goal"stronger" | "muscleMass" | "lean" | "generalFitness" | null
Risposta di esempio
{
  "id": "8fKq2ZbXvNc…",
  "displayName": "Alex",
  "username": "alexlifts",
  "weightUnit": "pounds",
  "distanceUnit": "kilometers",
  "gender": "male",
  "height": 183,
  "heightUnit": "centimeters",
  "goal": "muscleMass"
}

Allenamenti

GET/api/v1/workoutsworkouts:readpaginated

La cronologia degli allenamenti dell'utente, dal più recente al più vecchio.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30
startDatequerystring (ISO 8601)Solo gli allenamenti iniziati in questo istante o dopo (incluso).
endDatequerystring (ISO 8601)Solo gli allenamenti iniziati in questo istante o prima (incluso).

Risposta: Riepilogo allenamento (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
idstring (uuid)
namestring
notesstring | null
startDatestring (ISO 8601, UTC)
endDatestring (ISO 8601, UTC)
durationMinutesinteger
typestringOrigine dell'allenamento, es. "custom", "external". Potrebbero comparire nuovi valori.
volumenumberVolume totale sollevato, in libbre.
caloriesnumber
personalRecordCountintegerRecord personali stabiliti in questo allenamento.
exerciseCountinteger
Risposta di esempio
{
  "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

Un allenamento completo: ogni esercizio con le serie registrate, nell'ordine di esecuzione.

Parametri

NomeInTipoDescrizione
idobbligatoriopathstring (uuid)L'id dell'allenamento restituito dall'endpoint di elenco.

Risposta: Dettaglio allenamento

CampoTipoDescrizione
…tutti i campi del riepilogo allenamentoCome l'elemento dell'elenco, senza exerciseCount.
exercises[].exerciseIdintegerId dell'esercizio a catalogo.
exercises[].exerciseNamestring
exercises[].supersetIdinteger | nullGli esercizi che condividono lo stesso id sono stati eseguiti in superset.
exercises[].sets[].orderintegerPosizione all'interno dell'esercizio, a partire da 1.
exercises[].sets[].repsintegerRipetizioni eseguite.
exercises[].sets[].weightnumberLibbre.
exercises[].sets[].durationinteger | nullSecondi, per le serie a tempo.
exercises[].sets[].distanceinteger | nullMetri, per le serie a distanza.
exercises[].sets[].rpeinteger | nullSforzo percepito, RPE (1–10).
exercises[].sets[].setType"normal" | "warmup" | "dropSet" | "failure"
Risposta di esempio
{
  "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

I template di allenamento salvati dall'utente, dal più recente al più vecchio. Le serie sono valori pianificati, non registrazioni.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30

Risposta: Riepilogo template (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
idstring (uuid)
namestring
descriptionstring | null
exerciseCountinteger
exercisesarraySempre vuoto nelle risposte di elenco. Usa l'endpoint di dettaglio.
GET/api/v1/workout-templates/{id}workouts:read

Un template con i suoi esercizi e le sue serie pianificati.

Parametri

NomeInTipoDescrizione
idobbligatoriopathstring (uuid)L'id del template restituito dall'endpoint di elenco.

Risposta: Dettaglio template

CampoTipoDescrizione
…tutti i campi del riepilogo template
exercises[].exerciseIdinteger
exercises[].exerciseNamestring
exercises[].supersetIdinteger | null
exercises[].sets[].orderintegerPosizione all'interno dell'esercizio, a partire da 1.
exercises[].sets[].repsinteger | nullRipetizioni pianificate.
exercises[].sets[].weightnumber | nullLibbre.
exercises[].sets[].durationinteger | nullSecondi, per le serie a tempo.
exercises[].sets[].distanceinteger | nullMetri, per le serie a distanza.
exercises[].sets[].rpeinteger | nullSforzo percepito, RPE (1–10).
exercises[].sets[].setType"normal" | "warmup" | "dropSet" | "failure"
GET/api/v1/splitssplits:readpaginated

Gli split di allenamento personalizzati dell'utente, dal più recente al più vecchio.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30

Risposta: Riepilogo split (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
idstring (uuid)
namestring | null
descriptionstring | null
sessionCountinteger
sessionsarraySempre vuoto nelle risposte di elenco. Usa l'endpoint di dettaglio.
GET/api/v1/splits/{id}splits:read

Uno split con le sue sessioni in ordine.

Parametri

NomeInTipoDescrizione
idobbligatoriopathstring (uuid)L'id dello split restituito dall'endpoint di elenco.

Risposta: Dettaglio split

CampoTipoDescrizione
…tutti i campi del riepilogo split
sessions[].orderinteger
sessions[].type"muscles" | "workoutTemplate"
sessions[].musclesstring[]Muscoli target (camelCase, es. "chest"); valorizzato quando type è muscles.
sessions[].workoutTemplateIdstring (uuid) | nullTemplate collegato; valorizzato quando type è workoutTemplate.

Record e misurazioni

GET/api/v1/personal-recordsrecords:readpaginated

I record personali dell'utente, dal più recente al più vecchio.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30
exerciseIdqueryintegerFiltra per un singolo esercizio del catalogo.

Risposta: Record personale (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
exerciseIdinteger
exerciseNamestring
type"oneRepMax" | "weightMax" | "volumeMax" | "repetitionMax" | "durationMax" | "distanceMax"
repsinteger | null
weightnumber | nullLibbre.
durationinteger | nullSecondi.
distanceinteger | nullMetri.
workoutIdstring (uuid) | nullAllenamento in cui il record è stato stabilito, se noto.
achievedAtstring (ISO 8601, UTC)
Risposta di esempio
{
  "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

Voci di peso corporeo e circonferenze, dalla più recente alla più vecchia.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30
startDatequerystring (ISO 8601)Solo le voci con data uguale o successiva a questa (inclusa).
endDatequerystring (ISO 8601)Solo le voci con data uguale o precedente a questa (inclusa).

Risposta: Misurazione (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
idstring (uuid)
datestring (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.
weightnumber | null
bodyFatnumber | nullPercentuale.
neck / shoulder / chest / bicep / forearm / abdomen / waist / glutes / thigh / calfnumber | nullCirconferenze nel sistema di unità della voce.

Statistiche e trofei

GET/api/v1/statsstats:read

Le statistiche di allenamento principali dell'utente in una sola chiamata.

Risposta: Statistiche

CampoTipoDescrizione
streak.currentintegerStreak attuale, in settimane.
streak.longestintegerStreak più lunga di sempre, in settimane.
streak.workoutsThisWeekinteger
totalXpinteger
trophyCountinteger
Risposta di esempio
{
  "streak": { "current": 6, "longest": 14, "workoutsThisWeek": 3 },
  "totalXp": 12480,
  "trophyCount": 22
}
GET/api/v1/trophiesstats:readpaginated

Trofei ottenuti, dal più recente al più vecchio.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30

Risposta: Trofeo (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
typestringTipo di trofeo, in camelCase, es. "workout100", "streak5", "calendar15". Potrebbero comparire nuovi valori.
exerciseIdinteger | nullValorizzato per i trofei legati a un esercizio specifico.
exerciseNamestring | null
achievedAtstring (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.

GET/api/v1/exercisesexercises:readpaginated

Il catalogo degli esercizi più gli esercizi personalizzati dell'utente, in ordine alfabetico.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30
searchquerystringCorrispondenza sul nome senza distinzione tra maiuscole e minuscole (contiene). Max 200 caratteri.

Risposta: Esercizio (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
idintegerId stabile del catalogo, referenziato da allenamenti, record e template.
namestring
descriptionstring | null
categorystringEnum in camelCase, es. "barbell", "dumbbell".
typestringEnum in camelCase, es. "weight", "duration".
musclestringMuscolo principale, in camelCase, es. "chest".
secondaryMusclesstring[]
equipmentstring[]Nomi dell'attrezzatura utilizzabile per questo esercizio.
GET/api/v1/exercises/{id}exercises:read

Un esercizio per id di catalogo.

Parametri

NomeInTipoDescrizione
idobbligatoriopathintegerId dell'esercizio a catalogo.

Risposta: Esercizio

CampoTipoDescrizione
…come l'elemento dell'elenco
GET/api/v1/equipmentexercises:readpaginated

Il catalogo globale dell'attrezzatura, in ordine alfabetico.

Parametri

NomeInTipoDescrizione
pagequeryintegerNumero di pagina, a partire da 1. Default: 1
pageSizequeryintegerElementi per pagina, 1–100. Default: 30

Risposta: Attrezzatura (come items[] nell'envelope di paginazione)

CampoTipoDescrizione
idintegerId stabile del catalogo.
namestring
subtitlestring | null
categorystringEnum in camelCase.
GET/api/v1/equipment/{id}exercises:read

Un elemento di attrezzatura per id di catalogo.

Parametri

NomeInTipoDescrizione
idobbligatoriopathintegerId dell'attrezzatura a catalogo.

Risposta: Attrezzatura

CampoTipoDescrizione
…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.

GET/oauth/authorize

Schermata di consenso ospitata che avvia il flusso authorization-code. L'utente accede e approva; il suo browser viene reindirizzato indietro con ?code=gac_…&state=….

Parametri

NomeInTipoDescrizione
client_idobbligatorioquerystringIl client id della tua app.
redirect_uriobbligatorioquerystringDeve corrispondere esattamente a una redirect URI registrata.
scopequerystringScope separati da spazi. Omettilo per tutti gli scope consentiti alla tua app.
statequerystringValore casuale restituito così com'è. Verificalo al ritorno (protezione CSRF).
code_challengequerystringChallenge PKCE S256. Obbligatorio per i client pubblici.
code_challenge_methodquerystringÈ supportato solo "S256".

Risposta: Redirect

CampoTipoDescrizione
codestringAuthorization code monouso (gac_…), valido 10 minuti. Restituito sul redirect, non come JSON.
statestringRestituito identico alla richiesta.
POST/oauth/token

Scambia un authorization code, o ruota un refresh token, per una coppia di access/refresh token.

Parametri

NomeInTipoDescrizione
grant_typeobbligatorioformstring"authorization_code" o "refresh_token".
client_idobbligatorioformstring
client_secretformstringSolo per i client confidenziali; i client pubblici lo omettono (PKCE al suo posto).
codeformstringGrant authorization_code: il codice gac_….
redirect_uriformstringGrant authorization_code: deve corrispondere alla URI per cui il codice è stato emesso.
code_verifierformstringGrant authorization_code: il verifier PKCE della tua challenge.
refresh_tokenformstringGrant refresh_token: il token grt_… (monouso; ruotato in caso di successo).

Risposta: Risposta token

CampoTipoDescrizione
access_tokenstringBearer token (gat_…), valido 6 ore.
token_type"Bearer"
expires_inintegerSecondi alla scadenza dell'access token.
refresh_tokenstringNuovo refresh token a rotazione (grt_…), valido fino a 180 giorni.
scopestringScope concessi, separati da spazi.
Risposta di esempio
{
  "access_token": "gat_…",
  "token_type": "Bearer",
  "expires_in": 21600,
  "refresh_token": "grt_…",
  "scope": "workouts:read stats:read"
}
POST/oauth/revoke

Revoca un token (RFC 7009). Revocare un refresh token revoca l'intero grant. Restituisce 200 anche per token sconosciuti.

Parametri

NomeInTipoDescrizione
tokenobbligatorioformstringL'access token o il refresh token da revocare.
client_idobbligatorioformstring
client_secretformstringSolo per i client confidenziali.

Risposta: Vuota

CampoTipoDescrizione
(nessun corpo)200 in caso di successo; 401 invalid_client per credenziali errate.