การสมัครใช้บริการเว็บฮุค

Google Health API ช่วยให้แอปพลิเคชันของคุณได้รับการแจ้งเตือนแบบเรียลไทม์เมื่อข้อมูลสุขภาพของผู้ใช้มีการเปลี่ยนแปลง เซิร์ฟเวอร์ของคุณจะได้รับเว็บฮุคคำขอ HTTPS POST ทันทีที่ข้อมูลพร้อมใช้งานใน Google Health API แทนที่จะทำการสำรวจการเปลี่ยนแปลง

ประเภทข้อมูลที่รองรับ

ระบบรองรับการแจ้งเตือน Webhook สำหรับข้อมูลประเภทต่อไปนี้

ตาราง: ประเภทข้อมูล Google Health API ที่รองรับเว็บฮุค
ประเภทข้อมูล การดำเนินการ
ที่ใช้ได้
ขอบเขต
นาทีที่แอ็กทีฟ
dataType: active-minutes
filter parameter: active_minutes
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที

อุปกรณ์ที่เข้ากันได้

  • Fitbit Air
  • Fitbit Alta
  • Fitbit Alta HR
  • Fitbit Blaze
  • Fitbit Charge 2
  • Fitbit Charge 3
  • Fitbit Flex 2
  • Fitbit Inspire
  • Fitbit Inspire HR
  • Pixel Watch 4
list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
นาทีในโซนแอ็กทีฟ
dataType: active-zone-minutes
filter parameter: active_zone_minutes
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที

อุปกรณ์ที่เข้ากันได้

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
ระดับกิจกรรม
dataType: activity-level
filter parameter: activity_level
ประเภทระเบียน: Interval
รายการ, ตรวจสอบยอดคงเหลือ .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Altitude
dataType: altitude
filter parameter: altitude
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที
list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
น้ำตาลกลูโคสในเลือด
dataType: blood-glucose
filter parameter: blood_glucose
ประเภทระเบียน: ตัวอย่าง
list, get, reconcile, rollup, dailyRollup .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
ไขมันร่างกาย
dataType: body-fat
filter 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-zone
filter parameter: calories_in_heart_rate_zone
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที
rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
ความผันแปรของอัตราการเต้นของหัวใจประจำวัน
dataType: daily-heart-rate-variability
filter parameter: daily_heart_rate_variability
ประเภทการบันทึก: รายวัน

อุปกรณ์ที่เข้ากันได้

รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
โซนอัตราการเต้นของหัวใจประจำวัน
dataType: daily-heart-rate-zones
filter parameter: daily_heart_rate_zones
ประเภทการบันทึก: รายวัน
รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
ความอิ่มตัวของออกซิเจนรายวัน
dataType: daily-oxygen-saturation
filter parameter: daily_oxygen_saturation
ประเภทการบันทึก: รายวัน

อุปกรณ์ที่เข้ากันได้

รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
อัตราการหายใจรายวัน
dataType: daily-respiratory-rate
filter parameter: daily_respiratory_rate
ประเภทการบันทึก: รายวัน

อุปกรณ์ที่เข้ากันได้

รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
อัตราการเต้นของหัวใจขณะพักรายวัน
dataType: daily-resting-heart-rate
filter parameter: daily_resting_heart_rate
ประเภทการบันทึก: รายวัน

อุปกรณ์ที่เข้ากันได้

รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
ค่าเบี่ยงเบนของอุณหภูมิขณะนอนหลับรายวัน
dataType: daily-sleep-temperature-derivations
filter parameter: daily_sleep_temperature_derivations
ประเภทการบันทึก: รายวัน

อุปกรณ์ที่เข้ากันได้

รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
ระยะทาง
dataType: distance
filter parameter: distance
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที

อุปกรณ์ที่เข้ากันได้

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
การออกกำลังกาย
dataType: exercise
filter parameter: exercise
ประเภทระเบียน: เซสชัน

อุปกรณ์ที่เข้ากันได้

