L'API Google Health consente alla tua applicazione di ricevere notifiche in tempo reale quando i dati sulla salute di un utente cambiano. Anziché eseguire il polling per rilevare le modifiche, il tuo server riceve un webhook di richiesta POST HTTPS non appena i dati sono disponibili nell'API Google Health.
Tipi di dati supportati
Le notifiche webhook sono supportate per i seguenti tipi di dati:
| Tipo di dati | Operazioni disponibili |
Ambito |
|---|---|---|
|
Minuti attivi
dataType:
active-minutesfilter parameter: active_minutes
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
Dispositivi compatibili
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Minuti in zona attiva
dataType:
active-zone-minutesfilter parameter: active_zone_minutes
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
Dispositivi compatibili
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Livello di attività
dataType:
activity-levelfilter parameter: activity_level
Tipo di record: Interval
|
elenca, riconcilia | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Altitudine
dataType:
altitudefilter parameter: altitude
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Glicemia
dataType:
blood-glucosefilter parameter: blood_glucose
Tipo di record: Sample
|
list, get, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Grasso corporeo
dataType:
body-fatfilter parameter: body_fat
Tipo di record: Sample
Dispositivi compatibili
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Calorie nella zona del battito cardiaco
dataType:
calories-in-heart-rate-zonefilter parameter: calories_in_heart_rate_zone
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
|
rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Variabilità del battito cardiaco giornaliera
dataType:
daily-heart-rate-variabilityfilter parameter: daily_heart_rate_variability
Tipo di record: giornaliero
Dispositivi compatibili
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Zone del battito cardiaco giornaliere
dataType:
daily-heart-rate-zonesfilter parameter: daily_heart_rate_zones
Tipo di record: giornaliero
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Saturazione di ossigeno giornaliera
dataType:
daily-oxygen-saturationfilter parameter: daily_oxygen_saturation
Tipo di record: giornaliero
Dispositivi compatibili
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Frequenza respiratoria giornaliera
dataType:
daily-respiratory-ratefilter parameter: daily_respiratory_rate
Tipo di record: giornaliero
Dispositivi compatibili
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Battito cardiaco a riposo giornaliero
dataType:
daily-resting-heart-ratefilter parameter: daily_resting_heart_rate
Tipo di record: giornaliero
Dispositivi compatibili
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Derivazioni giornaliere della temperatura durante il sonno
dataType:
daily-sleep-temperature-derivationsfilter parameter: daily_sleep_temperature_derivations
Tipo di record: giornaliero
Dispositivi compatibili
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Distanza
dataType:
distancefilter parameter: distance
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
Dispositivi compatibili
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Allenamento
dataType:
exercisefilter parameter: exercise
Tipo di record: Sessione
Dispositivi compatibili
|
list, get, reconcile, create, update, batchDelete | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Piani
dataType:
floorsfilter parameter: floors
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
|
reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Battito cardiaco
dataType:
heart-ratefilter parameter: heart_rate
Tipo di record: Sample
Risoluzione dello spazio di archiviazione: 1 secondo
Dispositivi compatibili
|
list, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Variabilità del battito cardiaco
dataType:
heart-rate-variabilityfilter parameter: heart_rate_variability
Tipo di record: Sample
Dispositivi compatibili
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Altezza
|
list, get, reconcile, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Diario dell'idratazione
dataType:
hydration-logfilter parameter: hydration_log
Tipo di record: Sessione
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Diario alimentare
dataType:
nutrition-logfilter parameter: nutrition_log
Tipo di record: Sessione
Dispositivi compatibili
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Riepilogo del sonno relativo alla frequenza respiratoria
dataType:
respiratory-rate-sleep-summaryfilter parameter: respiratory_rate_sleep_summary
Tipo di record: Sample
Dispositivi compatibili
|
elenca, riconcilia | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
VO2 max corsa
dataType:
run-vo2-maxfilter parameter: run_vo2_max
Tipo di record: Sample
Dispositivi compatibili
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Periodo di sedentarietà
dataType:
sedentary-periodfilter parameter: sedentary_period
Tipo di record: Interval
Dispositivi compatibili
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Sonno
dataType:
sleepfilter parameter: sleep
Tipo di record: Sessione
Dispositivi compatibili
|
list, get, reconcile, create, update, batchDelete | .sleep.readonly.sleep.writeonly |
|
Passaggi
dataType:
stepsfilter parameter: steps
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
Dispositivi compatibili
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Tempo nella zona del battito cardiaco
dataType:
time-in-heart-rate-zonefilter parameter: time_in_heart_rate_zone
Tipo di record: Interval
Risoluzione di archiviazione: 1 minuto
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Peso
dataType:
weightfilter parameter: weight
Tipo di record: Sample
Dispositivi compatibili
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
Ambiti delle notifiche
Le notifiche vengono inviate per questi tipi di dati solo quando un utente ha concesso il consenso per uno degli ambiti corrispondenti:
- Attività, che copre i tipi di dati relativi a minuti attivi, passi, altitudine, distanza e piani:
https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonlyhttps://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly
- Metriche di salute, che copre il tipo di dati relativi al peso:
https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonlyhttps://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly
- Sonno, che copre il tipo di dati relativi al sonno:
https://www.googleapis.com/auth/googlehealth.sleep.readonlyhttps://www.googleapis.com/auth/googlehealth.sleep.writeonly
Service account IAM
Sebbene non sia obbligatorio, ti consigliamo di utilizzare un service account IAM durante la configurazione dei sottoscrittori per l'API Google Health. I service account offrono una maggiore sicurezza per i workload delle applicazioni rispetto agli account utente standard grazie alle seguenti funzionalità:
- Credenziali automatiche di breve durata: se collegati a un ambiente di esecuzione Google Cloud (come Compute Engine, Cloud Run o Google Kubernetes Engine), i service account ottengono e ruotano automaticamente credenziali sicure di breve durata. In questo modo si eliminano i rischi legati alla gestione e all'archiviazione di chiavi statiche persistenti.
- Principio del privilegio minimo: i service account forniscono identità dedicate per i workload. Puoi concedere loro solo le autorizzazioni specifiche necessarie per gestire gli endpoint dei sottoscrittori, evitando un accesso più ampio alle tue risorse Google Cloud.
- Indipendenza del ciclo di vita: i service account operano indipendentemente dall'account di un singolo utente, garantendo che i cambiamenti di personale non influiscano sulla stabilità dell'autenticazione a lungo termine.
Configura un service account
Per configurare l'applicazione del sottoscrittore in modo che utilizzi un service account per l'autenticazione:
- Crea un service account: nella console Google Cloud, vai alla pagina IAM e amministrazione del progetto per creare un nuovo service account gestito dall'utente.
- Concedi i ruoli IAM necessari: assegna al service account i ruoli appropriati necessari per gestire gli abbonati nel tuo progetto Google Cloud.
- Collega il service account al tuo workload: configura l'ambiente che ospita la logica del sottoscrittore in modo che venga eseguito come nuovo service account.
In questo modo, il codice dell'applicazione (ad esempio le librerie client delle API di Google) può rilevare e utilizzare automaticamente le credenziali di breve durata del service account quando chiama l'API REST
projects.subscribers.
Ruoli CPE
Per gestire gli iscritti o gli abbonamenti all'API Google Health, devi concedere il ruolo appropriato al service account di cui viene simulata l'identità che effettua le chiamate API. A seconda del livello di accesso necessario, assegna uno dei seguenti ruoli:
- Google Health API Read
- Google Health API Editor
- Google Health API Admin
Scopri di più sui ruoli e sulle autorizzazioni IAM dell'API Google Health.
Gestire gli iscritti
Prima di poter ricevere le notifiche, devi registrare un sottoscrittore, che rappresenta l'endpoint di notifica della tua applicazione. Puoi gestire gli iscritti utilizzando l'API REST disponibile all'indirizzo projects.subscribers.
L'endpoint del sottoscrittore deve utilizzare HTTPS (TLSv1.2+) ed essere accessibile pubblicamente.
Durante la creazione e gli aggiornamenti degli abbonati, l'API Google Health esegue una verifica per assicurarsi che l'URI dell'endpoint sia di tua proprietà. Se la verifica non va a buon fine, le operazioni di creazione e aggiornamento del sottoscrittore non vanno a buon fine con un errore FailedPreconditionException.
Crea un sottoscrittore
Per registrare un nuovo sottoscrittore per il tuo progetto, utilizza l'endpoint create. Devi fornire:
project-id: il numero di progetto in cui è stato creato il service account del webhook.subscriberId: un identificatore univoco che fornisci per il sottoscrittore. Questo ID deve essere compreso tra 4 e 36 caratteri e corrispondere all'espressione regolare ([a-z]([a-z0-9-]{2,34}[a-z0-9])).endpointUri: l'URL di destinazione per le notifiche webhook.subscriberConfigs: i tipi di dati per cui vuoi ricevere notifiche e la policy di sottoscrizione per ciascuno.endpointAuthorization: il meccanismo di autorizzazione per l'endpoint. Deve contenere unsecretfornito da te. Il valore disecretviene inviato nell'intestazioneAuthorizationcon ogni messaggio di notifica. Puoi utilizzare questo token per verificare che le richieste in entrata provengano dall'API Google Health. Ad esempio, puoi impostaresecretsuBearer R4nd0m5tr1ng123per l'autenticazione di connessione (bearer) oBasic dXNlcjpwYXNzd29yZA==per l'autenticazione di base.
In subscriberConfigs devi impostare subscriptionCreatePolicy per ogni tipo di dati. Impostalo su AUTOMATIC per utilizzare gli abbonamenti automatici o su MANUAL se intendi gestire autonomamente gli abbonamenti degli utenti. Per ulteriori dettagli su ciascuna opzione, consulta Sottoscrizioni automatiche e Sottoscrizioni manuali.
Richiesta
POST https://health.googleapis.com/v4/projects/project-id/subscribers?subscriberId=subscriber-id
{
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
],
"endpointAuthorization": {
"secret": "Bearer example-secret-token"
}
}Risposta
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
]
}Elenca gli iscritti
Utilizza l'endpoint list per recuperare tutti i sottoscrittori registrati per il tuo progetto.
Richiesta
GET https://health.googleapis.com/v4/projects/project-id/subscribers
Risposta
{
"subscribers": [
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
],
"endpointAuthorization": {
"authorizationTokenSet": true
}
}
],
"totalSize": 1
}Aggiorna un sottoscrittore
Utilizza l'endpoint patch per aggiornare un sottoscrittore nel tuo progetto. I campi che possono essere aggiornati sono
endpointUri, subscriberConfigs e endpointAuthorization.
Per aggiornare i campi, devi fornire un parametro di query updateMask e un corpo della richiesta. updateMask deve contenere un elenco separato da virgole dei nomi dei campi che vuoi aggiornare, utilizzando la notazione a cammello per i nomi dei campi (ad esempio, endpointUri). Il corpo della richiesta deve contenere un oggetto Subscriber parziale con i nuovi valori per i campi che vuoi aggiornare. Vengono aggiornati solo i campi specificati in updateMask. Se nel corpo della richiesta fornisci campi che non sono presenti in updateMask, questi vengono ignorati.
Se aggiorni endpointUri o endpointAuthorization, viene eseguita la verifica degli endpoint. Per maggiori dettagli, vedi Verifica degli endpoint.
Quando aggiorni subscriberConfigs, tieni presente che si tratta di una sostituzione completa, non di un'unione. Se subscriberConfigs è incluso in updateMask, tutte le configurazioni archiviate per quel sottoscrittore vengono sovrascritte con l'elenco fornito nel corpo della richiesta. Per aggiungere o rimuovere una configurazione, devi fornire l'insieme completo di configurazioni. Se stai aggiornando altri campi e vuoi mantenere le configurazioni attuali, ometti subscriberConfigs da updateMask.
Richiesta
PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id?updateMask=endpointUri
{
"endpointUri": "https://myapp.com/new-webhooks/health"
}Risposta
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/new-webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
]
}Elimina un abbonato
Utilizza l'endpoint delete per rimuovere un abbonato dal tuo progetto. Una volta eliminato, l'iscritto non riceverà più notifiche.
Richiesta
DELETE https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id
Risposta
Se l'eliminazione va a buon fine, viene restituito un corpo della risposta vuoto con stato HTTP "200 OK".{}Verifica endpoint
Per garantire la sicurezza e l'affidabilità della distribuzione delle notifiche, l'API Google Health esegue un handshake di verifica in due passaggi obbligatorio ogni volta che crei un sottoscrittore o aggiorni la configurazione del relativo endpoint (endpointUri o endpointAuthorization). Questo processo viene eseguito in modo sincrono durante la chiamata API. Il servizio invia due richieste POST automatiche all'URI dell'endpoint,
utilizzando lo user agent Google-Health-API-Webhooks, con il corpo JSON
{"type": "verification"}.
- Handshake autorizzato: la prima richiesta viene inviata con l'intestazione
Authorizationconfigurata. Il server deve rispondere con uno stato200 OKo201 Created. - Unauthorized Challenge: la seconda richiesta viene inviata senza credenziali.
Il server deve rispondere con uno stato
401 Unauthorizedo403 Forbidden.
Questo handshake conferma che l'endpoint è attivo e applica correttamente la sicurezza. Se uno dei passaggi non va a buon fine, la richiesta API non va a buon fine e viene visualizzato un errore FAILED_PRECONDITION. Solo dopo che questo handshake è andato a buon fine, il tuo sottoscrittore viene salvato e attivato per ricevere notifiche sui dati sulla salute.
Rotazione delle chiavi
Se devi ruotare le chiavi per endpointAuthorization, segui questi passaggi:
- Configura l'endpoint in modo che accetti sia i vecchi sia i nuovi valori
endpointAuthorization. - Aggiorna la configurazione del sottoscrittore con il nuovo valore
endpointAuthorizationutilizzando una richiestapatchcon?updateMask=endpointAuthorization. - Configura l'endpoint in modo che accetti solo il nuovo valore
endpointAuthorizationdopo aver confermato che il passaggio 2 è andato a buon fine.
Abbonamenti utente
L'API Google Health ti aiuta a gestire in modo efficiente gli abbonamenti degli utenti, riducendo la necessità di registrazione manuale durante l'onboarding degli utenti.
Abbonamenti automatici
Ti consigliamo di utilizzare le iscrizioni automatiche. Per attivare questa funzionalità, imposta
subscriptionCreatePolicy su AUTOMATIC in subscriberConfigs per i
tipi di dati specifici. I dataTypes che specifichi con una policy AUTOMATIC sono gli stessi tipi di dati per i quali l'API Google Health invia notifiche, a condizione che venga concesso anche il consenso dell'utente per questi tipi di dati.
Quando un utente concede il consenso all'applicazione per gli ambiti che corrispondono ai tipi di dati con una policy AUTOMATIC, l'API Google Health monitora e invia automaticamente notifiche per i tipi di dati risultanti dall'intersezione tra i tipi di dati per i quali l'utente ha dato il consenso e i tipi di dati di configurazione dell'iscrizione automatica per quell'utente. Le notifiche vengono quindi inviate al tuo endpoint ogni volta che l'utente genera nuovi dati per questi tipi. Questo vale per gli utenti che concedono il consenso prima o dopo la creazione del sottoscrittore. Le notifiche non vengono sottoposte a backfill per i dati generati prima della creazione del sottoscrittore.
Se un utente revoca il consenso, le notifiche per i tipi di dati corrispondenti verranno interrotte. Le sottoscrizioni automatiche sono gestite da Google e non possono essere elencate o eliminate singolarmente; vengono rimosse solo quando viene eliminato il sottoscrittore principale.
Abbonamenti manuali
Se il tuo sottoscrittore è configurato con un subscription_create_policy MANUAL
per tipi di dati specifici, devi creare e gestire esplicitamente le sottoscrizioni
per ogni utente. Una sottoscrizione collega un utente specifico al tuo endpoint
di sottoscrizione per un insieme definito di tipi di dati. Gli sviluppatori possono utilizzare API specifiche per:
- Crea abbonamenti (manuali) per
healthUserId: crea un nuovo abbonamento per un utente specifico. Questo metodo richiede che l'abbonato abbia unSubscriptionCreatePolicyimpostato suMANUALper i tipi di dati richiesti. - Aggiorna l'abbonamento (manuale): aggiorna i tipi di dati per un abbonamento utente esistente.
- Elimina abbonamento (manuale): elimina un abbonamento utente specifico. Una volta eliminato, l'endpoint dell'abbonato non riceverà più notifiche per questo utente per i tipi di dati associati.
- Elenca gli abbonamenti (manuali): elenca tutti gli abbonamenti attivi per un determinato abbonato. Puoi filtrare i risultati per utente o tipo di dati.
Notifiche
Quando i dati di un utente cambiano per un tipo di dati sottoscritto, l'API Google Health invia una richiesta POST HTTPS all'URL dell'endpoint del sottoscrittore.
Formato delle notifiche
Il payload della notifica contiene dettagli sulla modifica dei dati, tra cui l'ID utente, il tipo di dati e gli intervalli di tempo, che puoi utilizzare per eseguire query sui dati aggiornati. Quando si verificano più aggiornamenti o gli aggiornamenti vengono aggregati, le notifiche vengono raggruppate e inviate come array JSON di oggetti di notifica.
Quando viene attivata una notifica webhook, l'API Google Health invia una richiesta POST HTTPS all'endpoint del sottoscrittore nel seguente formato:
Upsert (dati di serie temporali)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-03-08T01:29:00Z",
"endTime": "2026-03-08T01:34:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 29
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 34
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-03-07T17:29:00",
"endTime": "2026-03-07T17:34:00"
}
}
]
}
}Elimina (dati delle serie temporali)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
[
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id",
"operation": "DELETE",
"dataType": "steps",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-08-12T12:00:00Z",
"endTime": "2026-08-12T12:30:00Z"
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-12T12:00:00",
"endTime": "2026-08-12T12:30:00"
}
}
]
}
}
]Elimina (dati identificabili)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id",
"operation": "DELETE",
"dataType": "sleep",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-08-12T22:00:00Z",
"endTime": "2026-08-13T06:00:00Z"
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-12T22:00:00",
"endTime": "2026-08-13T06:00:00"
}
}
],
"recordId": "9875412"
}
}In batch (più utenti e tipi di dati)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
[
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-project-number-subscriber-id-WEBHOOK_DATA_TYPE_STEPS",
"healthUserId": "health-user-id-1",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-03-08T01:29:00Z",
"endTime": "2026-03-08T01:34:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 29
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 34
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-03-07T17:29:00",
"endTime": "2026-03-07T17:34:00"
}
}
]
}
},
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-project-number-subscriber-id-WEBHOOK_DATA_TYPE_WEIGHT",
"healthUserId": "health-user-id-1",
"operation": "UPSERT",
"dataType": "weight",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-03-07T15:14:00Z",
"endTime": "2026-03-07T15:16:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 7,
"minutes": 14
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 7,
"minutes": 16
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-03-07T07:14:00",
"endTime": "2026-03-07T07:16:00"
}
}
]
}
},
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-project-number-subscriber-id-WEBHOOK_DATA_TYPE_STEPS",
"healthUserId": "health-user-id-2",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 20
},
"time": {
"hours": 0,
"minutes": 0
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 21
},
"time": {
"hours": 0,
"minutes": 0
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-20T00:00:00",
"endTime": "2026-08-21T00:00:00"
}
}
]
}
},
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id-2",
"operation": "UPSERT",
"dataType": "sleep",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-08-12T22:00:00Z",
"endTime": "2026-08-13T06:00:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 12
},
"time": {
"hours": 22,
"minutes": 0
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 13
},
"time": {
"hours": 6,
"minutes": 0
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-12T22:00:00",
"endTime": "2026-08-13T06:00:00"
}
}
],
"recordId": "9875412"
}
}
]Il campo operation indica il tipo di modifica che ha attivato la notifica:
UPSERT: inviato per qualsiasi aggiunta o modifica di dati.DELETE: inviato quando un utente elimina esplicitamente i dati (ad esempio eliminandodataPointsutilizzando l'app Fitbit o eseguendo un'eliminazione collettiva utilizzando l'API). Le notifiche webhook non vengono inviate per gli eventi di eliminazione a livello di sistema, ad esempio quando un utente revoca le autorizzazioni dell'applicazione o elimina il proprio account Fitbit.
Ti consigliamo di rendere idempotente la logica di gestione delle notifiche, in particolare per le operazioni UPSERT, poiché i nuovi tentativi possono causare l'invio di notifiche duplicate.
Il campo clientProvidedSubscriptionName è un identificatore univoco:
- Per gli abbonamenti con una policy
MANUAL, questo campo contiene il nome dell'abbonamento permanente fornito dallo sviluppatore, specificato al momento della creazione dell'abbonamento. In questo modo, viene fornito un ID stabile per la gestione degli abbonamenti manuali. - Per le sottoscrizioni create con una policy
AUTOMATIC, l'API Google Health genera e assegna automaticamente un identificatore nel formato:auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN>, dove:<project-number>è il numero di progetto Google Cloud dello sviluppatore (ad esempio,362978535920).<subscriber-id>è l'ID di registrazione / nome del sottoscrittore (ad esempio,google-health-sync).<WEBHOOK_DATA_TYPE_TOKEN>è il token che rappresenta il tipo di dati specifico. Consulta il vocabolario dei token dei tipi di dati dei webhook per tutti i token possibili.
L'inclusione di clientProvidedSubscriptionName per le policy manuali e automatiche garantisce un formato del payload di notifica coerente per tutti i tipi di abbonamento.
Mappatura degli ID utente
healthUserId è un identificatore dell'API Google Health per l'utente i cui dati sono stati modificati. Se la tua applicazione supporta più utenti, puoi ricevere notifiche per qualsiasi utente che ha concesso il consenso alla tua applicazione. Quando ricevi una notifica, utilizza healthUserId per identificare l'utente i cui dati sono stati modificati, in modo da poter utilizzare le sue credenziali OAuth per eseguire query sui suoi dati.
Per mappare le credenziali OAuth di un utente al suo healthUserId, utilizza l'endpoint getIdentity. Chiama questo endpoint con le credenziali di un utente durante l'onboarding per recuperare il suo healthUserId e memorizza questa mappatura. Questa mappatura non cambia nel tempo, quindi può essere memorizzata nella cache a tempo indeterminato. Per un esempio, consulta Recupera l'ID utente. In questo modo puoi selezionare le credenziali utente corrette quando esegui query sui dati in base al healthUserId in una notifica.
Intervalli di tempo
L'array intervals contiene uno o più oggetti che rappresentano gli intervalli di tempo per i dati modificati:
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
physicalTimeInterval |
object |
No | L'intervallo di tempo fisico UTC dei dati modificati (formato YYYY-MM-DDTHH:MM:SSZ). Compilato per le metriche infragiornaliere; omesso per i riepiloghi giornalieri e le attività di un'intera giornata. |
civilDateTimeInterval |
object |
Sì | La data e l'intervallo di tempo del calendario civile (locale) dei dati modificati, senza offset UTC o del fuso orario. |
civilIso8601TimeInterval |
object |
Sì | L'intervallo di tempo civile (locale) rappresentato come stringa ISO 8601 (formato YYYY-MM-DDTHH:MM:SS) senza offset UTC o di fuso orario. |
Per le attività di un'intera giornata e gli aggregati giornalieri, il campo physicalTimeInterval viene intenzionalmente omesso dal payload della notifica. Questo perché gli eventi di un'intera giornata vengono raggruppati in base alla data civile nominale e le durate fisiche possono essere imprecise quando si attraversano i fusi orari. Quando elabori eventi di un'intera giornata, come best practice devi fare affidamento sui campi civilDateTimeInterval o civilIso8601TimeInterval. Per informazioni dettagliate su come eseguire query su questi punti dati utilizzando i filtri per l'ora civile, consulta Filtra i dati in base all'ora di inizio civile di un intervallo.
Di seguito è riportato un esempio di payload di notifica per un'attività di un'intera giornata in cui il campo physicalTimeInterval viene omesso:
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-503461148219-lindasubscriber-WEBHOOK_DATA_TYPE_STEPS",
"healthUserId": "343864966078399341",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 7,
"day": 14
},
"time": {}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 7,
"day": 15
},
"time": {}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-07-14T00:00:00",
"endTime": "2026-07-15T00:00:00"
}
}
]
}
}Vocabolario dei token dei tipi di dati webhook
Quando utilizzi una policy AUTOMATIC, il clientProvidedSubscriptionName generato contiene uno dei seguenti token di suffisso WEBHOOK_DATA_TYPE_* con lettere maiuscole, a seconda del tipo di dati:
| Token del tipo di dati del webhook | Tipo di dati corrispondente |
|---|---|
WEBHOOK_DATA_TYPE_ACTIVE_MINUTES |
active-minutes |
WEBHOOK_DATA_TYPE_ACTIVE_ZONE_MINUTES |
active-zone-minutes |
WEBHOOK_DATA_TYPE_ACTIVITY_LEVEL |
activity-level |
WEBHOOK_DATA_TYPE_ALTITUDE |
altitude |
WEBHOOK_DATA_TYPE_BLOOD_GLUCOSE |
blood-glucose |
WEBHOOK_DATA_TYPE_BODY_FAT |
body-fat |
WEBHOOK_DATA_TYPE_CALORIES_IN_HEART_RATE_ZONE |
calories-in-heart-rate-zone |
WEBHOOK_DATA_TYPE_DAILY_HEART_RATE_VARIABILITY |
daily-heart-rate-variability |
WEBHOOK_DATA_TYPE_DAILY_HEART_RATE_ZONES |
daily-heart-rate-zones |
WEBHOOK_DATA_TYPE_DAILY_OXYGEN_SATURATION |
daily-oxygen-saturation |
WEBHOOK_DATA_TYPE_DAILY_RESPIRATORY_RATE |
daily-respiratory-rate |
WEBHOOK_DATA_TYPE_DAILY_RESTING_HEART_RATE |
daily-resting-heart-rate |
WEBHOOK_DATA_TYPE_DAILY_SLEEP_TEMPERATURE_DERIVATIONS |
daily-sleep-temperature-derivations |
WEBHOOK_DATA_TYPE_DISTANCE |
distance |
WEBHOOK_DATA_TYPE_EXERCISE |
exercise |
WEBHOOK_DATA_TYPE_FLOORS |
floors |
WEBHOOK_DATA_TYPE_HEART_RATE |
heart-rate |
WEBHOOK_DATA_TYPE_HEART_RATE_VARIABILITY |
heart-rate-variability |
WEBHOOK_DATA_TYPE_HEIGHT |
height |
WEBHOOK_DATA_TYPE_HYDRATION_LOG |
hydration-log |
WEBHOOK_DATA_TYPE_NUTRITION_LOG |
nutrition-log |
WEBHOOK_DATA_TYPE_RESPIRATORY_RATE_SLEEP_SUMMARY |
respiratory-rate-sleep-summary |
WEBHOOK_DATA_TYPE_RUN_VO2_MAX |
run-vo2-max |
WEBHOOK_DATA_TYPE_SEDENTARY_PERIOD |
sedentary-period |
WEBHOOK_DATA_TYPE_SLEEP |
sleep |
WEBHOOK_DATA_TYPE_STEPS |
steps |
WEBHOOK_DATA_TYPE_TIME_IN_HEART_RATE_ZONE |
time-in-heart-rate-zone |
WEBHOOK_DATA_TYPE_WEIGHT |
weight |
WEBHOOK_DATA_TYPE_USER_DELETED |
user-deleted (evento di sistema) |
WEBHOOK_DATA_TYPE_USER_REVOKED_ACCESS |
user-revoked-access (evento di sistema) |
Aggregazione degli aggiornamenti
Per ridurre al minimo il traffico di rete, l'API Google Health raggruppa i messaggi. Il sistema è configurato per inviare un massimo di 99 messaggi per batch, che vengono inviati automaticamente non appena diventano disponibili.
Quando i messaggi vengono raggruppati in batch, il payload viene inviato come array JSON contenente più oggetti di notifica. Un singolo batch può includere aggiornamenti per più utenti in più tipi di dati sottoscritti. Per un payload di esempio, consulta l'esempio Batch (più utenti e tipi di dati) nella sezione Formato di notifica.
Rispondere a una notifica
Il tuo server deve convalidare l'intestazione Authorization, gestire l'handshake sincrono della verifica dell'endpoint e rispondere immediatamente alle notifiche di modifica dei dati con un codice di stato HTTP 204 No Content. Per evitare timeout, elabora il payload della notifica in modo asincrono dopo aver inviato la risposta. Se l'API Google Health riceve un altro codice di stato o se la richiesta scade, riprova a inviare la notifica in un secondo momento.
Esempio di Node.js (Express):
app.post('/webhook-receiver', (req, res) => {
if (req.headers.authorization !== EXPECTED_SECRET) {
return res.status(401).send();
}
if (req.body && !Array.isArray(req.body) && req.body.type === 'verification') {
return res.status(200).send();
}
// 1. Immediately acknowledge the notification batch
res.status(204).send();
// 2. Normalize single-object or batched array notifications and reconcile asynchronously
const notifications = Array.isArray(req.body) ? req.body : [req.body];
setImmediate(async () => {
for (const notification of notifications) {
const { healthUserId, dataType, intervals = [] } = notification.data || {};
console.log(`Update for user ${healthUserId} of type ${dataType}`);
// Query /v4/users/${healthUserId}/dataTypes/${dataType}/dataPoints:reconcile?filter=...
// for each interval in `intervals` (using >= and < comparators; for sleep, filter on
// sleep.interval.end_time or sleep.interval.civil_end_time).
}
});
});
Per recuperare i dati aggiornati per ogni intervallo notificato, chiama l'endpoint
reconcile
con un filtro per l'intervallo di tempo. In questo modo,
gli intervalli sovrapposti dei dispositivi connessi vengono deduplicati automaticamente. Per
maggiori dettagli ed esempi, consulta
Visualizza i dati sugli intervalli riconciliati.
Esistono tre strategie comuni per aggiornare il database dopo aver ricevuto una notifica di modifica dei dati:
- Coda (consigliata): inserisci le notifiche in arrivo in una coda di messaggi ed elaborale dalla coda in base alle risorse di sistema.
- Recupero immediato: indica al tuo backend di chiamare immediatamente l'API Google Health per recuperare i dati aggiornati e caricarli nel tuo database.
- Aggiornamenti lazy: contrassegna i record del database locale dell'utente come dirty e recupera i dati aggiornati dall'API Google Health solo alla successiva visita dell'utente alla tua applicazione.
La strategia di accodamento ti consente di elaborare i dati quasi in tempo reale, pur rimanendo resiliente a un'ondata di notifiche (ad esempio, se un'interruzione del servizio causa la consegna di un backlog di notifiche tutte insieme). Sebbene tu possa scegliere di elaborare queste code in base a una pianificazione batch a intervalli lunghi (ad esempio, ogni 3 ore) per risparmiare risorse, lo svantaggio è che gli utenti in genere si aspettano di vedere i propri dati aggiornati quasi in tempo reale.
Best practice
Segui queste best practice per garantire una gestione affidabile ed efficiente degli aggiornamenti dei dati:
- Utilizza l'API Subscription: utilizza l'API Subscription per ricevere una notifica quando un utente ha nuovi dati da scaricare anziché eseguire il polling degli endpoint.
- Metti in coda ed elabora le notifiche separatamente: quando ricevi le notifiche relative agli abbonamenti, inseriscile in una coda ed elaborale separatamente in base alle risorse del tuo sistema. Mantieni l'endpoint webhook leggero confermando immediatamente la richiesta.
Verifica della firma
Per garantire l'autenticità delle notifiche dei webhook, il payload JSON non elaborato di ogni notifica webhook in uscita viene firmato con una chiave privata utilizzando PublicKeySign di Tink, fornendo la firma con codifica Base64 nell'intestazione HTTP GOOGLE-HEALTH-API-SIGNATURE della richiesta. Queste chiavi di firma vengono ruotate automaticamente ogni 30 giorni e il set di chiavi pubbliche ufficiale corrispondente viene distribuito come file JSON all'URL permanente https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
Come verificare la firma
Utilizzo di Tink (consigliato): gli sviluppatori possono verificare la firma utilizzando la primitiva
PublicKeyVerify
di Tink. Recupera il set di chiavi pubbliche dall'URL permanente, crea un'istanza della primitiva PublicKeyVerify con il set di chiavi e verifica l'intestazione GOOGLE-HEALTH-API-SIGNATURE decodificata rispetto al payload JSON del webhook non elaborato.
Verifica manuale (senza Tink): se gli sviluppatori scelgono di non utilizzare Tink, possono verificare manualmente la firma seguendo questi passaggi:
- Decodifica in Base64 l'intestazione
GOOGLE-HEALTH-API-SIGNATUREper separare il prefisso Tink di 5 byte (che contiene un prefisso di versione di 1 byte e un keyId di 4 byte) dalla firma effettiva con codifica DER. - Recupera il set di chiavi JSON da https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
- Individua la chiave corrispondente al keyId analizzato e decodifica in Base64 il relativo campo del valore, che contiene un buffer di protocollo EcdsaPublicKey serializzato.
- Estrai le coordinate x e y big-endian (tag Protobuf 3 e 4) da questo payload binario.
- Crea un'istanza di una chiave pubblica ECDSA P-256 standard in una libreria di crittografia integrata utilizzando le coordinate x e y estratte.
- Verifica il payload JSON del webhook non elaborato rispetto alla firma DER estratta utilizzando l'algoritmo SHA-256.
Stato e recupero degli abbonati
Se l'endpoint del sottoscrittore non è disponibile o restituisce un codice di stato di errore (diverso da 204), l'API Google Health archivia le notifiche in attesa per un massimo di 7 giorni e riprova l'invio con backoff esponenziale.
Una volta che l'endpoint torna online e risponde con 204, l'API
recapita automaticamente l'arretrato di messaggi archiviati. Le notifiche più vecchie di 7 giorni vengono eliminate e non possono essere recuperate.
Errori comuni
| Codice di errore | Messaggio | Descrizione | Suggerimento |
|---|---|---|---|
| 400 Bad Request | Numero di progetto non valido nel nome della risorsa | Quando elimini o aggiorni un sottoscrittore utilizzando l'ID progetto Google Cloud nell'URL della richiesta anziché il numero di progetto. Questo vale per le iscrizioni ai webhook che utilizzano l'endpoint projects.subscribers. |
Utilizza il numero del progetto Google Cloud nell'URL della richiesta, non l'ID progetto. |
| 403 Forbidden | Il chiamante non dispone dell'autorizzazione | Quando crei o elenchi i sottoscrittori utilizzando l'ID progetto Google Cloud nell'URL della richiesta anziché il numero di progetto. Questo vale per le iscrizioni ai webhook che utilizzano l'endpoint projects.subscribers. |
Utilizza il numero del progetto Google Cloud nell'URL della richiesta, non l'ID progetto. |