Google Health API 可讓應用程式在使用者健康資料變更時收到通知。Google Health API 有資料可用時,伺服器會立即收到 HTTPS POST 要求 Webhook,不必輪詢變更。
支援的資料類型
Webhook 通知支援下列資料類型:
| 資料類型 | 可用的 作業 |
範圍 |
|---|---|---|
|
活動分鐘數
dataType:
active-minutes篩選器參數: active_minutes
記錄類型:間隔
儲存空間解析度:1 分鐘
相容裝置
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
活動區間分鐘數
dataType:
active-zone-minutes篩選器參數: active_zone_minutes
記錄類型:間隔
儲存空間解析度:1 分鐘
相容裝置
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
活動強度
|
list、reconcile | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Altitude
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
血糖
|
list、get、reconcile、rollup、dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
體脂肪
|
list、get、reconcile、rollup、dailyRollup、create、update、batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
心率區間卡路里
|
rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
每日心率變異
dataType:
daily-heart-rate-variability篩選器參數: daily_heart_rate_variability
記錄類型:每日
相容裝置
|
list、reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
每日心率區間
|
list、reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
每日血氧濃度
dataType:
daily-oxygen-saturation篩選器參數: daily_oxygen_saturation
記錄類型:每日
相容裝置
|
list、reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
每日呼吸速率
dataType:
daily-respiratory-rate篩選器參數: daily_respiratory_rate
記錄類型:每日
相容裝置
|
list、reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
每日靜止心率
dataType:
daily-resting-heart-rate篩選器參數: daily_resting_heart_rate
記錄類型:每日
相容裝置
|
list、reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
每日睡眠體溫變化
dataType:
daily-sleep-temperature-derivations篩選器參數: daily_sleep_temperature_derivations
記錄類型:每日
相容裝置
|
list、reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
距離
dataType:
distance篩選器參數: distance
記錄類型:間隔
儲存空間解析度:1 分鐘
相容裝置
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
運動
dataType:
exercise篩選器參數: exercise
記錄類型:工作階段
相容裝置
|
list、get、reconcile、create、update、batchDelete | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
樓層數
dataType:
floors篩選器參數: floors
記錄類型:間隔
儲存空間解析度:1 分鐘
相容裝置
|
reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
心率
dataType:
heart-rate篩選器參數: heart_rate
記錄類型:範例
儲存空間解析度:1 秒
相容裝置
|
list、reconcile、rollup、dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
心率變異
dataType:
heart-rate-variability篩選器參數: heart_rate_variability
記錄類型:範例
相容裝置
|
list、reconcile | .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 |
|
營養記錄
|
list、get、reconcile、rollup、dailyRollup、create、update、batchDelete | .nutrition.readonly.nutrition.writeonly |
|
睡眠呼吸速率摘要
dataType:
respiratory-rate-sleep-summary篩選器參數: respiratory_rate_sleep_summary
記錄類型:範例
相容裝置
|
list、reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
跑步最大攝氧量
dataType:
run-vo2-max篩選器參數: run_vo2_max
記錄類型:範例
相容裝置
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
久坐時間
dataType:
sedentary-period篩選器參數: sedentary_period
記錄類型:間隔
相容裝置
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
睡眠
dataType:
sleep篩選器參數: sleep
記錄類型:工作階段
相容裝置
|
list、get、reconcile、create、update、batchDelete | .sleep.readonly.sleep.writeonly |
|
步驟
dataType:
steps篩選器參數: steps
記錄類型:間隔
儲存空間解析度:1 分鐘
相容裝置
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
心率區間時間
|
list、reconcile、rollup、dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
權重
|
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 與管理」頁面,建立新的使用者自行管理服務帳戶。
- 授予必要的 IAM 角色:為服務帳戶指派適當的角色,以便管理 Google Cloud 雲端專案的訂閱者。
- 將服務帳戶附加至工作負載:設定託管訂閱者邏輯的環境,以新服務帳戶的身分執行。這樣一來,應用程式程式碼 (例如 Google API 用戶端程式庫) 呼叫
projects.subscribersREST API 時,就能自動偵測並使用服務帳戶的短期憑證。
CPE 角色
如要管理 Google Health API 訂閱者或訂閱項目,您必須將適當的角色授予發出 API 呼叫的模擬服務帳戶。請根據所需的存取層級,指派下列其中一個角色:
- Google Health API 讀取
- Google Health API 編輯者
- Google Health API 管理員
進一步瞭解 Google Health API IAM 角色和權限。
管理訂閱者
您必須先註冊訂閱者 (代表應用程式的通知端點),才能接收通知。您可以使用 projects.subscribers 提供的 REST API 管理訂閱者。
訂閱者端點必須使用 HTTPS (TLSv1.2+) 且可公開存取。建立及更新訂閱者時,Google Health API 會執行驗證問題,確保您擁有端點 URI。如果驗證失敗,訂閱者建立和更新作業就會失敗,並顯示 FailedPreconditionException。
建立訂閱者
如要為專案註冊新的訂閱者,請使用 create 端點。您需要提供:
project-id:建立 Webhook 服務帳戶的專案編號。subscriberId:您為訂閱者提供的專屬 ID。這個 ID 的長度必須介於 4 到 36 個字元之間,且符合規則運算式 ([a-z]([a-z0-9-]{2,34}[a-z0-9]))。endpointUri:Webhook 通知的到達網頁網址。subscriberConfigs:您想接收通知的資料類型, 以及各類型的訂閱政策。endpointAuthorization::端點的授權機制。這必須包含您提供的secret。secret的值會與每則通知訊息一起在Authorization標頭中傳送。您可以使用這個權杖,驗證傳入的要求是否來自 Google Health 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)。要求主體必須包含部分 Subscriber 物件,其中含有要更新欄位的新值。只有 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 呼叫期間同步執行。服務會使用使用者代理程式 Google-Health-API-Webhooks,將兩個自動 POST 要求傳送至端點 URI,並附上 JSON 主體 {"type": "verification"}。
- 授權交握:第一個要求會與您設定的
Authorization標頭一起傳送。伺服器必須傳回200 OK或201 Created狀態。 - 未授權驗證:第二項要求在未提供憑證的情況下傳送。伺服器必須傳回
401 Unauthorized或403 Forbidden狀態。
這項交握程序可確認端點是否有效,以及是否正確執行安全性。如果任一步驟失敗,API 要求就會失敗,並傳回 FAILED_PRECONDITION 錯誤。只有在握手成功後,系統才會儲存並啟用訂閱者,以便接收健康資料通知。
金鑰輪替
如要輪替 endpointAuthorization 的金鑰,請按照下列步驟操作:
- 將端點設為接受新舊
endpointAuthorization值。 - 使用
patch要求和?updateMask=endpointAuthorization,以新的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 要求。
通知格式
通知酬載包含資料變更的詳細資料,包括使用者 ID、資料類型和時間間隔,您可以使用這些資料查詢更新後的資料。如果有多項更新或更新項目經過匯總,系統會將通知批次處理,並以通知物件的 JSON 陣列形式傳送。
觸發 Webhook 通知時,Google Health API 會向訂閱者端點傳送 HTTPS POST 要求,格式如下:
新增或更新 (時間序列資料)
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"
}
}批次 (多位使用者和多種資料類型)
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"
}
}
]operation 欄位會指出觸發通知的變更類型:
UPSERT:新增或修改任何資料時傳送。DELETE:當使用者明確刪除資料 (例如使用 Fitbit 應用程式刪除dataPoints,或使用 API 執行批次刪除) 時傳送。系統層級的刪除事件 (例如使用者撤銷應用程式權限或刪除 Fitbit 帳戶) 不會傳送 Webhook 通知。
建議您將通知處理邏輯設為冪等,尤其是 UPSERT 作業,因為重試可能會導致系統傳送重複的通知。
clientProvidedSubscriptionName 欄位是專屬 ID:
- 如果是採用
MANUAL政策的訂閱項目,這個欄位會包含建立訂閱項目時,由開發人員提供的永久訂閱項目名稱。這可提供穩定的 ID,用於管理手動訂閱項目。 - 對於使用
AUTOMATIC政策建立的訂閱項目,Google Health API 會自動產生並指派格式為auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN>的 ID,其中:<project-number>是開發人員的 Google Cloud 專案編號 (例如362978535920)。<subscriber-id>是訂閱者的註冊 ID/訂閱者名稱 (例如google-health-sync)。<WEBHOOK_DATA_TYPE_TOKEN>是代表特定資料類型的權杖。如要查看所有可能的權杖,請參閱「Webhook 資料類型權杖詞彙」。
無論是手動或自動政策,都應加入 clientProvidedSubscriptionName,確保所有訂閱類型的通知酬載格式一致。
使用者 ID 對應
healthUserId 是 Google Health API ID,用於識別資料有異動的使用者。如果應用程式支援多位使用者,只要使用者同意授權,您就能收到通知。收到通知時,請使用 healthUserId 找出資料有變動的使用者,以便使用他們的 OAuth 憑證查詢資料。
如要將使用者的 OAuth 憑證對應至其 healthUserId,請使用 getIdentity 端點。在使用者新手上路期間,使用使用者憑證呼叫這個端點,擷取其 healthUserId,並儲存這項對應關係。這項對應關係不會隨時間變更,因此可以無限期快取。如需範例,請參閱「取得使用者 ID」。這樣一來,您就能根據通知中的 healthUserId 查詢資料,並選取正確的使用者憑證。
時間間隔
intervals 陣列包含一或多個物件,代表修改資料的時間間隔:
| 欄位 | 類型 | 必要 | 說明 |
|---|---|---|---|
physicalTimeInterval |
object |
否 | 修改資料的世界標準時間實際時間間隔 (YYYY-MM-DDTHH:MM:SSZ 格式)。系統會填入當日指標,但會省略每日匯總和全天活動。 |
civilDateTimeInterval |
object |
是 | 修改資料的民用 (當地) 日曆日期和時間間隔,不含世界標準時間或時區偏移。 |
civilIso8601TimeInterval |
object |
是 | 以 ISO 8601 字串 (YYYY-MM-DDTHH:MM:SS 格式) 表示的民用 (當地) 時間間隔,不含世界標準時間或時區偏移。 |
如果是全天活動和每日匯總資料,通知酬載會刻意省略 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"
}
}
]
}
}Webhook 資料類型權杖的詞彙
使用 AUTOMATIC 政策時,產生的 clientProvidedSubscriptionName 會包含下列其中一個大寫 WEBHOOK_DATA_TYPE_* 後置字元符記,具體取決於資料類型:
| Webhook 資料類型權杖 | 對應的資料類型 |
|---|---|
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,擷取更新後的資料並提交至資料庫。
- 延遲更新:將使用者的本機資料庫記錄標示為「dirty」,並在使用者下次造訪應用程式時,才從 Google Health API 擷取更新資料。
佇列策略可讓您及時處理資料,同時保持彈性,因應大量通知 (例如,服務中斷導致大量通知同時傳送)。雖然您可以選擇以較長的時間間隔批次排程 (例如每 3 小時) 處理這些佇列,以節省資源,但缺點是使用者通常希望看到資料能及時更新。
最佳做法
請遵循下列最佳做法,確保能可靠且有效率地處理資料更新:
- 使用 Subscription API:使用 Subscription API,在使用者有新資料可供下載時接收通知,而不是輪詢端點。
- 分別排入佇列並處理通知:收到訂閱通知時,請將通知放入佇列,並根據系統資源分別處理。立即確認要求,讓 Webhook 端點保持輕量。
驗證簽章
為確保 Webhook 通知真實可靠,每個傳出 Webhook 通知的原始 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 相符的金鑰,並對其值欄位進行 Base64 解碼,其中包含序列化的 EcdsaPublicKey 通訊協定緩衝區。
- 從這個二進位酬載中擷取大端序 x 和 y 座標 (Protobuf 標記 3 和 4)。
- 使用擷取的 x 和 y 座標,在內建密碼編譯程式庫中例項化標準 ECDSA P-256 公開金鑰。
- 使用 SHA-256 演算法,根據擷取的 DER 簽章驗證原始 Webhook JSON 酬載。
訂閱者狀態和復原
如果訂閱者端點無法使用或傳回錯誤狀態碼 (204 以外的任何狀態碼),Google Health API 會儲存待處理的通知,最多保留 7 天,並以指數輪詢方式重試傳送。
端點重新上線並回應 204 後,API 會自動傳送待處理的已儲存訊息。超過 7 天的通知會遭到捨棄,且無法復原。
常見錯誤
| 錯誤代碼 | 訊息 | 說明 | 建議 |
|---|---|---|---|
| 400 Bad Request | 資源名稱中的專案編號無效 | 在要求網址中使用 Google Cloud 專案 ID (而非專案編號) 刪除或更新訂閱者時,這項規定適用於使用 projects.subscribers 端點的 Webhook 訂閱項目。 |
在要求網址中使用 Google Cloud 專案編號,而非專案 ID。 |
| 403 禁止存取 | 呼叫者沒有權限 | 使用 Google Cloud 雲端專案 ID (而非專案編號) 在要求網址中建立或列出訂閱端時。這項規定適用於使用 projects.subscribers 端點的 Webhook 訂閱項目。 |
在要求網址中使用 Google Cloud 專案編號,而非專案 ID。 |