list, get, reconcile, create, update, batchDelete .activity_and_fitness.readonly
.activity_and_fitness.writeonly
ชั้น
dataType: floors
filter parameter: floors
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที
reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
อัตราการเต้นของหัวใจ
dataType: heart-rate
filter parameter: heart_rate
ประเภทระเบียน: ตัวอย่าง
ความละเอียดของพื้นที่เก็บข้อมูล: 1 วินาที

อุปกรณ์ที่เข้ากันได้

list, reconcile, rollup, dailyRollup .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
ความผันแปรของอัตราการเต้นของหัวใจ
dataType: heart-rate-variability
filter parameter: heart_rate_variability
ประเภทระเบียน: ตัวอย่าง

อุปกรณ์ที่เข้ากันได้

รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
ส่วนสูง
dataType: height
filter parameter: height
ประเภทระเบียน: ตัวอย่าง
list, get, reconcile, create, update, batchDelete .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
บันทึกปริมาณน้ำที่ดื่ม
dataType: hydration-log
filter parameter: hydration_log
ประเภทระเบียน: เซสชัน
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete .nutrition.readonly
.nutrition.writeonly
บันทึกโภชนาการ
dataType: nutrition-log
filter parameter: nutrition_log
ประเภทระเบียน: เซสชัน

อุปกรณ์ที่เข้ากันได้

list, get, reconcile, rollup, dailyRollup, create, update, batchDelete .nutrition.readonly
.nutrition.writeonly
สรุปอัตราการหายใจขณะนอนหลับ
dataType: respiratory-rate-sleep-summary
filter parameter: respiratory_rate_sleep_summary
ประเภทระเบียน: ตัวอย่าง

อุปกรณ์ที่เข้ากันได้

รายการ, ตรวจสอบยอดคงเหลือ .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
ปริมาณการใช้ออกซิเจนสูงสุด (VO2 Max) ขณะวิ่ง
dataType: run-vo2-max
filter parameter: run_vo2_max
ประเภทระเบียน: ตัวอย่าง

อุปกรณ์ที่เข้ากันได้

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
ระยะเวลาที่ไม่มีการเคลื่อนไหว
dataType: sedentary-period
filter parameter: sedentary_period
ประเภทระเบียน: Interval

อุปกรณ์ที่เข้ากันได้

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
การนอนหลับ
dataType: sleep
filter parameter: sleep
ประเภทระเบียน: เซสชัน

อุปกรณ์ที่เข้ากันได้

list, get, reconcile, create, update, batchDelete .sleep.readonly
.sleep.writeonly
ขั้นตอน
dataType: steps
filter parameter: steps
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที

อุปกรณ์ที่เข้ากันได้

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
เวลาในโซนอัตราการเต้นของหัวใจ
dataType: time-in-heart-rate-zone
filter parameter: time_in_heart_rate_zone
ประเภทระเบียน: Interval
ความละเอียดของพื้นที่เก็บข้อมูล: 1 นาที
list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
น้ำหนัก
dataType: weight
filter 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.readonly
    • https://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly
  • ตัวชี้วัดสุขภาพ ซึ่งครอบคลุมประเภทข้อมูลน้ำหนัก
    • https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonly
    • https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly
  • การนอนหลับ ซึ่งครอบคลุมประเภทข้อมูลการนอนหลับ ดังนี้
    • https://www.googleapis.com/auth/googlehealth.sleep.readonly
    • https://www.googleapis.com/auth/googlehealth.sleep.writeonly

บัญชีบริการ IAM

