Interfejs Google Health API umożliwia aplikacji otrzymywanie powiadomień, gdy zmieniają się dane użytkownika dotyczące zdrowia. Zamiast sprawdzać, czy nastąpiły zmiany, Twój serwer otrzymuje żądanie HTTPS POST webhook, gdy tylko dane są dostępne w 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, uzgodnij | .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: Próbka
|
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: Próbka
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
Obsługa webhooków
Przewodnik po funkcjach związanych ze zdrowiem
Przewodnik po funkcjach związanych ze snem
Zgodne urządzenia
|
lista, uzgodnij | .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, uzgodnij | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Dzienne nasycenie tlenem
dataType:
daily-oxygen-saturationfilter parameter: daily_oxygen_saturation
Typ rekordu: dzienny
Obsługa webhooków
Przewodnik po funkcjach związanych ze zdrowiem
Przewodnik po funkcjach związanych ze snem
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
Obsługa webhooków
Przewodnik po funkcjach związanych ze zdrowiem
Przewodnik po funkcjach związanych ze snem
Zgodne urządzenia
|
lista, uzgodnij | .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, uzgodnij | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Dzienne odchylenia temperatury podczas snu
dataType:
daily-sleep-temperature-derivationsfilter parameter: daily_sleep_temperature_derivations
Typ rekordu: dzienny
Obsługa webhooków
Przewodnik po statystykach zdrowotnych
Przewodnik po śnie
Przewodnik po temperaturze 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
Zgodne urządzenia
|
reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Tętno
dataType:
heart-ratefilter parameter: heart_rate
Typ rekordu: Próbka
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: Próbka
Obsługa webhooków
Przewodnik po funkcjach związanych ze zdrowiem
Przewodnik po funkcjach związanych ze snem
Zgodne urządzenia
|
lista, uzgodnij | .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
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Informacje o częstości oddychania podczas snu
dataType:
respiratory-rate-sleep-summaryfilter parameter: respiratory_rate_sleep_summary
Typ rekordu: Próbka
Obsługa webhooków
Przewodnik po funkcjach związanych ze zdrowiem
Przewodnik po funkcjach związanych ze snem
Zgodne urządzenia
|
lista, uzgodnij | .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: Próbka
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: Próbka
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: po dołączeniu do środowiska wykonawczego Google Cloud (takiego jak Compute Engine, Cloud Run lub Google Kubernetes Engine) konta usługi 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.
- Przypisz niezbędne role uprawnień: przypisz do konta usługi odpowiednie role wymagane do zarządzania subskrybentami w projekcie Google Cloud.
- 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 personifikowane i wykonuje wywołania 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 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ć dostępny publicznie.
Podczas tworzenia i aktualizowania subskrybenta 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ę niepowodzeniem z 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 sekcjach Subskrypcje automatyczne i Subskrypcje ręczne.
Żą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 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 parametrze 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 podczas aktualizowania subscriberConfigs następuje pełne zastąpienie, a nie scalenie. Jeśli subscriberConfigs jest uwzględniony w 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ć kodem stanu
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, skonfiguruj punkt końcowy tak, aby akceptował tylko nową wartość
endpointAuthorization.
Subskrypcje użytkownika
Interfejs Google Health API pomaga efektywnie zarządzać subskrypcjami użytkowników, dzięki czemu nie trzeba ich rejestrować ręcznie podczas wprowadzania.
Subskrypcje automatyczne
Zalecamy korzystanie z subskrypcji automatycznych. 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ę aplikacji na zakresy odpowiadające 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 danych użytkowników, którzy wyrazili zgodę, i typów danych z 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 ustawieniem MANUAL subscription_create_policy w przypadku określonych typów danych, musisz jawnie tworzyć 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.
- Usuń subskrypcję (ręcznie) – usuwa subskrypcję określonego 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 żądanie HTTPS POST do punktu końcowego subskrybenta 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"
}
}Partia (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 trwałą, 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 słowniku tokenów typów danych webhooka.
Uwzględnienie 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ć, dane którego użytkownika uległy zmianie. Dzięki temu możesz użyć jego danych logowania OAuth, aby wysłać zapytanie o jego 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 z 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 danych zbiorczych pole physicalTimeInterval jest celowo pomijane w ładunku powiadomienia. Dzieje się tak, ponieważ 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 sekcji Filtrowanie danych według czasu rozpoczęcia interwału.
Poniżej znajdziesz przykładowy ładunek powiadomienia dotyczący 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 partię, 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 Batch (multiple users and data types) (Zestaw – wielu użytkowników i wiele typów danych).
Podejmowanie działania w związku z powiadomieniem
Serwer musi zweryfikować nagłówek Authorization, obsłużyć synchroniczne uzgadnianie 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 powiadomienia, wywołaj punkt końcowy reconcile z filtrem dla zakresu czasu powiadomienia. 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 artykule 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 poza kolejką 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 pobierz zaktualizowane dane z interfejsu Google Health API dopiero wtedy, gdy użytkownik ponownie odwiedzi Twoją aplikację.
Strategia kolejkowania umożliwia szybkie przetwarzanie danych, 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 zdecydować się na przetwarzanie tych kolejek 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ą szybkiej aktualizacji danych.
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 otrzymujesz powiadomienia o subskrypcji, umieszczaj 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 Base64 w nagłówku HTTP GOOGLE-HEALTH-API-SIGNATURE w żądaniu. Te klucze podpisu są automatycznie zmieniane 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 biblioteki 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 subskrypcji i jej przywracanie
Jeśli punkt końcowy aplikacji subskrybującej 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 wzrastającym czasem do ponowienia.
Gdy Twój punkt końcowy będzie znów 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 Google Cloud, a nie jego identyfikatora. |
| 403 Dostęp zabroniony | Wywołujący nie ma uprawnień | podczas tworzenia lub wyświetlania 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 Google Cloud, a nie jego identyfikatora. |