Google Health API מאפשר לאפליקציה לקבל התראות כשנתוני הבריאות וכושר של המשתמש משתנים. במקום לבדוק אם יש שינויים, השרת מקבל webhook של בקשת HTTPS POST ברגע שהנתונים זמינים ב-Google Health API.
סוגי נתונים נתמכים
אפשר לקבל התראות Webhook על סוגי הנתונים הבאים:
| סוג הנתונים | פעולות אפשריות |
היקף |
|---|---|---|
|
דקות פעילות
dataType:
active-minutesfilter parameter: active_minutes
סוג רשומה: אינטרוול
רזולוציית אחסון: דקה אחת
מכשירים תואמים
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
דקות טווח דופק
dataType:
active-zone-minutesfilter parameter: active_zone_minutes
סוג רשומה: אינטרוול
רזולוציית אחסון: דקה אחת
מכשירים תואמים
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
רמת הפעילות
dataType:
activity-levelfilter parameter: activity_level
סוג רשומה: אינטרוול
|
רשימה, התאמה | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Altitude
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
רמת סוכר בדם
dataType:
blood-glucosefilter parameter: blood_glucose
סוג הרשומה: דוגמה
|
list, get, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
אחוז שומן בגוף
dataType:
body-fatfilter parameter: body_fat
סוג הרשומה: דוגמה
מכשירים תואמים
|
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
סוג רשומה: אינטרוול
רזולוציית אחסון: דקה אחת
|
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
סוג רשומה: אינטרוול
רזולוציית אחסון: דקה אחת
מכשירים תואמים
|
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 |
|
רצפות
dataType:
floorsfilter parameter: floors
סוג רשומה: אינטרוול
רזולוציית אחסון: דקה אחת
מכשירים תואמים
|
reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
דופק
dataType:
heart-ratefilter parameter: heart_rate
סוג הרשומה: דוגמה
רזולוציית אחסון: שנייה אחת
מכשירים תואמים
|
list, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
שונות קצב הלב
dataType:
heart-rate-variabilityfilter parameter: heart_rate_variability
סוג הרשומה: דוגמה
מכשירים תואמים
|
רשימה, התאמה | .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 |
|
יומן מאזן הנוזלים
dataType:
hydration-logfilter parameter: hydration_log
סוג הרשומה: סשן
|
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-summaryfilter parameter: respiratory_rate_sleep_summary
סוג הרשומה: דוגמה
מכשירים תואמים
|
רשימה, התאמה | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
ריצת VO2 Max
dataType:
run-vo2-maxfilter parameter: run_vo2_max
סוג הרשומה: דוגמה
מכשירים תואמים
|
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
סוג רשומה: אינטרוול
רזולוציית אחסון: דקה אחת
מכשירים תואמים
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
זמן בטווח הדופק
dataType:
time-in-heart-rate-zonefilter parameter: time_in_heart_rate_zone
סוג רשומה: אינטרוול
רזולוציית אחסון: דקה אחת
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
משקל
dataType:
weightfilter parameter: weight
סוג הרשומה: דוגמה
מכשירים תואמים
|
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
לא חובה, אבל מומלץ להשתמש בחשבון שירות של IAM כשמגדירים מנויים ל-Google Health API. חשבונות שירות מספקים אבטחה טובה יותר לעומסי עבודה של אפליקציות בהשוואה לחשבונות משתמשים רגילים, באמצעות התכונות הבאות:
- פרטי כניסה אוטומטיים לטווח קצר: כשמצרפים חשבונות שירות לסביבת הרצה של Google Cloud (כמו Compute Engine, Cloud Run או Google Kubernetes Engine), הם מקבלים ומחליפים באופן אוטומטי פרטי כניסה מאובטחים לטווח קצר. כך נמנעים הסיכונים שקשורים לניהול ולאחסון של מפתחות סטטיים קבועים.
- העיקרון של הרשאות מינימליות: חשבונות שירות מספקים זהויות ייעודיות לעומסי עבודה. אתם יכולים לתת להם רק את ההרשאות הספציפיות שנדרשות כדי לנהל את נקודות הקצה של המנויים, וכך להימנע ממתן גישה רחבה יותר למשאבים שלכם ב-Google Cloud.
- מחזור חיים עצמאי: חשבונות שירות פועלים בנפרד מחשבונות של משתמשים ספציפיים, כך ששינויים בצוות לא משפיעים על היציבות של האימות לטווח הארוך.
הגדרה של חשבון שירות
כדי להגדיר את אפליקציית המנוי לאימות באמצעות חשבון שירות:
- יצירת חשבון שירות: במסוף Google Cloud, נכנסים לדף IAM & Admin של הפרויקט כדי ליצור חשבון שירות חדש בניהול המשתמש.
- הקצאת תפקידי IAM נדרשים: מקצים לחשבון השירות את התפקידים המתאימים שנדרשים כדי לנהל את המנויים בפרויקט בענן ב-Google Cloud.
- מקשרים את חשבון השירות לעומס העבודה: מגדירים את הסביבה שמארחת את הלוגיקה של המנוי כך שתפעל כחשבון השירות החדש.
כך קוד האפליקציה (כמו ספריות הלקוח של Google API) יכול לזהות באופן אוטומטי את פרטי הכניסה לטווח קצר של חשבון השירות ולהשתמש בהם כששולחים קריאה ל-API בארכיטקטורת REST של
projects.subscribers.
תפקידי CPE
כדי לנהל את המנויים או המינויים של Google Health API, צריך להקצות את התפקיד המתאים לחשבון השירות שמתחזה למשתמש ומבצע את הקריאות ל-API. בהתאם לרמת הגישה הנדרשת, מקצים אחד מהתפקידים הבאים:
- קריאה של Google Health API
- עריכה ב-Google Health API
- אדמין ב-Google Health API
מידע נוסף על תפקידים והרשאות ב-IAM של Google Health API
ניהול המנויים
כדי לקבל התראות, צריך לרשום מנוי שמייצג את נקודת הקצה של ההתראות באפליקציה. אפשר לנהל את המנויים באמצעות ה-API בארכיטקטורת REST שזמין בכתובת projects.subscribers.
נקודת הקצה של המנוי צריכה להשתמש ב-HTTPS (TLSv1.2 ואילך) ולהיות נגישה לכולם.
במהלך היצירה והעדכון של המנויים, Google Health API מבצע אתגר אימות כדי לוודא שאתם הבעלים של ה-URI של נקודת הקצה. אם האימות נכשל, פעולות היצירה והעדכון של המנוי נכשלות עם השגיאה FailedPreconditionException.
יצירת מנוי
כדי לרשום מנוי חדש לפרויקט, משתמשים בנקודת הקצה create. תצטרכו לספק:
-
project-id: מספר הפרויקט שבו נוצר חשבון השירות של ה-webhook. -
subscriberId: מזהה ייחודי שאתם מספקים למנוי. המזהה צריך להיות באורך שבין 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לאימות Bearer, או לערך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`.{}אימות של נקודות קצה (Endpoint Verification)
כדי להבטיח את האבטחה והאמינות של שליחת ההתראות, 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 ואי אפשר להציג אותם ברשימה או למחוק אותם בנפרד. הם מוסרים רק כשמוחקים את המינוי הראשי.
מינויים ידניים
אם המנוי מוגדר עם subscription_create_policy ידני לסוגי נתונים ספציפיים, צריך ליצור ולנהל מינויים באופן מפורש לכל משתמש. מינוי מקשר משתמש ספציפי לנקודת הקצה של המנוי שלכם, עבור קבוצה מוגדרת של סוגי נתונים. מפתחים יכולים להשתמש בממשקי API ספציפיים כדי:
- יצירת מינויים (ידניים) לכל
healthUserId– יצירת מינוי חדש למשתמש ספציפי. כדי להשתמש בשיטה הזו, המנוי צריך להגדיר אתSubscriptionCreatePolicyלערךMANUALלסוגי הנתונים המבוקשים. - עדכון מינוי (ידני) – עדכון סוגי הנתונים של מינוי משתמש קיים.
- מחיקת מינוי (ידנית) – מחיקת מינוי של משתמש ספציפי. אחרי המחיקה, נקודת הקצה של המנוי לא תקבל יותר התראות לגבי המשתמש הזה עבור סוגי הנתונים המשויכים.
- רשימת מינויים (ידנית) – מציגה את כל המינויים הפעילים של מנוי מסוים. אפשר לסנן את התוצאות לפי משתמש או סוג נתונים.
התראות
כשנתונים של משתמש משתנים בסוג נתונים שהמנוי נרשם אליו, Google Health API שולח בקשת HTTPS POST לכתובת ה-URL של נקודת הקצה של המנוי.
פורמט ההתראות
מטען הייעודי (payload) של ההתראה מכיל פרטים על השינוי בנתונים, כולל מזהה המשתמש, סוג הנתונים ומרווחי הזמן, שבהם אתם יכולים להשתמש כדי לשלוח שאילתות לגבי הנתונים המעודכנים. כשמתרחשים כמה עדכונים או כשהעדכונים נצברים, ההתראות נשלחות יחד כמערך 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: נשלח כשמשתמש מוחק נתונים באופן מפורש (למשל, מחיקתdataPointsבאמצעות אפליקציית Fitbit או מחיקה בכמות גדולה באמצעות ה-API). התראות Webhook לא נשלחות לאירועי מחיקה ברמת המערכת, כמו משתמש שמבטל את הרשאות האפליקציה או מוחק את חשבון 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>הוא האסימון שמייצג את סוג הנתונים הספציפי. בקטע אוצר מילים של אסימונים מסוג נתוני webhook מפורטים כל האסימונים האפשריים.
-
הכללת clientProvidedSubscriptionName גם במדיניות ידנית וגם במדיניות אוטומטית מבטיחה פורמט עקבי של מטען ייעודי (payload) של התראות בכל סוגי המינויים.
מיפוי מזהי משתמשים
השדה 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 מושמט בכוונה מהמטען הייעודי (payload) של ההתראה. הסיבה לכך היא שאירועים שנמשכים יום שלם מקובצים לפי התאריך האזרחי הנומינלי שלהם, ומשך הזמן הפיזי יכול להיות לא מדויק כשחוצים אזורי זמן. כשמעבדים אירועים שנמשכים יום שלם, מומלץ להסתמך על השדות civilDateTimeInterval או civilIso8601TimeInterval. במאמר סינון נתונים לפי שעת התחלה אזרחית של מרווח זמן מוסבר איך לשלוח שאילתות לגבי נקודות הנתונים האלה באמצעות מסנני זמן אזרחי.
הדוגמה הבאה היא מטען ייעודי (payload) של התראה על פעילות שנמשכת יום שלם, שבה השדה 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 שמכיל כמה אובייקטים של התראות. אצווה אחת יכולה לכלול עדכונים לכמה משתמשים בכמה סוגי נתונים שהם נרשמו אליהם. דוגמה למטען ייעודי (payload) מופיעה בקטע Batch (ריבוי משתמשים וסוגי נתונים) בפורמט של התראות.
איך מגיבים להתראה
השרת צריך לאמת את הכותרת Authorization, לטפל בלחיצת היד הסינכרונית של אימות נקודת הקצה (endpoint) ולהגיב מיד להתראות על שינויים בנתונים באמצעות קוד סטטוס HTTP 204 No Content. כדי למנוע זמן קצוב לתפוגה, צריך לעבד את המטען הייעודי (payload) של ההתראה באופן אסינכרוני אחרי שליחת התגובה. אם 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 עם מסנן לטווח התאריכים של המרווח. התכונה הזו מסירה באופן אוטומטי כפילויות של פרקי זמן חופפים ממכשירים מחוברים. לפרטים נוספים ולעיון בדוגמאות, ראו תצוגה מותאמת של נתוני מרווחים.
יש שלוש אסטרטגיות נפוצות לעדכון מסד הנתונים אחרי קבלת התראה על שינוי בנתונים:
- הוספה לתור (מומלץ): הוספת ההתראות הנכנסות לתור הודעות ועיבוד שלהן מחוץ לתור על סמך משאבי המערכת.
- אחזור מיידי: אתם יכולים להנחות את ה-backend לבצע קריאה מיידית ל-Google Health API כדי לאחזר את הנתונים המעודכנים ולשמור אותם במסד הנתונים.
- עדכונים עצלים: מסמנים את הרשומות במסד הנתונים המקומי של המשתמש כ"מלוכלכות", ומאחזרים את הנתונים המעודכנים מ-Google Health API רק כשהמשתמש נכנס לאפליקציה בפעם הבאה.
אסטרטגיית התור מאפשרת לכם לעבד נתונים במהירות, תוך שמירה על עמידות בפני הצפה של התראות (לדוגמה, אם הפסקת שירות גורמת להצטברות של התראות שצריך לשלוח בבת אחת). אפשר לבחור לעבד את התורים האלה לפי לוח זמנים של אצווה במרווחים ארוכים (למשל, כל 3 שעות) כדי לחסוך במשאבים, אבל החיסרון הוא שבדרך כלל המשתמשים מצפים שהנתונים שלהם יתעדכנו מיד.
שיטות מומלצות
כדי לוודא שהטיפול בעדכוני הנתונים יהיה אמין ויעיל, כדאי לפעול לפי השיטות המומלצות הבאות:
- שימוש ב-Subscription API: משתמשים ב-Subscription API כדי לקבל התראה כשיש למשתמש נתונים חדשים להורדה, במקום לדגום נקודות קצה.
- הוספה לתור ועיבוד של התראות בנפרד: כשמקבלים התראות על מינויים, כדאי להוסיף אותן לתור ולעבד אותן בנפרד בהתאם למשאבי המערכת. כדי שנקודת הקצה של ה-webhook תהיה קלה, צריך לאשר את הבקשה באופן מיידי.
אימות חתימה
כדי לוודא את האותנטיות של התראות Webhook, המטען הייעודי (payload) הגולמי של JSON בכל התראת Webhook יוצאת נחתם באמצעות מפתח פרטי באמצעות PublicKeySign של Tink, ומספק את החתימה בקידוד Base64 בכותרת ה-HTTP GOOGLE-HEALTH-API-SIGNATURE בבקשה. מפתחות החתימה האלה עוברים רוטציה אוטומטית כל 30 יום, וקבוצת המפתחות הציבוריים הרשמיים התואמת מופצת כקובץ JSON בכתובת ה-URL הקבועה https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
איך מאמתים את החתימה
שימוש ב-Tink (מומלץ): מפתחים יכולים לאמת את החתימה באמצעות הפרימיטיב PublicKeyVerify של Tink. אחזרו את קבוצת המפתחות הציבוריים מכתובת ה-URL הקבועה, צרו מופע של הפרימיטיב PublicKeyVerify עם קבוצת המפתחות, ואמתו את הכותרת GOOGLE-HEALTH-API-SIGNATURE המפוענחת מול המטען הייעודי (payload) הגולמי של ה-webhook בפורמט JSON.
אימות ידני (בלי Tink): אם המפתחים בוחרים לא להשתמש ב-Tink, הם יכולים לאמת את החתימה באופן ידני לפי השלבים הבאים:
- מפענחים את כותרת ה-
GOOGLE-HEALTH-API-SIGNATUREבקידוד Base64 כדי להפריד בין הקידומת של Tink בגודל 5 בייטים (שמכילה קידומת גרסה בגודל בייט אחד ו-keyId בגודל 4 בייטים) לבין החתימה בפועל בקידוד DER. - אחזור של קבוצת המפתחות בפורמט JSON מהכתובת https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
- מאתרים את המפתח שתואם ל-keyId שנותח, ומפענחים את שדה הערך שלו בקידוד Base64, שמכיל מאגר אחסון לפרוטוקולים מסוג EcdsaPublicKey שעבר סריאליזציה.
- מחלצים את הקואורדינטות x ו-y בפורמט Big-endian (תגי Protobuf 3 ו-4) ממטען הייעודי (payload) הבינארי הזה.
- יוצרים מופע של מפתח ציבורי ECDSA P-256 סטנדרטי בספריית קריפטוגרפיה מובנית, באמצעות הקואורדינטות x ו-y שחולצו.
- מאמתים את המטען הייעודי (payload) הגולמי של ה-webhook בפורמט JSON מול החתימה שחולצה בפורמט DER באמצעות אלגוריתם SHA-256.
סטטוס המנוי ושחזור
אם נקודת הקצה של המנוי לא זמינה או מחזירה קוד סטטוס של שגיאה (כל קוד שהוא לא 204), ההתראות בהמתנה נשמרות ב-Google Health API למשך עד 7 ימים, והמערכת מנסה לשלוח אותן שוב עם השהיה מעריכית לפני ניסיון חוזר (exponential backoff).
כשהנקודת הקצה חוזרת לפעול ומגיבה עם 204, ה-API מעביר אוטומטית את ההודעות שנשמרו. התראות שנוצרו לפני יותר מ-7 ימים נמחקות ואי אפשר לשחזר אותן.
שגיאות נפוצות
| קוד שגיאה | שליחת הודעה | תיאור | המלצה |
|---|---|---|---|
| 400 בקשה לא תקינה | מספר הפרויקט בשם המשאב לא תקין | כשמוחקים או מעדכנים מנוי באמצעות מזהה הפרויקט ב-Google Cloud בכתובת ה-URL של הבקשה, במקום מספר הפרויקט. ההגדרה הזו חלה על מינויים ל-webhook שמשתמשים בנקודת הקצה projects.subscribers. |
צריך להשתמש במספר הפרויקט ב-Google Cloud בכתובת ה-URL של הבקשה, ולא במזהה הפרויקט. |
| 403 אסור | לגורם המזמין אין הרשאה | כשיוצרים או מציגים רשימה של מנויים באמצעות מזהה הפרויקט ב-Google Cloud בכתובת ה-URL של הבקשה, במקום מספר הפרויקט. ההגדרה הזו חלה על מינויים ל-webhook שמשתמשים בנקודת הקצה projects.subscribers. |
צריך להשתמש במספר הפרויקט ב-Google Cloud בכתובת ה-URL של הבקשה, ולא במזהה הפרויקט. |