แม้ว่าจะไม่จำเป็น แต่เราขอแนะนำให้ใช้บัญชีบริการ IAM เมื่อกำหนดค่าผู้สมัครใช้บริการสำหรับ Google Health API บัญชีบริการช่วยให้ภาระงานของแอปพลิเคชันมีความปลอดภัยมากกว่าบัญชีผู้ใช้มาตรฐานผ่านฟีเจอร์ต่อไปนี้

  • ข้อมูลเข้าสู่ระบบแบบอายุสั้นอัตโนมัติ: เมื่อเชื่อมต่อกับสภาพแวดล้อมการดำเนินการของ Google Cloud (เช่น Compute Engine, Cloud Run หรือ Google Kubernetes Engine) บัญชีบริการจะได้รับและหมุนเวียนข้อมูลเข้าสู่ระบบแบบอายุสั้นที่ปลอดภัยโดยอัตโนมัติ ซึ่งจะช่วยลดความเสี่ยงในการจัดการและจัดเก็บคีย์แบบคงที่ถาวร
  • หลักการให้สิทธิ์ขั้นต่ำที่สุด: บัญชีบริการจะให้ข้อมูลประจำตัวเฉพาะสำหรับเวิร์กโหลด คุณสามารถให้สิทธิ์เฉพาะที่จำเป็นในการจัดการปลายทางของผู้สมัครใช้บริการเท่านั้น เพื่อหลีกเลี่ยงการเข้าถึงทรัพยากร Google Cloud ในวงกว้าง
  • ความเป็นอิสระของวงจร: บัญชีบริการทำงานแยกต่างหากจากบัญชีของผู้ใช้แต่ละราย เพื่อให้มั่นใจว่าการเปลี่ยนแปลงบุคลากรจะไม่ส่งผลต่อความเสถียรของการตรวจสอบสิทธิ์ในระยะยาว

ตั้งค่าบัญชีบริการ

วิธีกำหนดค่าแอปพลิเคชันผู้สมัครใช้บริการเพื่อตรวจสอบสิทธิ์โดยใช้บัญชีบริการมีดังนี้

  1. สร้างบัญชีบริการ: ในคอนโซล Google Cloud ให้ไปที่หน้า IAM และผู้ดูแลระบบของโปรเจ็กต์เพื่อสร้างบัญชีบริการใหม่ที่จัดการโดยผู้ใช้
  2. มอบบทบาท IAM ที่จำเป็น: มอบหมายบทบาทที่เหมาะสมให้กับบัญชีบริการที่จำเป็นในการจัดการผู้สมัครใช้บริการในโปรเจ็กต์ที่อยู่ในระบบคลาวด์ Google Cloud
  3. แนบบัญชีบริการกับภาระงาน: กำหนดค่าสภาพแวดล้อมที่โฮสต์ตรรกะของผู้สมัครใช้บริการให้ทำงานเป็นบัญชีบริการใหม่ ซึ่งจะช่วยให้โค้ดของแอปพลิเคชัน (เช่น ไลบรารีของไคลเอ็นต์ Google API) ตรวจหาและใช้ข้อมูลเข้าสู่ระบบที่มีอายุสั้นของบัญชีบริการโดยอัตโนมัติเมื่อเรียกใช้ projects.subscribers REST API

บทบาท CPE

หากต้องการจัดการผู้สมัครใช้บริการหรือการสมัครใช้บริการ Google Health API คุณต้องให้สิทธิ์บทบาทที่เหมาะสมแก่บัญชีบริการที่แอบอ้างเป็นบุคคลอื่นซึ่งทำการเรียก API มอบหมายบทบาทใดบทบาทหนึ่งต่อไปนี้ โดยขึ้นอยู่กับระดับการเข้าถึงที่ต้องการ

  • Google Health API Read
  • ผู้แก้ไข Google Health API
  • ผู้ดูแลระบบ Google Health API

ดูข้อมูลเพิ่มเติมเกี่ยวกับบทบาทและสิทธิ์ IAM ของ Google Health API

จัดการผู้ติดตาม

