Google Health API позволяет вашему приложению получать уведомления в реальном времени, когда данные о здоровье пользователя меняются. Вместо опроса изменений ваш сервер получает запрос HTTPS POST вебхука, как только данные становятся доступны в Google Health API.
Поддерживаемые типы данных
Уведомления вебхуков поддерживаются для следующих типов данных:
| Тип данных | Доступные операции |
Область действия |
|---|---|---|
|
Количество минут активности
dataType:
active-minutesfilter parameter: active_minutes
Тип записи: интервал.
Разрешение хранилища: 1 минута.
Совместимые устройства
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Минуты в зоне активности
dataType:
active-zone-minutesfilter parameter: active_zone_minutes
Тип записи: интервал.
Разрешение хранилища: 1 минута.
Совместимые устройства
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Уровень активности
|
список, сверка | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Высота
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Уровень глюкозы в крови
dataType:
blood-glucosefilter parameter: blood_glucose
Тип записи: Sample.
|
list, get, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Жировая ткань
dataType:
body-fatfilter parameter: body_fat
Тип записи: Sample.
Совместимые устройства
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Калории в пульсовой зоне
dataType:
calories-in-heart-rate-zonefilter parameter: calories_in_heart_rate_zone
Тип записи: интервал.
Разрешение хранилища: 1 минута.
|
rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Вариабельность сердечного ритма за день
dataType:
daily-heart-rate-variabilityfilter parameter: daily_heart_rate_variability
Тип записи: ежедневно.
Совместимые устройства
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Ежедневные зоны пульса
dataType:
daily-heart-rate-zonesfilter parameter: daily_heart_rate_zones
Тип записи: ежедневно.
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Ежедневный уровень кислорода в крови
dataType:
daily-oxygen-saturationfilter parameter: daily_oxygen_saturation
Тип записи: ежедневно.
Совместимые устройства
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Ежедневная частота дыхания
dataType:
daily-respiratory-ratefilter parameter: daily_respiratory_rate
Тип записи: ежедневно.
Совместимые устройства
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Частота сердечных сокращений в покое за день
dataType:
daily-resting-heart-ratefilter parameter: daily_resting_heart_rate
Тип записи: ежедневно.
Совместимые устройства
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Ежедневные отклонения температуры во время сна
dataType:
daily-sleep-temperature-derivationsfilter parameter: daily_sleep_temperature_derivations
Тип записи: ежедневно.
Поддержка вебхуков
Руководство по жизненным показателям
Руководство по сну
Руководство по температуре кожи
Совместимые устройства
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Расстояние
dataType:
distancefilter parameter: distance
Тип записи: интервал.
Разрешение хранилища: 1 минута.
Совместимые устройства
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Тренировка
dataType:
exercisefilter parameter: exercise
Тип записи: сеанс.
Совместимые устройства
|
list, get, reconcile, create, update, batchDelete | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Этажи
|
reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Пульс
dataType:
heart-ratefilter parameter: heart_rate
Тип записи: Sample.
Разрешение хранилища: 1 секунда.
Совместимые устройства
|
list, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Вариабельность сердечного ритма
dataType:
heart-rate-variabilityfilter parameter: heart_rate_variability
Тип записи: Sample.
Совместимые устройства
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Высота
|
list, get, reconcile, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Журнал потребления жидкости
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Журнал питания
dataType:
nutrition-logfilter parameter: nutrition_log
Тип записи: сеанс.
Совместимые устройства
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Сводка о частоте дыхания во время сна
dataType:
respiratory-rate-sleep-summaryfilter parameter: respiratory_rate_sleep_summary
Тип записи: Sample.
Совместимые устройства
|
список, сверка | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
МПК при беге
dataType:
run-vo2-maxfilter parameter: run_vo2_max
Тип записи: Sample.
Совместимые устройства
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Период малоподвижного образа жизни
dataType:
sedentary-periodfilter parameter: sedentary_period
Тип записи: интервал.
Совместимые устройства
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Сон
dataType:
sleepfilter parameter: sleep
Тип записи: сеанс.
Совместимые устройства
|
list, get, reconcile, create, update, batchDelete | .sleep.readonly.sleep.writeonly |
|
Шаги
dataType:
stepsfilter parameter: steps
Тип записи: интервал.
Разрешение хранилища: 1 минута.
Совместимые устройства
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Время в пульсовой зоне
dataType:
time-in-heart-rate-zonefilter parameter: time_in_heart_rate_zone
Тип записи: интервал.
Разрешение хранилища: 1 минута.
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Вес
dataType:
weightfilter parameter: weight
Тип записи: Sample.
Совместимые устройства
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
Области действия уведомлений
Уведомления отправляются для этих типов данных, только если пользователь дал согласие для одной из соответствующих областей действия:
- Активность – количество минут активности, шагов, высота, расстояние и количество этажей.
https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonlyhttps://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly
- Показатели здоровья, которые включают тип данных "Вес":
https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonlyhttps://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly
- Сон – данные о сне:
https://www.googleapis.com/auth/googlehealth.sleep.readonlyhttps://www.googleapis.com/auth/googlehealth.sleep.writeonly
Сервисные аккаунты IAM
При настройке подписчиков для Google Health API мы рекомендуем использовать сервисный аккаунт IAM, хотя это и не обязательно. Сервисные аккаунты обеспечивают более высокий уровень безопасности для рабочих нагрузок приложений по сравнению со стандартными аккаунтами пользователей благодаря следующим функциям:
- Автоматизированные краткосрочные учетные данные. При подключении к среде выполнения Google Cloud (например, Compute Engine, Cloud Run или Google Kubernetes Engine) сервисные аккаунты автоматически получают и меняют безопасные краткосрочные учетные данные. Это позволяет избежать рисков, связанных с управлением и хранением постоянных статических ключей.
- Принцип минимальных привилегий. Сервисные аккаунты предоставляют выделенные идентификационные данные для рабочих нагрузок. Вы можете предоставить им только определенные разрешения, необходимые для управления конечными точками подписчиков, избегая более широкого доступа к вашим ресурсам Google Cloud.
- Независимость жизненного цикла. Сервисные аккаунты работают независимо от аккаунтов отдельных пользователей, поэтому кадровые изменения не влияют на стабильность аутентификации.
Как настроить сервисный аккаунт
Чтобы настроить аутентификацию приложения-подписчика с помощью сервисного аккаунта, выполните следующие действия:
- Создайте сервисный аккаунт. В консоли Google Cloud перейдите на страницу IAM & Admin (IAM и администрирование) и создайте новый управляемый пользователем сервисный аккаунт.
- Предоставьте необходимые роли IAM. Назначьте сервисному аккаунту соответствующие роли, необходимые для управления подписчиками в облачном проекте Google Cloud.
- Прикрепите сервисный аккаунт к рабочей нагрузке. Настройте среду, в которой размещена логика подписчика, для запуска в качестве нового сервисного аккаунта.
Это позволяет коду приложения (например, клиентским библиотекам Google API) автоматически обнаруживать и использовать краткосрочные учетные данные сервисного аккаунта при вызове REST API
projects.subscribers.
Роли CPE
Чтобы управлять подписчиками или подписками Google Health API, необходимо предоставить соответствующую роль сервисному аккаунту, от имени которого выполняются вызовы API. В зависимости от требуемого уровня доступа назначьте одну из следующих ролей:
- Google Health API Read
- Редактор Google Health API
- Администратор Google Health API
Подробнее о ролях и разрешениях IAM в Google Health API…
Как управлять списком подписчиков
Чтобы получать уведомления, необходимо зарегистрировать подписчика, который представляет конечную точку уведомлений вашего приложения. Управлять подписчиками можно с помощью REST API, доступного по адресу projects.subscribers.
Конечная точка подписчика должна использовать HTTPS (TLSv1.2+) и быть общедоступной.
При создании и обновлении подписчика Google Health API выполняет проверку, чтобы убедиться, что вы являетесь владельцем URI конечной точки. Если проверка не пройдена, то операции по созданию и обновлению подписчиков завершаются ошибкой FailedPreconditionException.
Создать подписчика
Чтобы зарегистрировать нового подписчика для проекта, используйте конечную точку create. Вам потребуется указать:
project-id: номер проекта, в котором был создан сервисный аккаунт вебхука.subscriberId– уникальный идентификатор, который вы предоставляете подписчику. Идентификатор должен содержать от 4 до 36 символов и соответствовать регулярному выражению ([a-z]([a-z0-9-]{2,34}[a-z0-9])).endpointUri– целевой URL для уведомлений вебхука.subscriberConfigs: типы данных, для которых вы хотите получать уведомления, и правила подписки для каждого из них.endpointAuthorization– механизм авторизации для конечной точки. Он должен содержать предоставленный вами объектsecret. Значениеsecretотправляется в заголовкеAuthorizationс каждым уведомлением. С помощью этого токена можно проверять, что входящие запросы поступают от Google Health API. Например, для аутентификации Bearer можно задать дляsecretзначениеBearer R4nd0m5tr1ng123, а для базовой аутентификации –Basic dXNlcjpwYXNzd29yZA==.
В subscriberConfigs необходимо задать subscriptionCreatePolicy для каждого типа данных. Установите значение AUTOMATIC, чтобы использовать автоматические подписки, или MANUAL, если вы хотите управлять подписками пользователей самостоятельно. Подробнее об автоматических и ручных подписках…
Запрос
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"
}
}Ответ
{
"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"
}
]
}Список подписчиков
Используйте конечную точку list, чтобы получить список всех подписчиков, зарегистрированных в вашем проекте.
Запрос
GET https://health.googleapis.com/v4/projects/project-id/subscribers
Ответ
{
"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
}Обновить подписчика
Чтобы обновить подписчика в проекте, используйте конечную точку patch. Обновить можно поля endpointUri, subscriberConfigs и endpointAuthorization.
Чтобы обновить поля, укажите параметр запроса updateMask и тело запроса. В параметре updateMask необходимо указать список названий полей, которые нужно обновить, разделенных запятыми. Названия полей должны быть написаны в верблюжьем регистре (например, endpointUri). Тело запроса должно содержать частичный объект Subscriber с новыми значениями для полей, которые нужно обновить. Обновляются только поля, указанные в updateMask. Если вы укажете в теле запроса поля, которых нет в updateMask, они будут проигнорированы.
Если вы обновите endpointUri или endpointAuthorization, будет выполнена проверка конечных точек. Подробнее о проверке конечных точек…
При обновлении subscriberConfigs происходит полная замена, а не объединение. Если subscriberConfigs включен в updateMask, все сохраненные конфигурации для этого подписчика перезаписываются списком, указанным в теле запроса. Чтобы добавить или удалить конфигурацию, необходимо предоставить полный набор конфигураций. Если вы обновляете другие поля и хотите сохранить текущие настройки, не указывайте subscriberConfigs в updateMask.
Запрос
PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id?updateMask=endpointUri
{
"endpointUri": "https://myapp.com/new-webhooks/health"
}Ответ
{
"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"
}
]
}Как удалить подписчика
Чтобы удалить подписчика из проекта, используйте конечную точку delete. После удаления подписчик больше не будет получать уведомления.
Запрос
DELETE https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id
Ответ
Если удаление выполнено успешно, возвращается пустой текст ответа со статусом HTTP `200 OK`.{}Проверка конечных точек
Чтобы обеспечить безопасность и надежность доставки уведомлений, Google Health API выполняет обязательную двухэтапную аутентификацию каждый раз, когда вы создаете подписчика или обновляете конфигурацию его конечной точки (endpointUri или endpointAuthorization). Этот процесс выполняется синхронно во время вызова API. Сервис отправляет два автоматических запроса POST на URI конечной точки, используя агент пользователя Google-Health-API-Webhooks с телом JSON {"type": "verification"}.
- Авторизованное рукопожатие. Первый запрос отправляется с настроенным вами заголовком
Authorization. Сервер должен возвращать статус200 OKили201 Created. - Запрос на авторизацию. Второй запрос отправляется без учетных данных.
Сервер должен возвращать статус
401 Unauthorizedили403 Forbidden.
Это подтверждает, что конечная точка активна и правильно обеспечивает безопасность. Если на любом из этих этапов произойдет сбой, запрос к API не будет выполнен и появится сообщение об ошибке FAILED_PRECONDITION. Только после успешного рукопожатия подписчик сохраняется и активируется для получения уведомлений о данных о здоровье.
Ротация ключей
Чтобы выполнить ротацию ключей для endpointAuthorization, сделайте следующее:
- Настройте конечную точку так, чтобы она принимала и старые, и новые значения
endpointAuthorization. - Обновите конфигурацию подписчика, указав новое значение
endpointAuthorization. Для этого используйте запросpatchс?updateMask=endpointAuthorization. - Настройте конечную точку так, чтобы она принимала только новое значение
endpointAuthorization, после того как убедитесь, что шаг 2 выполнен успешно.
Подписки пользователей
Google Health API помогает эффективно управлять подписками пользователей, снижая необходимость в ручной регистрации во время адаптации пользователей.
Автоматические подписки
Рекомендуем использовать автоматические подписки. Чтобы включить эту функцию, задайте для параметра subscriptionCreatePolicy значение AUTOMATIC в файле subscriberConfigs для определенных типов данных. Типы данных dataTypes, которые вы указываете в правилах AUTOMATIC, совпадают с типами данных, для которых Google Health API отправляет уведомления, если согласие пользователя также предоставлено для этих типов данных.
Когда пользователь предоставляет приложению согласие на доступ к областям действия, соответствующим типам данных с правилом AUTOMATIC, Google Health API автоматически отслеживает и отправляет уведомления для типов данных, которые соответствуют как типам данных, на доступ к которым пользователь дал согласие, так и типам данных, указанным в конфигурации автоматического подписчика для этого пользователя. После этого на вашу конечную точку будут отправляться уведомления, когда пользователь генерирует новые данные этих типов. Это работает для пользователей, которые дают согласие до или после создания подписчика. Уведомления не создаются для данных, сгенерированных до создания подписчика.
Если пользователь отзовет согласие, уведомления для соответствующих типов данных перестанут поступать. Автоматическими подписками управляет Google. Их нельзя просматривать или удалять по отдельности. Они удаляются только вместе с родительским подписчиком.
Подписки, оформленные вручную
Если для определенных типов данных в подписчике задано значение MANUAL subscription_create_policy, вы должны явным образом создавать подписки для каждого пользователя и управлять ими. Подписка связывает определенного пользователя с вашей конечной точкой подписчика для определенного набора типов данных. Разработчики могут использовать специальные API, чтобы:
- Create (manual) subscriptions per
healthUserId– создает подписку для определенного пользователя. Для этого метода у подписчика должно быть установлено значениеMANUALдляSubscriptionCreatePolicyв отношении запрашиваемых типов данных. - Обновление подписки (вручную) – обновление типов данных для существующей подписки пользователя.
- Delete (manual) subscription – удаление подписки пользователя. После удаления конечная точка подписчика больше не будет получать уведомления для этого пользователя по связанным типам данных.
- Список подписок (вручную) – список всех активных подписок для определенного подписчика. Результаты можно отфильтровать по пользователю или типу данных.
Уведомления
Когда данные пользователя меняются для подписанного типа данных, Google Health API отправляет запрос HTTPS POST на URL конечной точки подписчика.
Формат уведомления
Полезная нагрузка уведомления содержит сведения об изменении данных, включая идентификатор пользователя, тип данных и временные интервалы, которые можно использовать для запроса обновленных данных. Если происходит несколько обновлений или они агрегируются, уведомления объединяются в пакет и доставляются в виде массива JSON, состоящего из объектов уведомлений.
Когда срабатывает уведомление вебхука, Google Health API отправляет запрос HTTPS POST конечной точке подписчика в следующем формате:
Upsert (данные временного ряда)
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"
}
}
]
}
}Удалить (данные временного ряда)
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"
}
}
]
}
}
]Удалить (идентифицируемые данные)
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"
}
}Поле operation указывает на тип изменения, которое вызвало уведомление:
UPSERT– отправляется при любом добавлении или изменении данных.DELETE– отправляется, когда пользователь удаляет данные, напримерdataPointsв приложении Fitbit или с помощью API. Уведомления вебхука не отправляются для событий удаления на системном уровне, например когда пользователь отзывает разрешения приложения или удаляет свой аккаунт Fitbit.
Рекомендуем сделать логику обработки уведомлений идемпотентной, особенно для операций UPSERT, поскольку повторные попытки могут привести к отправке дублирующихся уведомлений.
Поле clientProvidedSubscriptionName – это уникальный идентификатор.
- Для подписок с правилами
MANUALэто поле содержит постоянное название подписки, указанное разработчиком при ее создании. Это позволяет получить стабильный идентификатор для управления подписками, оформленными вручную. - Для подписок, созданных с правилом
AUTOMATIC, Google Health API автоматически генерирует и назначает идентификатор в форматеauto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN>, где:<project-number>– номер облачного проекта Google Cloud разработчика (например,362978535920).<subscriber-id>– регистрационный идентификатор или имя подписчика (например,google-health-sync).<WEBHOOK_DATA_TYPE_TOKEN>– токен, представляющий определенный тип данных. Все возможные токены перечислены в словаре токенов типов данных вебхуков.
Включение clientProvidedSubscriptionName для правил, применяемых вручную и автоматически, обеспечивает согласованный формат полезной нагрузки уведомлений для всех типов подписок.
Сопоставление идентификаторов пользователей
healthUserId – это идентификатор Google Health API для пользователя, чьи данные изменились. Если ваше приложение поддерживает несколько пользователей, вы можете получать уведомления для любого пользователя, который предоставил согласие вашему приложению. Получив уведомление, используйте healthUserId, чтобы определить, чьи данные изменились, и запросить их с помощью учетных данных OAuth этого пользователя.
Чтобы сопоставить учетные данные OAuth пользователя с его healthUserId, используйте конечную точку getIdentity. Вызовите эту конечную точку с учетными данными пользователя во время его регистрации, чтобы получить его healthUserId, и сохраните это сопоставление. Это сопоставление не меняется со временем, поэтому его можно кешировать на неопределенный срок. Пример приведен в разделе Получение идентификатора пользователя. Это позволяет выбрать правильные учетные данные пользователя при запросе данных на основе healthUserId в уведомлении.
Временные интервалы
Массив intervals содержит один или несколько объектов, представляющих временные интервалы для измененных данных:
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
physicalTimeInterval |
object |
Нет | Физический интервал времени по UTC для измененных данных (формат YYYY-MM-DDTHH:MM:SSZ). Заполняется для показателей за день. Пропускается для ежедневных сводок и действий за весь день. |
civilDateTimeInterval |
object |
Да | Дата и временной интервал измененных данных по местному календарю без учета UTC или часового пояса. |
civilIso8601TimeInterval |
object |
Да | Интервал местного времени, представленный в виде строки ISO 8601 (формат YYYY-MM-DDTHH:MM:SS) без UTC или смещения часового пояса. |
Для действий, выполняемых в течение всего дня, и ежедневных агрегированных данных поле physicalTimeInterval намеренно опущено в полезной нагрузке уведомления. Это связано с тем, что мероприятия на весь день группируются по номинальной гражданской дате, а физическая продолжительность времени может быть неточной при пересечении часовых поясов. При обработке мероприятий на весь день рекомендуется использовать поля civilDateTimeInterval или civilIso8601TimeInterval. Подробнее о том, как запрашивать эти элементы данных с помощью фильтров гражданского времени…
Ниже приведен пример полезной нагрузки уведомления для действия, которое длится весь день. Поле 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"
}
}
]
}
}Словарь токенов типов данных вебхука
При использовании правила AUTOMATIC сгенерированный clientProvidedSubscriptionName содержит один из следующих суффиксов WEBHOOK_DATA_TYPE_*, написанных заглавными буквами, в зависимости от типа данных:
| Токен типа данных вебхука | Тип данных |
|---|---|
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 (системное событие) |
WEBHOOK_DATA_TYPE_USER_REVOKED_ACCESS |
user-revoked-access (системное событие) |
Агрегирование обновлений
Чтобы минимизировать сетевой трафик, Google Health API объединяет сообщения в пакеты. Система настроена на отправку не более 99 сообщений в пакете, которые автоматически отправляются по мере их появления.
При пакетной обработке сообщений полезная нагрузка доставляется в виде массива JSON, содержащего несколько объектов уведомлений. Один пакет может включать обновления для нескольких пользователей по нескольким типам данных, на которые оформлена подписка. Пример полезной нагрузки приведен в разделе Пакет (несколько пользователей и типов данных).
Реакция на уведомления
Ваш сервер должен проверить заголовок Authorization, обработать синхронное рукопожатие для проверки конечной точки и немедленно ответить на уведомления об изменении данных кодом статуса HTTP 204 No Content. Чтобы избежать тайм-аутов, обрабатывайте полезную нагрузку уведомления асинхронно после отправки ответа. Если Google Health API получает любой другой код статуса или время ожидания запроса истекает, он пытается отправить уведомление позже.
Пример 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).
}
});
});
Чтобы получить обновленные данные для каждого интервала, указанного в уведомлении, вызовите конечную точку reconcile с фильтром для диапазона дат интервала. При этом автоматически удаляются дублирующиеся интервалы с подключенных устройств. Подробную информацию и примеры вы найдете в статье Как получить согласованное представление данных за интервал.
Существует три распространенных стратегии обновления базы данных после получения уведомления об изменении данных:
- Постановка в очередь (рекомендуется). Входящие уведомления помещаются в очередь сообщений и обрабатываются по мере освобождения системных ресурсов.
- Немедленное извлечение. Настройте серверную часть так, чтобы она сразу вызывала Google Health API для получения обновленных данных и их сохранения в базе данных.
- Ленивые обновления. Пометьте записи в локальной базе данных пользователя как устаревшие и получайте обновленные данные из Google Health API только при следующем посещении пользователем вашего приложения.
Стратегия очередей позволяет обрабатывать данные практически в реальном времени, оставаясь устойчивой к потоку уведомлений (например, если сбой в обслуживании приводит к одновременной доставке большого количества уведомлений). Вы можете обрабатывать эти очереди по расписанию с длинными интервалами (например, каждые 3 часа), чтобы экономить ресурсы, но недостаток заключается в том, что пользователи обычно ожидают, что их данные будут обновляться почти в реальном времени.
Рекомендации
Чтобы обеспечить надежную и эффективную обработку обновлений данных, следуйте приведенным ниже рекомендациям.
- Используйте Subscription API. С помощью Subscription API вы можете получать уведомления о том, что у пользователя появились новые данные для скачивания, вместо того чтобы опрашивать конечные точки.
- Помещайте уведомления в очередь и обрабатывайте их отдельно. Когда вы получаете уведомления о подписках, помещайте их в очередь и обрабатывайте отдельно в зависимости от ресурсов системы. Чтобы не перегружать конечную точку вебхука, подтверждайте запрос сразу.
Подтверждение подписи
Чтобы обеспечить подлинность уведомлений вебхуков, необработанные полезные данные JSON каждого исходящего уведомления подписываются закрытым ключом с помощью PublicKeySign Tink. При этом в HTTP-заголовке GOOGLE-HEALTH-API-SIGNATURE запроса указывается подпись в кодировке Base64. Эти ключи подписи автоматически меняются каждые 30 дней, а соответствующий официальный набор открытых ключей распространяется в виде JSON-файла по постоянному URL https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
Как подтвердить подпись
С помощью Tink (рекомендуется). Разработчики могут проверить подпись, используя примитив PublicKeyVerify в Tink. Получите набор открытых ключей по постоянному URL, создайте экземпляр примитива PublicKeyVerify с набором ключей и проверьте декодированный заголовок GOOGLE-HEALTH-API-SIGNATURE по необработанной полезной нагрузке JSON веб-хука.
Проверка вручную (без Tink). Если вы не хотите использовать Tink, то можете проверить подпись вручную. Для этого выполните следующие действия:
- Декодирует заголовок
GOOGLE-HEALTH-API-SIGNATUREв формате Base64, чтобы отделить префикс Tink размером 5 байт (содержит префикс версии размером 1 байт и keyId размером 4 байта) от подписи в кодировке DER. - Получите набор ключей JSON по адресу https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
- Найдите ключ, соответствующий обработанному keyId, и декодируйте его поле value в формате Base64. Это поле содержит сериализованный буфер протокола EcdsaPublicKey.
- Извлеките из этих двоичных данных координаты x и y с прямым порядком байтов (теги Protobuf 3 и 4).
- Создайте экземпляр стандартного открытого ключа ECDSA P-256 во встроенной криптографической библиотеке, используя извлеченные координаты x и y.
- Проверьте необработанную полезную нагрузку JSON веб-хука по извлеченной подписи DER с помощью алгоритма SHA-256.
Статус подписчика и восстановление подписки
Если конечная точка подписчика станет недоступна или вернет код статуса ошибки (любой, кроме 204), Google Health API будет хранить ожидающие уведомления до семи дней и повторно отправлять их с экспоненциальной задержкой.
Когда конечная точка снова заработает и вернет код 204, API автоматически доставит все сохраненные сообщения. Уведомления, полученные более семи дней назад, удаляются без возможности восстановления.
Распространенные ошибки
| Код ошибки | Сообщение | Описание | Рекомендация |
|---|---|---|---|
| 400 Bad Request | Недопустимый номер проекта в названии ресурса. | При удалении или обновлении подписчика с использованием идентификатора проекта Google Cloud в URL запроса вместо номера проекта. Это относится к подпискам на вебхуки, использующим конечную точку projects.subscribers. |
В URL запроса используйте номер проекта Google Cloud, а не его идентификатор. |
| 403 Forbidden | У звонящего нет нужных прав | При создании или получении списка подписчиков с использованием идентификатора проекта Google Cloud в URL запроса вместо номера проекта. Это относится к подпискам на вебхуки, использующим конечную точку projects.subscribers. |
В URL запроса используйте номер проекта Google Cloud, а не его идентификатор. |