借助 Google Health API,您的应用可以在用户健康数据发生变化时接收实时通知。您的服务器无需轮询更改,而是在 Google Health API 中有数据可用时立即收到 HTTPS POST 请求(网络钩子)。{:target="_blank" class="external"}
支持的数据类型
Webhook 通知支持以下数据类型:
- 活跃区间分钟数
- 活动级别
- 海拔高度
- 血糖
- 体脂
- 心率区间内的卡路里
- 每日心率变异性
- 每日心率区间
- 每日血氧饱和度
- 每日呼吸频率
- 每日静息心率
- 每日睡眠体温推导
- 距离
- 锻炼
- 楼层数
- 心率
- 心率变异性
- 高度
- 饮水量记录
- 营养记录
- 呼吸频率睡眠总结
- 跑步最大摄氧量
- 久坐不动时段
- 睡眠
- 步骤
- 处于各心率区间的时间
- 总卡路里数
- 重量
仅当用户已针对以下相应范围授予同意声明时,系统才会针对这些数据类型发送通知:
- 活动,涵盖步数、海拔、距离和楼层数据类型:
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 和管理”页面,创建新的用户管理的服务账号。
- 授予必要的 IAM 角色: 为服务账号分配管理 Google Cloud 项目中的订阅者所需的相应角色。
- 将服务账号附加到工作负载: 将托管订阅者逻辑的环境配置为以新服务账号的身份运行。这样一来,您的应用代码(例如 Google API 客户端库)在调用
projects.subscribersREST API 时,便可自动检测并使用服务账号的短期有效凭据。
CPE 角色
如需管理 Google Health API 订阅者或订阅,您必须向发出 API 调用的模拟服务账号授予适当的角色。根据所需的访问权限级别,分配以下角色之一:
- Google Health API Read
- Google Health API Editor
- Google Health API Admin
详细了解 Google Health API IAM 角色和权限。
管理订阅者
您必须先注册订阅者(代表应用的通知端点),然后才能接收通知。您可以使用 projects.subscribers 提供的 REST API 管理订阅者。
订阅者端点必须使用 HTTPS (TLSv1.2+) 且可公开访问。在创建和更新订阅者期间,Google Health API 会执行验证质询,以确保您拥有该端点 URI。如果验证失败,订阅者创建和更新操作也会失败,并显示 FailedPreconditionException。
创建订阅者
如需为项目注册新订阅者,请使用 create 端点。您需要提供以下信息:
project-id:创建 Webhook 服务账号的项目编号。subscriberId:您为订阅者提供的唯一标识符。此 ID 的长度必须介于 4 到 36 个字符之间,并且必须与正则表达式 ([a-z]([a-z0-9-]{2,34}[a-z0-9])) 相匹配。endpointUri:Webhook 通知的目标网址。subscriberConfigs:您希望接收通知的数据类型,以及每种数据类型的订阅政策。endpointAuthorization:端点的授权机制。此字段必须包含您提供的secret。secret的值会随每个通知消息在Authorization标头中发送。您可以使用此令牌来验证传入的请求是否来自 Google 健康数据 API。例如,您可以将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)。请求正文必须包含一个部分订阅者对象,其中包含您要更新的字段的新值。系统只会更新 updateMask 中指定的字段。如果您在请求正文中提供的字段不在 updateMask 中,系统会忽略这些字段。
如果您更新 endpointUri 或 endpointAuthorization,系统会执行端点验证。如需了解详情,请参阅端点验证。
更新 subscriberConfigs 时,请注意这是完全替换,而不是合并。如果 updateMask 中包含 subscriberConfigs,则相应订阅者的所有已存储配置都会被请求正文中提供的列表覆盖。如需添加或移除配置,您必须提供完整的配置集。如果您要更新其他字段并希望保留当前配置,请从 updateMask 中省略 subscriberConfigs。
请求
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` 的空响应正文。{}端点验证
为确保通知传送的安全性和可靠性,每当您创建订阅者或更新其端点配置(endpointUri 或 endpointAuthorization)时,Google Health API 都会执行强制性的两步验证握手。此过程在 API 调用期间同步执行。该服务会使用 User-Agent Google-Health-API-Webhooks-Verifier 向您的端点 URI 发送两个自动 POST 请求,其中包含 JSON 正文 {"type": "verification"}。
- 授权握手:第一个请求随配置的
Authorization标头一起发送。您的服务器必须以200 OK或201 Created状态做出响应。 - 未经授权的质询:第二个请求是在未提供凭据的情况下发送的。您的服务器必须以
401 Unauthorized或403 Forbidden状态做出响应。
此握手可确认您的端点处于活动状态,并且正确强制执行安全性。如果任一步骤失败,API 请求也会失败,并显示 FAILED_PRECONDITION 错误。只有在此握手成功后,系统才会保存并激活您的订阅者,以便接收健康数据通知。
密钥轮替
如果您需要轮替 endpointAuthorization 的密钥,请按以下步骤操作:
- 将端点配置为同时接受旧值和新值
endpointAuthorization。 - 使用包含
?updateMask=endpointAuthorization的patch请求,将订阅者配置更新为新的endpointAuthorization值。 - 在确认第 2 步成功完成后,将端点配置为仅接受新的
endpointAuthorization值。
用户订阅
Google Health API 可帮助您高效管理用户订阅,从而减少用户初始配置期间对人工注册的需求。
自动订阅
我们建议使用自动订阅。如需启用此功能,请在 subscriberConfigs 中将特定数据类型的 subscriptionCreatePolicy 设置为 AUTOMATIC。您通过 AUTOMATIC 政策指定的 dataTypes 与 Google Health API 发送通知的数据类型相同,前提是用户也已针对这些数据类型授予用户同意。
当用户针对具有 AUTOMATIC 政策的数据类型授予应用范围权限时,Google Health API 会自动跟踪并发送通知,通知中包含用户同意的数据类型与该用户的自动订阅者配置数据类型之间的交集所产生的数据类型。然后,每当该用户生成这些类型的新数据时,系统都会向您的端点发送通知。无论用户是在您创建订阅者之前还是之后授予许可,此方法都适用。对于在订阅者创建之前生成的数据,系统不会回填通知。
如果用户撤消同意,系统将停止发送相应数据类型的通知。自动订阅由 Google 管理,无法单独列出或删除;只有在删除父订阅者时,系统才会移除自动订阅。
手动订阅
如果您的订阅者针对特定数据类型配置了 MANUAL subscription_create_policy,您必须为每位用户明确创建和管理订阅。订阅会将特定用户与您的订阅者端点相关联,以获取一组定义的数据类型。开发者可以使用特定 API 来执行以下操作:
- 按
healthUserId创建(手动)订阅 - 为特定用户创建新订阅。此方法要求订阅者针对所请求的数据类型将SubscriptionCreatePolicy设置为MANUAL。 - 更新(手动)订阅 - 更新现有用户订阅的数据类型。
- 删除(手动)订阅 - 删除特定用户的订阅。删除后,您的订阅者端点将不再接收此用户针对关联数据类型的通知。
- 列出(手动)订阅 - 列出指定订阅者的所有有效订阅。您可以按用户或数据类型过滤结果。
通知
当用户订阅的数据类型发生变化时,Google Health API 会向订阅方端点网址发送 HTTPS POST 请求。
通知格式
通知载荷是一个 JSON 对象,包含有关数据更改的详细信息。这包括用户 ID、数据类型和时间间隔,您可以使用这些信息查询更新后的数据。
{
"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"
}
}
]
}
}
operation 字段用于指示触发通知的更改类型:
UPSERT:在添加或修改任何数据时发送。DELETE:当用户删除数据时发送。
我们建议您使通知处理逻辑遵循幂等原则,尤其是对于 UPSERT 操作,因为重试可能会导致发送重复的通知。
clientProvidedSubscriptionName 字段是唯一标识符。对于采用 MANUAL 政策的订阅,此字段包含创建订阅时指定的持久性开发者提供的订阅名称。这样可以提供用于管理手动订阅的稳定 ID。对于使用 AUTOMATIC 政策创建的订阅,Google Health API 会自动为每条通知生成一个唯一标识符(随机 UUID)并将其分配给此字段。为手动和自动政策都添加 clientProvidedSubscriptionName 可确保所有订阅类型的通知载荷格式保持一致。
healthUserId 是数据发生更改的用户的 Google Health API 标识符。如果您的应用支持多位用户,您可能会收到已向您的应用授予同意权限的任何用户的通知。收到通知后,请使用 healthUserId 标识数据发生更改的用户,以便您可以使用其 OAuth 凭据查询其数据。
如需将用户的 OAuth 凭据映射到其 healthUserId,请使用 getIdentity 端点。在用户注册期间,使用用户的凭据调用此端点以检索其 healthUserId,并存储此映射。此映射不会随时间变化,因此可以无限期缓存。如需查看示例,请参阅获取用户 ID。这样,您就可以在通知中根据 healthUserId 查询数据时选择正确的用户凭据。
回应通知
您的服务器必须立即以 HTTP 204 No Content 状态代码响应通知。为避免超时,请在发送响应后异步处理通知载荷。如果 Google Health API 收到任何其他状态代码或请求超时,它会在稍后重试发送通知。
Node.js (Express) 示例:
app.post('/webhook-receiver', (req, res) => {
// 1. Immediately acknowledge the notification
res.status(204).send();
// 2. Process the data asynchronously in the background
const notification = req.body;
setImmediate(() => {
console.log(`Update for user ${notification.data.healthUserId} of type ${notification.data.dataType}`);
// Trigger your data retrieval logic here
});
});
签名验证
为确保网络钩子通知的真实性,每个传出的网络钩子通知的原始 JSON 载荷都会使用私钥通过 Tink 的 PublicKeySign 进行签名,并在请求的 GOOGLE-HEALTH-API-SIGNATURE HTTP 标头中提供 Base64 编码的签名。这些签名密钥每 30 天自动轮换一次,相应的官方公钥集以 JSON 文件的形式分发,永久网址为 https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json。
如何验证签名
使用 Tink(推荐):开发者可以使用 Tink 的 PublicKeyVerify 原语来验证签名。从永久网址中提取公钥集,使用该密钥集实例化 PublicKeyVerify 原语,并针对原始 webhook JSON 载荷验证解码后的 GOOGLE-HEALTH-API-SIGNATURE 标头。
手动验证(不使用 Tink):如果开发者选择不使用 Tink,可以按照以下步骤手动验证签名:
- 对
GOOGLE-HEALTH-API-SIGNATURE标头进行 Base64 解码,以将 5 字节的 Tink 前缀(包含 1 字节的版本前缀和 4 字节的 keyId)与实际的 DER 编码签名分开。 - 从 https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json 获取 JSON 密钥集。
- 找到与解析的 keyId 匹配的密钥,并对其值字段(包含序列化的 EcdsaPublicKey Protocol Buffer)进行 Base64 解码。
- 从该二进制载荷中提取大端序 x 和 y 坐标(Protobuf 标记 3 和 4)。
- 使用提取的 x 和 y 坐标在内置加密库中实例化标准 ECDSA P-256 公钥。
- 使用 SHA-256 算法,根据提取的 DER 签名验证原始 webhook JSON 载荷。
订阅者状态和恢复
如果订阅方端点变得不可用或返回错误状态代码(除 204 以外的任何代码),Google Health API 会将待处理的通知存储最多 7 天,并以指数退避算法方式重试传送。
当您的端点重新上线并以 204 进行响应时,API 会自动传送积压的已存储消息。超过 7 天的通知会被舍弃,无法恢复。
常见错误
| 错误代码 | 消息 | 说明 | 建议 |
|---|---|---|---|
| 400 无效请求 | 资源名称中的项目编号无效 | 在请求网址中使用 Google Cloud 项目 ID 而不是项目编号来删除或更新订阅者时。这适用于使用 projects.subscribers 端点的 Webhook 订阅。 |
在请求网址中使用您的 Google Cloud 项目编号,而不是项目 ID。 |
| 403 禁止访问 | 调用方无权限 | 在请求网址中使用 Google Cloud 项目 ID 而不是项目编号来创建或列出订阅者时。这适用于使用 projects.subscribers 端点的 Webhook 订阅。 |
在请求网址中使用您的 Google Cloud 项目编号,而不是项目 ID。 |