ก่อนที่จะรับการแจ้งเตือนได้ คุณต้องลงทะเบียน Subscriber ซึ่งแสดงถึงปลายทางการแจ้งเตือนของแอปพลิเคชัน คุณจัดการผู้สมัครใช้บริการได้โดยใช้ 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 เช่น คุณสามารถตั้งค่า 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 ต้องมีรายการชื่อช่องที่คั่นด้วยคอมมาซึ่งคุณต้องการอัปเดต โดยใช้การเขียนแบบ Camel Case สำหรับชื่อช่อง (เช่น 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 จะทำการแฮนด์เชคเพื่อการยืนยันแบบ 2 ขั้นตอนที่จำเป็นทุกครั้งที่คุณสร้างผู้สมัครใช้บริการหรืออัปเดตการกำหนดค่าปลายทาง (endpointUri หรือ endpointAuthorization) กระบวนการนี้จะดำเนินการพร้อมกันระหว่างการเรียก API บริการจะส่งคำขอ POST อัตโนมัติ 2 รายการไปยัง URI ปลายทางของคุณโดยใช้ User Agent Google-Health-API-Webhooks พร้อมเนื้อหา JSON {"type": "verification"}

  • Authorized Handshake: ระบบจะส่งคำขอแรกพร้อมส่วนหัว Authorization ที่คุณกำหนดค่าไว้ เซิร์ฟเวอร์ต้องตอบกลับด้วยสถานะ 200 OK หรือ 201 Created
  • การตรวจสอบสิทธิ์ที่ไม่ได้รับอนุญาต: ระบบจะส่งคำขอที่ 2 โดยไม่มีข้อมูลเข้าสู่ระบบ เซิร์ฟเวอร์ต้องตอบสนองด้วยสถานะ 401 Unauthorized หรือ 403 Forbidden

การแฮนด์เชคนี้เป็นการยืนยันว่าปลายทางของคุณทำงานอยู่และบังคับใช้การรักษาความปลอดภัยอย่างถูกต้อง หากขั้นตอนใดขั้นตอนหนึ่งไม่สำเร็จ คำขอ API จะไม่สำเร็จพร้อมข้อผิดพลาด FAILED_PRECONDITION ระบบจะบันทึกและเปิดใช้งานผู้สมัครใช้บริการเพื่อรับการแจ้งเตือนข้อมูลสุขภาพหลังจากที่การแฮนด์เชคนี้สำเร็จเท่านั้น

การหมุนเวียนคีย์

หากต้องการหมุนเวียนคีย์สำหรับ endpointAuthorization ให้ทำตามขั้นตอนต่อไปนี้

  1. กำหนดค่าปลายทางให้ยอมรับทั้งค่า endpointAuthorization เก่าและใหม่
  2. อัปเดตการกำหนดค่าผู้ติดตามด้วยค่า endpointAuthorization ใหม่โดยใช้คำขอ patch ที่มี ?updateMask=endpointAuthorization
  3. กำหนดค่าปลายทางให้ยอมรับเฉพาะค่า endpointAuthorization ใหม่หลังจากยืนยันว่าขั้นตอนที่ 2 สำเร็จแล้ว

การสมัครใช้บริการของผู้ใช้

Google Health API ช่วยให้คุณจัดการการสมัครใช้บริการของผู้ใช้ได้อย่างมีประสิทธิภาพ ซึ่งช่วยลดความจำเป็นในการลงทะเบียนด้วยตนเองระหว่างการเตรียมความพร้อมผู้ใช้งานใหม่

การสมัครใช้บริการอัตโนมัติ

เราขอแนะนำให้ใช้การสมัครใช้บริการอัตโนมัติ หากต้องการเปิดใช้ฟีเจอร์นี้ ให้ตั้งค่า subscriptionCreatePolicy เป็น AUTOMATIC ใน subscriberConfigs สำหรับประเภทข้อมูลที่เฉพาะเจาะจง dataTypes ที่คุณระบุด้วยนโยบาย AUTOMATIC เป็นข้อมูลประเภทเดียวกับที่ Google Health API ส่งการแจ้งเตือน โดยต้องได้รับความยินยอมจากผู้ใช้สำหรับข้อมูลประเภทดังกล่าวด้วย

เมื่อผู้ใช้ให้ความยินยอมแก่แอปพลิเคชันสำหรับขอบเขตที่สอดคล้องกับประเภทข้อมูลที่มีนโยบาย AUTOMATIC Google Health API จะติดตามและส่งการแจ้งเตือนสำหรับประเภทข้อมูลที่เกิดจากการตัดกันระหว่างประเภทข้อมูลที่ผู้ใช้ให้ความยินยอมและประเภทข้อมูลการกำหนดค่าผู้สมัครใช้บริการอัตโนมัติสำหรับผู้ใช้รายนั้นโดยอัตโนมัติ จากนั้นระบบจะส่งการแจ้งเตือนไปยังปลายทางของคุณเมื่อใดก็ตามที่ผู้ใช้สร้างข้อมูลใหม่สำหรับประเภทเหล่านั้น ซึ่งจะใช้ได้กับผู้ใช้ที่ให้ความยินยอมก่อนหรือหลังจากที่คุณสร้างผู้สมัครใช้บริการ ระบบจะไม่ทดแทนข้อมูลการแจ้งเตือนที่สร้างขึ้นก่อนที่จะสร้างผู้สมัครใช้บริการ

หากผู้ใช้เพิกถอนความยินยอม การแจ้งเตือนสำหรับประเภทข้อมูลที่เกี่ยวข้องจะหยุดลง การสมัครใช้บริการอัตโนมัติจะจัดการโดย Google และไม่สามารถแสดงหรือลบทีละรายการได้ โดยจะนำออกเมื่อมีการลบผู้สมัครใช้บริการหลักเท่านั้น

การสมัครใช้บริการด้วยตนเอง

หากผู้สมัครใช้บริการของคุณได้รับการกำหนดค่าด้วย subscription_create_policy แบบ MANUAL สำหรับประเภทข้อมูลที่เฉพาะเจาะจง คุณต้องสร้างและจัดการการสมัครใช้บริการ สำหรับผู้ใช้แต่ละรายอย่างชัดเจน การสมัครใช้บริการจะลิงก์ผู้ใช้ที่เฉพาะเจาะจงกับปลายทางของผู้สมัครใช้บริการสำหรับชุดประเภทข้อมูลที่กำหนด นักพัฒนาแอปสามารถใช้ API ที่เฉพาะเจาะจงเพื่อดำเนินการต่อไปนี้

  • สร้างการสมัครใช้บริการ (ด้วยตนเอง) ต่อ healthUserId - สร้างการสมัครใช้บริการใหม่สำหรับผู้ใช้ที่ระบุ วิธีนี้กำหนดให้ผู้สมัครใช้บริการต้องตั้งค่า SubscriptionCreatePolicy เป็น MANUAL สำหรับประเภทข้อมูลที่ขอ
  • อัปเดตการสมัครใช้บริการ (ด้วยตนเอง) - อัปเดตประเภทข้อมูลสำหรับการสมัครใช้บริการของผู้ใช้ที่มีอยู่
  • ลบการสมัครใช้บริการ (ด้วยตนเอง) - ลบการสมัครใช้บริการของผู้ใช้ที่ระบุ เมื่อลบแล้ว ปลายทางของผู้สมัครใช้บริการจะไม่ได้รับการแจ้งเตือนสำหรับผู้ใช้รายนี้สำหรับประเภทข้อมูลที่เกี่ยวข้องอีกต่อไป
  • แสดงรายการการสมัครใช้บริการ (ด้วยตนเอง) - แสดงรายการการสมัครใช้บริการที่ใช้งานอยู่ทั้งหมดสำหรับผู้สมัครใช้บริการที่ระบุ คุณสามารถกรองผลลัพธ์ตามผู้ใช้หรือประเภทข้อมูลได้

การแจ้งเตือน

เมื่อข้อมูลของผู้ใช้มีการเปลี่ยนแปลงสำหรับประเภทข้อมูลที่สมัครใช้บริการ 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"
  }
}

