Interfejs Google Health API umożliwia aplikacji otrzymywanie powiadomień w czasie rzeczywistym, gdy zmieniają się dane dotyczące zdrowia użytkownika. Zamiast odpytywać o zmiany, Twój serwer otrzymuje żądanie HTTPS POST webhook, gdy tylko dane są dostępne w interfejsie Google Health API.
Obsługiwane typy danych
Powiadomienia webhook są obsługiwane w przypadku tych typów danych:
| Typ danych | Dostępne operacje |
Zakres |
|---|---|---|
|
Aktywne minuty
dataType:
active-minutesfilter parameter: active_minutes
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
Zgodne urządzenia
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Aktywne minuty w strefie
dataType:
active-zone-minutesfilter parameter: active_zone_minutes
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
Zgodne urządzenia
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Poziom aktywności
|
lista, uzgadnianie | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Altitude
dataType:
altitudefilter parameter: altitude
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Glukoza we krwi
dataType:
blood-glucosefilter parameter: blood_glucose
Typ rekordu: Sample
|
list, get, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Tkanka tłuszczowa
dataType:
body-fatfilter parameter: body_fat
Typ rekordu: Sample
Zgodne urządzenia
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Kalorie w strefie tętna
dataType:
calories-in-heart-rate-zonefilter parameter: calories_in_heart_rate_zone
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
|
rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Dzienna zmienność rytmu serca
dataType:
daily-heart-rate-variabilityfilter parameter: daily_heart_rate_variability
Typ rekordu: dzienny
Zgodne urządzenia
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Dzienne strefy tętna
dataType:
daily-heart-rate-zonesfilter parameter: daily_heart_rate_zones
Typ rekordu: dzienny
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Dzienne nasycenie tlenem
dataType:
daily-oxygen-saturationfilter parameter: daily_oxygen_saturation
Typ rekordu: dzienny
Zgodne urządzenia
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Dzienna częstość oddychania
dataType:
daily-respiratory-ratefilter parameter: daily_respiratory_rate
Typ rekordu: dzienny
Zgodne urządzenia
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Dzienne tętno spoczynkowe
dataType:
daily-resting-heart-ratefilter parameter: daily_resting_heart_rate
Typ rekordu: dzienny
Zgodne urządzenia
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Odchylenia temperatury podczas snu w ciągu dnia
dataType:
daily-sleep-temperature-derivationsfilter parameter: daily_sleep_temperature_derivations
Typ rekordu: dzienny
Obsługa webhooków
Przewodnik po funkcjach związanych ze zdrowiem
Przewodnik po funkcjach związanych ze snem
Przewodnik po funkcjach związanych z temperaturą skóry
Zgodne urządzenia
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Dystans
dataType:
distancefilter parameter: distance
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
Zgodne urządzenia
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Ćwiczenia
dataType:
exercisefilter parameter: exercise
Typ rekordu: sesja
Zgodne urządzenia
|
list, get, reconcile, create, update, batchDelete | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Piętra
dataType:
floorsfilter parameter: floors
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
|
reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Tętno
dataType:
heart-ratefilter parameter: heart_rate
Typ rekordu: Sample
Rozdzielczość pamięci: 1 sekunda
Zgodne urządzenia
|
list, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Zmienność rytmu serca
dataType:
heart-rate-variabilityfilter parameter: heart_rate_variability
Typ rekordu: Sample
Zgodne urządzenia
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Wysokość
|
list, get, reconcile, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Zapis nawodnienia
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Dziennik odżywiania
dataType:
nutrition-logfilter parameter: nutrition_log
Typ rekordu: sesja
Zgodne urządzenia
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Podsumowanie dotyczące częstości oddechów podczas snu
dataType:
respiratory-rate-sleep-summaryfilter parameter: respiratory_rate_sleep_summary
Typ rekordu: Sample
Zgodne urządzenia
|
lista, uzgadnianie | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Pułap tlenowy podczas biegu
dataType:
run-vo2-maxfilter parameter: run_vo2_max
Typ rekordu: Sample
Zgodne urządzenia
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Okres braku aktywności
dataType:
sedentary-periodfilter parameter: sedentary_period
Typ rekordu: Interwał
Zgodne urządzenia
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Sen
dataType:
sleepfilter parameter: sleep
Typ rekordu: sesja
Zgodne urządzenia
|
list, get, reconcile, create, update, batchDelete | .sleep.readonly.sleep.writeonly |
|
Kroki
dataType:
stepsfilter parameter: steps
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
Zgodne urządzenia
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Czas w strefie tętna
dataType:
time-in-heart-rate-zonefilter parameter: time_in_heart_rate_zone
Typ rekordu: Interwał
Rozdzielczość pamięci masowej: 1 minuta
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Waga
dataType:
weightfilter parameter: weight
Typ rekordu: Sample
Zgodne urządzenia
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
Zakresy powiadomień
Powiadomienia dotyczące tych typów danych są wysyłane tylko wtedy, gdy użytkownik wyraził zgodę na jeden z odpowiednich zakresów:
- Aktywność, która obejmuje aktywne minuty, kroki, wysokość, dystans i piętra:
https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonlyhttps://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly
- Wskaźniki zdrowotne, które obejmują typ danych dotyczących wagi:
https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonlyhttps://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly
- Sen, który obejmuje typ danych o śnie:
https://www.googleapis.com/auth/googlehealth.sleep.readonlyhttps://www.googleapis.com/auth/googlehealth.sleep.writeonly
Konta usługi używane przez Uprawnienia
Podczas konfigurowania subskrybentów interfejsu Google Health API zalecamy używanie konta usługi uprawnień, choć nie jest to wymagane. Konta usługi zapewniają lepsze bezpieczeństwo zadań aplikacji w porównaniu ze standardowymi kontami użytkowników dzięki tym funkcjom:
- Zautomatyzowane dane logowania o krótkim okresie ważności: gdy konta usługi są dołączone do środowiska wykonawczego Google Cloud (takiego jak Compute Engine, Cloud Run lub Google Kubernetes Engine), automatycznie uzyskują i rotują bezpieczne dane logowania o krótkim okresie ważności. Eliminuje to ryzyko związane z zarządzaniem trwałymi kluczami statycznymi i ich przechowywaniem.
- Zasada jak najmniejszych uprawnień: konta usługi zapewniają dedykowane tożsamości dla zadań. Możesz przyznać im tylko określone uprawnienia potrzebne do zarządzania punktami końcowymi subskrybentów, unikając szerszego dostępu do zasobów Google Cloud.
- Niezależność cyklu życia: konta usługi działają niezależnie od kont poszczególnych użytkowników, dzięki czemu zmiany personelu nie wpływają na długoterminową stabilność uwierzytelniania.
skonfigurować konto usługi,
Aby skonfigurować aplikację subskrybenta do uwierzytelniania za pomocą konta usługi:
- Utwórz konto usługi: w konsoli Google Cloud otwórz stronę Administracja w projekcie, aby utworzyć nowe konto usługi zarządzane przez użytkownika.
- Przyznaj niezbędne role Uprawnień: przypisz do konta usługi odpowiednie role wymagane do zarządzania subskrybentami w projekcie w chmurze Google.
- Dołącz konto usługi do zadania: skonfiguruj środowisko, w którym działa logika subskrybenta, tak aby działało jako nowe konto usługi.
Dzięki temu kod aplikacji (np. biblioteki klienta interfejsu API Google) może automatycznie wykrywać i używać krótkotrwałych danych logowania konta usługi podczas wywoływania interfejsu API REST
projects.subscribers.
Role CPE
Aby zarządzać subskrybentami lub subskrypcjami interfejsu Google Health API, musisz przyznać odpowiednią rolę kontu usługi, które jest używane do wywoływania interfejsu API. W zależności od wymaganego poziomu dostępu przypisz jedną z tych ról:
- Odczyt interfejsu Google Health API
- Edytujący interfejs Google Health API
- Administrator interfejsu Google Health API
Więcej informacji o rolach i uprawnieniach w Google Health API
Zarządzanie subskrybentami
Zanim zaczniesz otrzymywać powiadomienia, musisz zarejestrować subskrybenta, który reprezentuje punkt końcowy powiadomień aplikacji. Subskrybentami możesz zarządzać za pomocą interfejsu API typu REST dostępnego na stronie projects.subscribers.
Punkt końcowy subskrybenta musi używać protokołu HTTPS (TLSv1.2+) i być publicznie dostępny.
Podczas tworzenia i aktualizowania subskrybentów interfejs Google Health API przeprowadza weryfikację, aby upewnić się, że jesteś właścicielem identyfikatora URI punktu końcowego. Jeśli weryfikacja się nie powiedzie, operacje tworzenia i aktualizowania subskrybenta zakończą się błędem FailedPreconditionException.
Tworzenie subskrybenta
Aby zarejestrować nowego subskrybenta w projekcie, użyj punktu końcowego create. Musisz podać te dane:
project-id: numer projektu, w którym utworzono konto usługi webhooka.subscriberId: unikalny identyfikator subskrybenta, który podajesz. Ten identyfikator musi mieć od 4 do 36 znaków i pasować do wyrażenia regularnego ([a-z]([a-z0-9-]{2,34}[a-z0-9])).endpointUri: docelowy adres URL powiadomień webhooka.subscriberConfigs: typy danych, o których chcesz otrzymywać powiadomienia, oraz zasady dotyczące subskrypcji dla każdego z nich.endpointAuthorization: mechanizm autoryzacji punktu końcowego. Musi on zawierać podany przez Ciebie elementsecret. Wartośćsecretjest wysyłana w nagłówkuAuthorizationz każdą wiadomością z powiadomieniem. Możesz użyć tego tokena, aby sprawdzić, czy żądania przychodzące pochodzą z interfejsu Google Health API. Możesz na przykład ustawićsecretnaBearer R4nd0m5tr1ng123w przypadku uwierzytelniania przy użyciu tokena okaziciela lub naBasic dXNlcjpwYXNzd29yZA==w przypadku uwierzytelniania podstawowego.
W subscriberConfigs musisz ustawić subscriptionCreatePolicy dla każdego typu danych. Ustaw wartość AUTOMATIC, aby używać automatycznych subskrypcji, lub MANUAL, jeśli chcesz samodzielnie zarządzać subskrypcjami użytkowników. Więcej informacji o każdej z tych opcji znajdziesz w artykułach o subskrypcjach automatycznych i subskrypcjach ręcznych.
Żądanie
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"
}
}Odpowiedź
{
"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"
}
]
}Subskrybenci listy
Użyj punktu końcowego list, aby pobrać wszystkich subskrybentów zarejestrowanych w Twoim projekcie.
Żądanie
GET https://health.googleapis.com/v4/projects/project-id/subscribers
Odpowiedź
{
"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
}Aktualizowanie subskrybenta
Aby zaktualizować subskrybenta w projekcie, użyj punktu końcowego patch. Pola, które można zaktualizować, to endpointUri, subscriberConfigs i endpointAuthorization.
Pola aktualizuje się, podając parametr zapytania updateMask i treść żądania. Parametr updateMask musi zawierać rozdzieloną przecinkami listę nazw pól, które chcesz zaktualizować, przy czym nazwy pól muszą być zapisane w formacie camelCase (np. endpointUri). Treść żądania musi zawierać częściowy obiekt Subscriber z nowymi wartościami pól, które chcesz zaktualizować. Aktualizowane są tylko pola określone w updateMask. Jeśli w treści żądania podasz pola, których nie ma w updateMask, zostaną one zignorowane.
Jeśli zaktualizujesz endpointUri lub endpointAuthorization, zostanie przeprowadzona weryfikacja punktów końcowych. Więcej informacji znajdziesz w artykule Weryfikacja punktów końcowych.
Pamiętaj, że aktualizacja subscriberConfigs to pełne zastąpienie, a nie scalenie. Jeśli parametr subscriberConfigs jest uwzględniony w parametrze updateMask, wszystkie zapisane konfiguracje tego subskrybenta zostaną zastąpione listą podaną w treści żądania. Aby dodać lub usunąć konfigurację, musisz podać pełny zestaw konfiguracji. Jeśli aktualizujesz inne pola i chcesz zachować bieżące konfiguracje, pomiń subscriberConfigs w updateMask.
Żądanie
PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id?updateMask=endpointUri
{
"endpointUri": "https://myapp.com/new-webhooks/health"
}Odpowiedź
{
"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"
}
]
}Usuwanie subskrybenta
Aby usunąć subskrybenta z projektu, użyj punktu końcowego delete. Po usunięciu subskrybent nie będzie już otrzymywać powiadomień.
Żądanie
DELETE https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id
Odpowiedź
Jeśli usunięcie się powiedzie, zwracana jest pusta treść odpowiedzi ze stanem HTTP `200 OK`.{}Weryfikacja punktów końcowych
Aby zapewnić bezpieczeństwo i niezawodność dostarczania powiadomień, interfejs Google Health API przeprowadza obowiązkową weryfikację dwuetapową za każdym razem, gdy tworzysz subskrybenta lub aktualizujesz konfigurację jego punktu końcowego (endpointUri lub endpointAuthorization). Ten proces jest wykonywany synchronicznie podczas wywołania interfejsu API. Usługa wysyła 2 automatyczne żądania POST do identyfikatora URI punktu końcowego, używając klienta użytkownika Google-Health-API-Webhooks z treścią JSON {"type": "verification"}.
- Autoryzowane powitanie: pierwsze żądanie jest wysyłane ze skonfigurowanym nagłówkiem
Authorization. Serwer musi odpowiedzieć stanem200 OKlub201 Created. - Unauthorized Challenge: drugie żądanie jest wysyłane bez danych logowania.
Serwer musi odpowiadać stanem
401 Unauthorizedlub403 Forbidden.
To uzgadnianie potwierdza, że punkt końcowy jest aktywny i prawidłowo egzekwuje zabezpieczenia. Jeśli którykolwiek z tych kroków się nie powiedzie, żądanie do interfejsu API zakończy się błędem FAILED_PRECONDITION. Dopiero po pomyślnym zakończeniu tego procesu subskrybent zostanie zapisany i aktywowany, aby otrzymywać powiadomienia o danych dotyczących zdrowia.
Rotacja kluczy
Jeśli chcesz dokonać rotacji kluczy dla endpointAuthorization, wykonaj te czynności:
- Skonfiguruj punkt końcowy tak, aby akceptował zarówno stare, jak i nowe wartości
endpointAuthorization. - Zaktualizuj konfigurację subskrybenta, podając nową wartość
endpointAuthorization, za pomocą żądaniapatchz?updateMask=endpointAuthorization. - Po potwierdzeniu, że krok 2 został wykonany pomyślnie, skonfiguruj punkt końcowy tak, aby akceptował tylko nową wartość
endpointAuthorization.
Subskrypcje użytkownika
Interfejs Google Health API pomaga skutecznie zarządzać subskrypcjami użytkowników, dzięki czemu nie trzeba ich rejestrować ręcznie podczas wprowadzania.
Subskrypcje automatyczne
Zalecamy korzystanie z automatycznych subskrypcji. Aby włączyć tę funkcję, ustaw subscriptionCreatePolicy na AUTOMATIC w subscriberConfigs dla określonych typów danych. Typy danych dataTypes określone w zasadach AUTOMATIC są takie same jak typy danych, dla których interfejs Google Health API wysyła powiadomienia, pod warunkiem że użytkownik wyraził zgodę na te typy danych.
Gdy użytkownik wyrazi zgodę na dostęp aplikacji do zakresów odpowiadających typom danych z zasadą AUTOMATIC, interfejs Google Health API automatycznie śledzi i wysyła powiadomienia dotyczące typów danych, które wynikają z przecięcia typów danych, na które użytkownik wyraził zgodę, i typów danych automatycznej konfiguracji subskrybenta dla tego użytkownika. Powiadomienia są wysyłane do Twojego punktu końcowego, gdy użytkownik wygeneruje nowe dane tych typów. Działa to w przypadku użytkowników, którzy wyrażą zgodę przed utworzeniem subskrybenta lub po jego utworzeniu. Powiadomienia nie są uzupełniane w przypadku danych wygenerowanych przed utworzeniem subskrybenta.
Jeśli użytkownik wycofa zgodę, powiadomienia dotyczące odpowiednich typów danych zostaną zatrzymane. Subskrypcjami automatycznymi zarządza Google i nie można ich wyświetlać ani usuwać pojedynczo. Są usuwane tylko wtedy, gdy zostanie usunięty nadrzędny subskrybent.
Subskrypcje ręczne
Jeśli subskrybent jest skonfigurowany z trybem MANUAL subscription_create_policy w przypadku określonych typów danych, musisz jawnie utworzyć subskrypcje dla każdego użytkownika i nimi zarządzać. Subskrypcja łączy konkretnego użytkownika z punktem końcowym subskrybenta w przypadku określonego zestawu typów danych. Deweloperzy mogą używać określonych interfejsów API, aby:
- Tworzenie (ręczne) subskrypcji na
healthUserId– tworzy nową subskrypcję dla konkretnego użytkownika. Ta metoda wymaga, aby subskrybent miał ustawioną wartośćSubscriptionCreatePolicynaMANUALdla żądanych typów danych. - Aktualizowanie subskrypcji (ręczne) – aktualizuje typy danych w przypadku istniejącej subskrypcji użytkownika.
- Usuwanie subskrypcji (ręczne) – usuwa określoną subskrypcję użytkownika. Po usunięciu punkt końcowy subskrybenta nie będzie już otrzymywać powiadomień dotyczących tego użytkownika w przypadku powiązanych typów danych.
- Wyświetlanie listy subskrypcji (ręcznie) – wyświetla listę wszystkich aktywnych subskrypcji danego subskrybenta. Wyniki możesz filtrować według użytkownika lub typu danych.
Powiadomienia
Gdy dane użytkownika dotyczące subskrybowanego typu danych ulegną zmianie, interfejs Google Health API wysyła żądanie HTTPS POST na adres URL punktu końcowego subskrybenta.
Format powiadomienia
Ładunek powiadomienia zawiera szczegółowe informacje o zmianie danych, w tym identyfikator użytkownika, typ danych i przedziały czasu, których możesz użyć do wysłania zapytania o zaktualizowane dane. Gdy występuje wiele aktualizacji lub są one agregowane, powiadomienia są grupowane i dostarczane jako tablica JSON obiektów powiadomień.
Gdy zostanie wywołane powiadomienie webhooka, interfejs Google Health API wysyła do punktu końcowego subskrybenta żądanie HTTPS POST w tym formacie:
Upsert (dane dotyczące serii czasowej)
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"
}
}
]
}
}Usuwanie (dane serii czasowej)
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"
}
}
]
}
}
]Usuwanie (dane umożliwiające identyfikację)
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"
}
}Grupowo (wielu użytkowników i typów danych)
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"
}
}
]Pole operation wskazuje typ zmiany, która wywołała powiadomienie:
UPSERT: wysyłane w przypadku dodania lub modyfikacji danych.DELETE: wysyłane, gdy użytkownik wyraźnie usunie dane (np. usuniedataPointsza pomocą aplikacji Fitbit lub wykona usunięcie zbiorcze za pomocą interfejsu API). Powiadomienia webhook nie są wysyłane w przypadku zdarzeń usunięcia na poziomie systemu, takich jak cofnięcie uprawnień aplikacji przez użytkownika lub usunięcie konta Fitbit.
Zalecamy, aby logika obsługi powiadomień była idempotentna, zwłaszcza w przypadku operacji UPSERT, ponieważ ponowne próby mogą spowodować wysłanie zduplikowanych powiadomień.
Pole clientProvidedSubscriptionName to unikalny identyfikator:
- W przypadku subskrypcji z zasadą
MANUALto pole zawiera stałą, podaną przez dewelopera nazwę subskrypcji określoną podczas jej tworzenia. Zapewnia to stabilny identyfikator do zarządzania subskrypcjami ręcznymi. - W przypadku subskrypcji utworzonych za pomocą zasady
AUTOMATICinterfejs Google Health API automatycznie generuje i przypisuje identyfikator w formacie:auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN>, gdzie:<project-number>to numer projektu Google Cloud dewelopera (np.362978535920).<subscriber-id>to identyfikator rejestracji subskrybenta lub nazwa subskrybenta (np.google-health-sync).<WEBHOOK_DATA_TYPE_TOKEN>to token reprezentujący konkretny typ danych. Wszystkie możliwe tokeny znajdziesz w artykule Słownik tokenów typów danych webhooka.
Uwzględnienie parametru clientProvidedSubscriptionName zarówno w przypadku zasad ręcznych, jak i automatycznych zapewnia spójny format danych powiadomień we wszystkich typach subskrypcji.
Mapowanie identyfikatorów użytkowników
healthUserId to identyfikator interfejsu Google Health API użytkownika, którego dane uległy zmianie. Jeśli Twoja aplikacja obsługuje wielu użytkowników, możesz otrzymywać powiadomienia dotyczące każdego użytkownika, który wyraził zgodę na korzystanie z aplikacji. Gdy otrzymasz powiadomienie, użyj healthUserId, aby określić, których użytkowników dane uległy zmianie, i użyć ich danych logowania OAuth do wysłania zapytania o ich dane.
Aby zmapować dane logowania OAuth użytkownika na jego healthUserId, użyj punktu końcowego getIdentity. Wywołaj ten punkt końcowy z danymi logowania użytkownika podczas jego wprowadzania, aby pobrać jego healthUserId i zapisać to mapowanie. To mapowanie nie zmienia się z czasem, więc może być przechowywane w pamięci podręcznej bezterminowo. Przykład znajdziesz w sekcji Pobieranie identyfikatora użytkownika. Dzięki temu możesz wybrać prawidłowe dane logowania użytkownika podczas wysyłania zapytania o dane na podstawie healthUserId w powiadomieniu.
Przedziały czasu
Tablica intervals zawiera co najmniej 1 obiekt reprezentujący przedziały czasu zmodyfikowanych danych:
| Pole | Typ | Wymagane | Opis |
|---|---|---|---|
physicalTimeInterval |
object |
Nie | Przedział czasu UTC zmodyfikowanych danych (format YYYY-MM-DDTHH:MM:SSZ). Wypełniane w przypadku danych uzyskiwanych w ciągu dnia; pomijane w przypadku podsumowań dziennych i aktywności całodniowych. |
civilDateTimeInterval |
object |
Tak | Data i godzina zmodyfikowanych danych według kalendarza cywilnego (lokalnego), bez przesunięć UTC lub strefy czasowej. |
civilIso8601TimeInterval |
object |
Tak | Przedział czasu cywilnego (lokalnego) reprezentowany jako ciąg znaków ISO 8601 (format YYYY-MM-DDTHH:MM:SS) bez przesunięć czasu UTC lub strefy czasowej. |
W przypadku aktywności całodniowych i dziennych wartości zagregowanych pole physicalTimeInterval jest celowo pomijane w ładunku powiadomienia. Wynika to z tego, że wydarzenia całodniowe są grupowane według nominalnej daty cywilnej, a czas trwania może być niedokładny, gdy przekracza strefy czasowe. Podczas przetwarzania wydarzeń całodniowych najlepiej jest korzystać z pól civilDateTimeInterval lub civilIso8601TimeInterval. Szczegółowe informacje o tym, jak wysyłać zapytania o te punkty danych za pomocą filtrów czasu cywilnego, znajdziesz w artykule Filtrowanie danych według czasu rozpoczęcia interwału.
Poniżej znajdziesz przykładowy ładunek powiadomienia o całodniowej aktywności, w którym pominięto pole physicalTimeInterval:
{
"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"
}
}
]
}
}Słownik tokenów typów danych webhooka
Gdy używasz zasady AUTOMATIC, wygenerowany clientProvidedSubscriptionName zawiera jeden z tych tokenów sufiksu WEBHOOK_DATA_TYPE_* pisanych wielkimi literami, w zależności od typu danych:
| Token typu danych webhooka | Odpowiedni typ danych |
|---|---|
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 (zdarzenie systemowe) |
WEBHOOK_DATA_TYPE_USER_REVOKED_ACCESS |
user-revoked-access (zdarzenie systemowe) |
agregacja aktualizacji,
Aby zminimalizować ruch w sieci, interfejs Google Health API grupuje wiadomości. System jest skonfigurowany tak, aby wysyłać maksymalnie 99 wiadomości na grupę, które są automatycznie przekazywane, gdy tylko staną się dostępne.
Gdy wiadomości są grupowane, ładunek jest dostarczany jako tablica JSON zawierająca wiele obiektów powiadomień. Pojedyncza partia może zawierać aktualizacje dotyczące wielu użytkowników i wielu subskrybowanych typów danych. Przykładowy ładunek znajdziesz w sekcji Format powiadomienia w przykładzie Grupa (wielu użytkowników i typów danych).
Podejmowanie działania w związku z powiadomieniem
Serwer musi zweryfikować nagłówek Authorization, obsłużyć synchroniczne potwierdzenie weryfikacji punktu końcowego i natychmiast odpowiedzieć na powiadomienia o zmianie danych za pomocą kodu stanu HTTP 204 No Content. Aby uniknąć przekroczenia limitu czasu, po wysłaniu odpowiedzi przetwórz ładunek powiadomienia asynchronicznie. Jeśli interfejs Google Health API otrzyma inny kod stanu lub upłynie limit czasu żądania, spróbuje ponownie wysłać powiadomienie później.
Przykład w 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).
}
});
});
Aby pobrać zaktualizowane dane dla każdego powiadomionego interwału, wywołaj punkt końcowy reconcile z filtrem dla zakresu czasu interwału. Spowoduje to automatyczne usunięcie duplikatów nakładających się interwałów z połączonych urządzeń. Szczegółowe informacje i przykłady znajdziesz w sekcji Uzyskiwanie uzgodnionego widoku danych interwałowych.
Istnieją 3 typowe strategie aktualizowania bazy danych po otrzymaniu powiadomienia o zmianie danych:
- Kolejkowanie (zalecane): umieszczaj przychodzące powiadomienia w kolejce wiadomości i przetwarzaj je z kolejki na podstawie zasobów systemowych.
- Natychmiastowe pobieranie: skonfiguruj backend tak, aby natychmiast wywoływał interfejs Google Health API w celu pobrania zaktualizowanych danych i zapisania ich w bazie danych.
- Leniwe aktualizacje: oznacz rekordy lokalnej bazy danych użytkownika jako nieaktualne i pobieraj zaktualizowane dane z interfejsu Google Health API tylko wtedy, gdy użytkownik ponownie odwiedzi Twoją aplikację.
Strategia kolejkowania umożliwia przetwarzanie danych w czasie zbliżonym do rzeczywistego, a jednocześnie zapewnia odporność na zalew powiadomień (np. gdy przerwa w działaniu usługi powoduje, że wszystkie zaległe powiadomienia są dostarczane jednocześnie). Możesz przetwarzać te kolejki w długich odstępach czasu (np. co 3 godziny), aby oszczędzać zasoby, ale wadą tego rozwiązania jest to, że użytkownicy zwykle oczekują, że ich dane będą aktualizowane w czasie zbliżonym do rzeczywistego.
Sprawdzone metody
Aby zapewnić niezawodne i efektywne przetwarzanie aktualizacji danych, postępuj zgodnie z tymi sprawdzonymi metodami:
- Używaj interfejsu Subscription API: używaj interfejsu Subscription API, aby otrzymywać powiadomienia, gdy użytkownik ma nowe dane do pobrania, zamiast odpytywać punkty końcowe.
- Kolejkuj i przetwarzaj powiadomienia osobno: gdy otrzymasz powiadomienia o subskrypcji, umieść je w kolejce i przetwarzaj osobno na podstawie zasobów systemu. Punkt końcowy webhooka powinien być lekki, aby natychmiast potwierdzać żądanie.
weryfikowaniem podpisu,
Aby zapewnić autentyczność powiadomień z webhooków, nieprzetworzone dane JSON każdego wychodzącego powiadomienia z webhooka są podpisywane kluczem prywatnym za pomocą PublicKeySign Tink, co zapewnia podpis zakodowany w formacie Base64 w nagłówku HTTP GOOGLE-HEALTH-API-SIGNATURE w żądaniu. Te klucze podpisu są automatycznie rotowane co 30 dni, a odpowiadający im oficjalny zestaw kluczy publicznych jest rozpowszechniany jako plik JSON pod stałym adresem URL https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
Jak zweryfikować podpis
Korzystanie z Tink (zalecane): deweloperzy mogą zweryfikować podpis za pomocą elementu podstawowego PublicKeyVerify w Tink. Pobierz zestaw kluczy publicznych ze stałego adresu URL, utwórz instancję elementu podstawowego PublicKeyVerify z zestawem kluczy i zweryfikuj zdekodowany nagłówek GOOGLE-HEALTH-API-SIGNATURE pod kątem nieprzetworzonego ładunku JSON webhooka.
Weryfikacja ręczna (bez Tink): jeśli deweloperzy nie chcą używać Tink, mogą ręcznie zweryfikować podpis, wykonując te czynności:
- Dekoduje nagłówek
GOOGLE-HEALTH-API-SIGNATUREw formacie base64, aby oddzielić 5-bajtowy prefiks Tink (zawierający 1-bajtowy prefiks wersji i 4-bajtowy identyfikator klucza) od rzeczywistego podpisu zakodowanego w formacie DER. - Pobierz zestaw kluczy JSON z https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
- Znajdź klucz pasujący do przeanalizowanego identyfikatora keyId i zdekoduj w formacie Base64 jego pole wartości, które zawiera zserializowany bufor protokołu EcdsaPublicKey.
- Wyodrębnij z tych danych binarnych współrzędne x i y w formacie big endian (tagi Protobuf 3 i 4).
- Utwórz instancję standardowego klucza publicznego ECDSA P-256 we wbudowanej bibliotece kryptograficznej, używając wyodrębnionych współrzędnych x i y.
- Sprawdź nieprzetworzony ładunek JSON webhooka pod kątem wyodrębnionego podpisu DER za pomocą algorytmu SHA-256.
Stan subskrybenta i przywracanie
Jeśli punkt końcowy subskrybenta stanie się niedostępny lub zwróci kod stanu błędu (inny niż 204), interfejs Google Health API będzie przechowywać oczekujące powiadomienia przez maksymalnie 7 dni i ponawiać próby dostarczenia z wykorzystaniem wzrastającego czasu do ponowienia.
Gdy Twój punkt końcowy znów będzie online i odpowie kodem 204, interfejs API automatycznie dostarczy zaległe wiadomości. Powiadomienia starsze niż 7 dni są odrzucane i nie można ich odzyskać.
Typowe błędy
| Kod błędu | Wiadomość | Opis | Rekomendacja |
|---|---|---|---|
| 400 Nieprawidłowe żądanie | Nieprawidłowy numer projektu w nazwie zasobu | Podczas usuwania lub aktualizowania subskrybenta za pomocą identyfikatora projektu Google Cloud w adresie URL żądania zamiast numeru projektu. Dotyczy to subskrypcji webhooków korzystających z punktu końcowego projects.subscribers. |
W adresie URL żądania użyj numeru projektu w chmurze Google, a nie identyfikatora projektu. |
| 403 Dostęp zabroniony | The caller does not have permission | podczas tworzenia lub wyświetlania listy subskrybentów, gdy w adresie URL żądania używasz identyfikatora projektu Google Cloud zamiast numeru projektu. Dotyczy to subskrypcji webhooków korzystających z punktu końcowego projects.subscribers. |
W adresie URL żądania użyj numeru projektu w chmurze Google, a nie identyfikatora projektu. |