เป็นกลุ่ม (ผู้ใช้หลายคนและประเภทข้อมูลหลายรายการ)

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

เราขอแนะนำให้ทำให้ตรรกะการจัดการการแจ้งเตือนเป็นแบบ Idempotent โดยเฉพาะสำหรับการดำเนินการ 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 มีออบเจ็กต์อย่างน้อย 1 รายการที่แสดงช่วงเวลาสำหรับข้อมูลที่แก้ไข

ช่อง ประเภท ต้องระบุ คำอธิบาย
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 ที่มีออบเจ็กต์การแจ้งเตือนหลายรายการ โดยกลุ่มเดียวสามารถรวมการอัปเดตสำหรับผู้ใช้หลายรายในข้อมูลที่สมัครใช้บริการหลายประเภท ดูตัวอย่างเพย์โหลดได้ที่ตัวอย่างBatch (ผู้ใช้หลายคนและประเภทข้อมูลหลายรายการ) ในส่วนรูปแบบการแจ้งเตือน

ตอบกลับการแจ้งเตือน

เซิร์ฟเวอร์ของคุณต้องตรวจสอบส่วนหัว 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 พร้อมกับ filter สำหรับช่วงเวลาของช่วงเวลาดังกล่าว ซึ่งจะกรองช่วงเวลาที่ทับซ้อนกันจากอุปกรณ์ที่เชื่อมต่อออกโดยอัตโนมัติ ดูรายละเอียดและตัวอย่างได้ที่ดูมุมมองที่ปรับยอดแล้วของข้อมูลช่วงเวลา

กลยุทธ์ที่ใช้กันทั่วไปในการอัปเดตฐานข้อมูลหลังจากได้รับการแจ้งเตือนการเปลี่ยนแปลงข้อมูลมี 3 กลยุทธ์ดังนี้

  • การจัดคิว (แนะนำ): วางการแจ้งเตือนขาเข้าไว้ในคิวข้อความและประมวลผลการแจ้งเตือนเหล่านั้นนอกคิวตามทรัพยากรระบบ
  • การดึงข้อมูลทันที: สั่งให้แบ็กเอนด์เรียกใช้ Google Health API ทันทีเพื่อดึงข้อมูลที่อัปเดตและบันทึกลงในฐานข้อมูล
  • การอัปเดตแบบ Lazy: ทำเครื่องหมายระเบียนฐานข้อมูลในเครื่องของผู้ใช้ว่าไม่ถูกต้อง และดึงข้อมูลที่อัปเดตจาก Google Health API เมื่อผู้ใช้เข้าชมแอปพลิเคชันของคุณในครั้งถัดไปเท่านั้น

กลยุทธ์การจัดคิวช่วยให้คุณประมวลผลข้อมูลได้แบบเกือบเรียลไทม์ ขณะเดียวกันก็ยังคงยืดหยุ่นต่อการแจ้งเตือนจำนวนมาก (เช่น หากการหยุดทำงานของบริการทำให้การแจ้งเตือนที่ค้างอยู่ถูกส่งพร้อมกันทั้งหมด) แม้ว่าคุณจะเลือกประมวลผลคิวเหล่านี้ตามกำหนดการแบบกลุ่มที่มีช่วงเวลายาวนาน (เช่น ทุก 3 ชั่วโมง) เพื่อประหยัดทรัพยากรได้ แต่ข้อเสียคือโดยปกติแล้วผู้ใช้คาดหวังว่าจะเห็นข้อมูลอัปเดตแบบเกือบเรียลไทม์

แนวทางปฏิบัติแนะนำ

ทำตามแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อให้มั่นใจว่าการจัดการการอัปเดตข้อมูลเป็นไปอย่างน่าเชื่อถือและมีประสิทธิภาพ

  • ใช้ Subscription API: ใช้ Subscription API เพื่อรับการแจ้งเตือนเมื่อผู้ใช้มีข้อมูลใหม่ให้ดาวน์โหลด แทนที่จะใช้ปลายทางแบบโพลลิ่ง
  • จัดคิวและประมวลผลการแจ้งเตือนแยกกัน: เมื่อได้รับการแจ้งเตือนการสมัครใช้บริการ ให้จัดคิวและประมวลผลการแจ้งเตือนแยกกันตามทรัพยากรของระบบ ทำให้ปลายทางเว็บฮุคมีขนาดเล็กโดยการรับทราบคำขอทันที

การยืนยันลายเซ็น

เพื่อให้มั่นใจว่าการแจ้งเตือน Webhook เป็นของแท้ เพย์โหลด 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 ที่ถอดรหัสแล้วกับเพย์โหลด JSON ของเว็บฮุคแบบดิบ

การยืนยันด้วยตนเอง (โดยไม่ใช้ Tink): หากนักพัฒนาแอปเลือกที่จะไม่ใช้ Tink นักพัฒนาแอปจะยืนยันลายเซ็นด้วยตนเองได้โดยทำตามขั้นตอนต่อไปนี้

  1. ถอดรหัส Base64 ส่วนหัว GOOGLE-HEALTH-API-SIGNATURE เพื่อแยกคำนำหน้า Tink ขนาด 5 ไบต์ (ซึ่งมีคำนำหน้าเวอร์ชันขนาด 1 ไบต์และ keyId ขนาด 4 ไบต์) ออกจากลายเซ็นที่เข้ารหัส DER จริง
  2. ดึงข้อมูลชุดคีย์ JSON จาก https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json
  3. ค้นหาคีย์ที่ตรงกับ keyId ที่แยกวิเคราะห์แล้ว และถอดรหัส Base64 ในช่องค่า ซึ่งมีบัฟเฟอร์โปรโตคอล EcdsaPublicKey ที่เรียงลำดับแล้ว
  4. ดึงข้อมูลพิกัด x และ y แบบ Big-Endian (แท็ก Protobuf 3 และ 4) จากเพย์โหลดไบนารีนี้
  5. สร้างอินสแตนซ์คีย์สาธารณะ ECDSA P-256 มาตรฐานในไลบรารีวิทยาการเข้ารหัสในตัวโดยใช้พิกัด x และ y ที่ดึงมา
  6. ยืนยันเพย์โหลด JSON ของเว็บฮุกดิบกับลายเซ็น DER ที่ดึงมาโดยใช้อัลกอริทึม SHA-256

สถานะผู้ติดตามและการกู้คืน

หากปลายทางของผู้สมัครใช้บริการไม่พร้อมใช้งานหรือแสดงรหัสสถานะข้อผิดพลาด (รหัสอื่นที่ไม่ใช่ 204) Google Health API จะจัดเก็บการแจ้งเตือนที่รอดำเนินการไว้เป็นเวลาสูงสุด 7 วัน และลองนำส่งอีกครั้งด้วย Exponential Backoff

เมื่อปลายทางกลับมาออนไลน์และตอบกลับด้วย 204 แล้ว API จะส่งข้อความที่ค้างอยู่ซึ่งจัดเก็บไว้โดยอัตโนมัติ ระบบจะทิ้งการแจ้งเตือนที่เก่ากว่า 7 วันและกู้คืนไม่ได้

ข้อผิดพลาดที่พบบ่อย

รหัสข้อผิดพลาด ข้อความ คำอธิบาย คำแนะนำ
400 คำขอไม่ถูกต้อง หมายเลขโปรเจ็กต์ในชื่อทรัพยากรไม่ถูกต้อง เมื่อลบหรืออัปเดตผู้สมัครใช้บริการโดยใช้รหัสโปรเจ็กต์ที่อยู่ในระบบคลาวด์ของ Google ใน URL คำขอแทนหมายเลขโปรเจ็กต์ ซึ่งจะมีผลกับการสมัครใช้บริการเว็บฮุคที่ใช้ปลายทาง projects.subscribers ใช้หมายเลขโปรเจ็กต์ Google Cloud ใน URL คำขอ ไม่ใช่รหัสโปรเจ็กต์
403 ต้องห้าม ผู้โทรไม่มีสิทธิ์ เมื่อสร้างหรือแสดงรายชื่อผู้สมัครใช้บริการโดยใช้รหัสโปรเจ็กต์ Google Cloud ใน URL คำขอแทนหมายเลขโปรเจ็กต์ ซึ่งจะมีผลกับการสมัครใช้บริการเว็บฮุคที่ใช้ปลายทาง projects.subscribers ใช้หมายเลขโปรเจ็กต์ Google Cloud ใน URL คำขอ ไม่ใช่รหัสโปรเจ็กต์