Langganan webhook

Google Health API memungkinkan aplikasi Anda menerima notifikasi real-time saat data kesehatan pengguna berubah. Alih-alih melakukan polling untuk perubahan, server Anda menerima webhook permintaan POST HTTPS segera setelah data tersedia di Google Health API.

Jenis data yang didukung

Notifikasi webhook didukung untuk jenis data berikut:

Tabel: Jenis data Google Health API dengan dukungan webhook
Jenis data Operasi
yang tersedia
Cakupan
Menit Aktif
dataType: active-minutes
filter parameter: active_minutes
Jenis data: Interval
Resolusi penyimpanan: 1 menit

Perangkat yang kompatibel

  • 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
Menit Zona Aktif
dataType: active-zone-minutes
filter parameter: active_zone_minutes
Jenis data: Interval
Resolusi penyimpanan: 1 menit

Perangkat yang kompatibel

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Tingkat Aktivitas
dataType: activity-level
filter parameter: activity_level
Jenis data: Interval
daftar, rekonsiliasi .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Altitude
dataType: altitude
filter parameter: altitude
Jenis data: Interval
Resolusi penyimpanan: 1 menit
list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Glukosa Darah
dataType: blood-glucose
filter parameter: blood_glucose
Jenis data: Sample
list, get, reconcile, rollup, dailyRollup .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Lemak Tubuh
dataType: body-fat
filter parameter: body_fat
Jenis data: Sample

Perangkat yang kompatibel

list, get, reconcile, rollup, dailyRollup, create, update, batchDelete .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Kalori dalam Zona Detak Jantung
dataType: calories-in-heart-rate-zone
filter parameter: calories_in_heart_rate_zone
Jenis data: Interval
Resolusi penyimpanan: 1 menit
rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Variabilitas Detak Jantung Harian
dataType: daily-heart-rate-variability
filter parameter: daily_heart_rate_variability
Jenis data: Harian

Perangkat yang kompatibel

daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Zona Detak Jantung Harian
dataType: daily-heart-rate-zones
filter parameter: daily_heart_rate_zones
Jenis data: Harian
daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Saturasi Oksigen Harian
dataType: daily-oxygen-saturation
filter parameter: daily_oxygen_saturation
Jenis data: Harian

Perangkat yang kompatibel

daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Laju Pernapasan Harian
dataType: daily-respiratory-rate
filter parameter: daily_respiratory_rate
Jenis data: Harian

Perangkat yang kompatibel

daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Detak Jantung Saat Istirahat Harian
dataType: daily-resting-heart-rate
filter parameter: daily_resting_heart_rate
Jenis data: Harian

Perangkat yang kompatibel

daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Deviasi Suhu Tidur Harian
dataType: daily-sleep-temperature-derivations
filter parameter: daily_sleep_temperature_derivations
Jenis data: Harian

Perangkat yang kompatibel

daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Jarak
dataType: distance
filter parameter: distance
Jenis data: Interval
Resolusi penyimpanan: 1 menit

Perangkat yang kompatibel

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Latihan
dataType: exercise
filter parameter: exercise
Jenis data: Sesi

Perangkat yang kompatibel

list, get, reconcile, create, update, batchDelete .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Lantai
dataType: floors
filter parameter: floors
Jenis data: Interval
Resolusi penyimpanan: 1 menit
reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Detak Jantung
dataType: heart-rate
filter parameter: heart_rate
Jenis data: Sample
Resolusi penyimpanan: 1 detik

Perangkat yang kompatibel

list, reconcile, rollup, dailyRollup .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Variabilitas Detak Jantung
dataType: heart-rate-variability
filter parameter: heart_rate_variability
Jenis data: Sample

Perangkat yang kompatibel

daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Tinggi
dataType: height
filter parameter: height
Jenis data: Sample
list, get, reconcile, create, update, batchDelete .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
Catatan Hidrasi
dataType: hydration-log
filter parameter: hydration_log
Jenis data: Sesi
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete .nutrition.readonly
.nutrition.writeonly
Catatan Gizi
dataType: nutrition-log
filter parameter: nutrition_log
Jenis data: Sesi

Perangkat yang kompatibel

list, get, reconcile, rollup, dailyRollup, create, update, batchDelete .nutrition.readonly
.nutrition.writeonly
Ringkasan data tidur laju pernapasan
dataType: respiratory-rate-sleep-summary
filter parameter: respiratory_rate_sleep_summary
Jenis data: Sample

Perangkat yang kompatibel

daftar, rekonsiliasi .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly
VO2 Maks Lari
dataType: run-vo2-max
filter parameter: run_vo2_max
Jenis data: Sample

Perangkat yang kompatibel

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Periode Tidak Bergerak
dataType: sedentary-period
filter parameter: sedentary_period
Jenis data: Interval

Perangkat yang kompatibel

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Tidur
dataType: sleep
filter parameter: sleep
Jenis data: Sesi

Perangkat yang kompatibel

list, get, reconcile, create, update, batchDelete .sleep.readonly
.sleep.writeonly
Langkah
dataType: steps
filter parameter: steps
Jenis data: Interval
Resolusi penyimpanan: 1 menit

Perangkat yang kompatibel

list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Waktu di Zona Detak Jantung
dataType: time-in-heart-rate-zone
filter parameter: time_in_heart_rate_zone
Jenis data: Interval
Resolusi penyimpanan: 1 menit
list, reconcile, rollup, dailyRollup .activity_and_fitness.readonly
.activity_and_fitness.writeonly
Berat
dataType: weight
filter parameter: weight
Jenis data: Sample

Perangkat yang kompatibel

list, get, reconcile, rollup, dailyRollup, create, update, batchDelete .health_metrics_and_measurements.readonly
.health_metrics_and_measurements.writeonly

Cakupan notifikasi

Notifikasi dikirim untuk jenis data ini hanya jika pengguna telah memberikan izin untuk salah satu cakupan yang sesuai:

  • Aktivitas, yang mencakup jenis data menit aktif, langkah, ketinggian, jarak, dan lantai:
    • https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly
    • https://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly
  • Metrik Kesehatan, yang mencakup jenis data berat:
    • https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonly
    • https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly
  • Tidur, yang mencakup jenis data tidur:
    • https://www.googleapis.com/auth/googlehealth.sleep.readonly
    • https://www.googleapis.com/auth/googlehealth.sleep.writeonly

Akun Layanan IAM

Meskipun tidak wajib, sebaiknya gunakan Akun Layanan IAM saat mengonfigurasi pelanggan untuk Google Health API. Akun layanan memberikan keamanan yang lebih baik untuk workload aplikasi dibandingkan dengan akun pengguna standar melalui fitur berikut:

  • Kredensial berjangka pendek otomatis: Saat dilampirkan ke lingkungan eksekusi Google Cloud (seperti Compute Engine, Cloud Run, atau Google Kubernetes Engine), akun layanan akan otomatis mendapatkan dan merotasi kredensial berjangka pendek yang aman. Hal ini menghilangkan risiko pengelolaan dan penyimpanan kunci statis persisten.
  • Prinsip hak istimewa terendah: Akun layanan menyediakan identitas khusus untuk workload. Anda dapat memberi mereka hanya izin khusus yang diperlukan untuk mengelola endpoint pelanggan, sehingga menghindari akses yang lebih luas ke resource Google Cloud Anda.
  • Kemandirian siklus proses: Akun layanan beroperasi secara independen dari akun pengguna perorangan, sehingga memastikan bahwa perubahan personel tidak memengaruhi stabilitas autentikasi jangka panjang.

Menyiapkan akun layanan

Untuk mengonfigurasi aplikasi pelanggan agar melakukan autentikasi menggunakan akun layanan:

  1. Buat akun layanan: Di konsol Google Cloud, buka halaman IAM & Admin project Anda untuk membuat akun layanan terkelola pengguna baru.
  2. Berikan peran IAM yang diperlukan: Tetapkan peran yang sesuai ke akun layanan yang diperlukan untuk mengelola pelanggan di project Google Cloud Anda.
  3. Lampirkan akun layanan ke workload Anda: Konfigurasikan lingkungan yang menghosting logika pelanggan Anda untuk dijalankan sebagai akun layanan baru. Hal ini memungkinkan kode aplikasi Anda (seperti library klien Google API) untuk otomatis mendeteksi dan menggunakan kredensial akun layanan yang berumur singkat saat memanggil projects.subscribers REST API.

Peran CPE

Untuk mengelola Pelanggan atau Langganan Google Health API, Anda harus memberikan peran yang sesuai kepada Akun Layanan yang ditiru identitasnya yang melakukan panggilan API. Bergantung pada tingkat akses yang diperlukan, tetapkan salah satu peran berikut:

  • Google Health API Read
  • Editor Google Health API
  • Admin Google Health API

Pelajari lebih lanjut peran dan izin IAM Google Health API.

Mengelola subscriber

Sebelum dapat menerima notifikasi, Anda harus mendaftarkan Subscriber, yang mewakili endpoint notifikasi aplikasi Anda. Anda dapat mengelola pelanggan menggunakan REST API yang tersedia di projects.subscribers.

Endpoint pelanggan Anda harus menggunakan HTTPS (TLSv1.2+) dan dapat diakses secara publik. Selama pembuatan dan pembaruan pelanggan, Google Health API melakukan tantangan verifikasi untuk memastikan Anda memiliki URI endpoint. Jika verifikasi gagal, operasi pembuatan dan pembaruan pelanggan akan gagal dengan FailedPreconditionException.

Membuat pelanggan

Untuk mendaftarkan pelanggan baru untuk project Anda, gunakan endpoint create. Anda harus memberikan:

  • project-id: Nomor project tempat akun layanan webhook dibuat.
  • subscriberId: ID unik yang Anda berikan untuk pelanggan. ID ini harus terdiri dari 4 hingga 36 karakter, dan cocok dengan ekspresi reguler ([a-z]([a-z0-9-]{2,34}[a-z0-9])).
  • endpointUri: URL tujuan untuk notifikasi webhook.
  • subscriberConfigs: Jenis data yang ingin Anda terima notifikasinya, dan kebijakan langganan untuk masing-masing jenis data.
  • endpointAuthorization: Mekanisme otorisasi untuk endpoint Anda. Ini harus berisi secret yang Anda berikan. Nilai secret dikirim di header Authorization dengan setiap pesan notifikasi. Anda dapat menggunakan token ini untuk memverifikasi bahwa permintaan masuk berasal dari Google Health API. Misalnya, Anda dapat menetapkan secret ke Bearer R4nd0m5tr1ng123 untuk autentikasi Bearer, atau Basic dXNlcjpwYXNzd29yZA== untuk autentikasi Basic.

Di subscriberConfigs, Anda harus menetapkan subscriptionCreatePolicy untuk setiap jenis data. Tetapkan ke AUTOMATIC untuk menggunakan langganan otomatis, atau MANUAL jika Anda bermaksud mengelola langganan pengguna sendiri. Lihat langganan otomatis dan langganan manual untuk mengetahui detail selengkapnya tentang setiap opsi.

Permintaan

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"
  }
}

Respons

{
  "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"
    }
  ]
}

Mencantumkan pelanggan

Gunakan endpoint list untuk mengambil semua pelanggan yang terdaftar untuk project Anda.

Permintaan

GET https://health.googleapis.com/v4/projects/project-id/subscribers

Respons

{
  "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
}

Memperbarui pelanggan

Gunakan endpoint patch untuk memperbarui pelanggan di project Anda. Kolom yang dapat diperbarui adalah endpointUri, subscriberConfigs, dan endpointAuthorization.

Anda memperbarui kolom dengan memberikan parameter kueri updateMask dan isi permintaan. updateMask harus berisi daftar nama kolom yang dipisahkan koma yang ingin Anda perbarui, menggunakan camel case untuk nama kolom (misalnya, endpointUri). Isi permintaan harus berisi objek Subscriber parsial dengan nilai baru untuk kolom yang ingin Anda perbarui. Hanya kolom yang ditentukan di updateMask yang diperbarui. Jika Anda memberikan kolom dalam isi permintaan yang tidak ada di updateMask, kolom tersebut akan diabaikan.

Jika Anda memperbarui endpointUri atau endpointAuthorization, verifikasi endpoint akan dilakukan. Lihat Verifikasi endpoint untuk mengetahui detailnya.

Saat memperbarui subscriberConfigs, perhatikan bahwa ini adalah penggantian penuh, bukan penggabungan. Jika subscriberConfigs disertakan dalam updateMask, semua konfigurasi yang disimpan untuk pelanggan tersebut akan ditimpa dengan daftar yang disediakan dalam isi permintaan. Untuk menambahkan atau menghapus konfigurasi, Anda harus menyediakan kumpulan konfigurasi lengkap. Jika Anda mengupdate kolom lain dan ingin mempertahankan konfigurasi saat ini, hapus subscriberConfigs dari updateMask.

Permintaan

PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id?updateMask=endpointUri
{
  "endpointUri": "https://myapp.com/new-webhooks/health"
}

Respons

{
  "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"
    }
  ]
}

Menghapus pelanggan

Gunakan endpoint delete untuk menghapus pelanggan dari project Anda. Setelah dihapus, pelanggan tidak akan lagi menerima notifikasi.

Permintaan

DELETE https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id

Respons

Isi respons kosong dengan status HTTP `200 OK` akan ditampilkan jika penghapusan berhasil.
{}

Verifikasi endpoint

Untuk memastikan keamanan dan keandalan pengiriman notifikasi, Google Health API melakukan handshake verifikasi dua langkah wajib setiap kali Anda membuat pelanggan atau memperbarui konfigurasi endpoint-nya (endpointUri atau endpointAuthorization). Proses ini dilakukan secara sinkron selama panggilan API. Layanan ini mengirimkan dua permintaan POST otomatis ke URI endpoint Anda, menggunakan Agen Pengguna Google-Health-API-Webhooks, dengan isi JSON {"type": "verification"}.

  • Handshake yang Diotorisasi: Permintaan pertama dikirim dengan header Authorization yang Anda konfigurasi. Server Anda harus merespons dengan status 200 OK atau 201 Created.
  • Unauthorized Challenge: Permintaan kedua dikirim tanpa kredensial. Server Anda harus merespons dengan status 401 Unauthorized atau 403 Forbidden.

Handshake ini mengonfirmasi bahwa endpoint Anda aktif dan menerapkan keamanan dengan benar. Jika salah satu langkah gagal, permintaan API akan gagal dengan error FAILED_PRECONDITION. Hanya setelah handshake ini berhasil, pelanggan Anda akan disimpan dan diaktifkan untuk menerima notifikasi data kesehatan.

Rotasi kunci

Jika Anda perlu merotasi kunci untuk endpointAuthorization, ikuti langkah-langkah berikut:

  1. Konfigurasikan endpoint Anda untuk menerima nilai endpointAuthorization lama dan baru.
  2. Perbarui konfigurasi pelanggan dengan nilai endpointAuthorization baru menggunakan permintaan patch dengan ?updateMask=endpointAuthorization.
  3. Konfigurasikan endpoint Anda agar hanya menerima nilai endpointAuthorization baru setelah mengonfirmasi bahwa langkah 2 berhasil.

Langganan pengguna

Google Health API membantu Anda mengelola langganan pengguna secara efisien, sehingga mengurangi kebutuhan pendaftaran manual selama orientasi pengguna.

Langganan otomatis

Sebaiknya gunakan langganan otomatis. Untuk mengaktifkan fitur ini, tetapkan subscriptionCreatePolicy ke AUTOMATIC di subscriberConfigs untuk jenis data tertentu. dataTypes yang Anda tentukan dengan kebijakan AUTOMATIC adalah jenis data yang sama dengan yang dikirimi notifikasi oleh Google Health API, asalkan izin pengguna juga diberikan untuk jenis data tersebut.

Saat pengguna memberikan izin aplikasi untuk cakupan yang sesuai dengan jenis data dengan kebijakan AUTOMATIC, Google Health API akan otomatis melacak dan mengirimkan notifikasi untuk jenis data yang dihasilkan dari persimpangan antara jenis data yang diizinkan pengguna dan jenis data konfigurasi pelanggan otomatis untuk pengguna tersebut. Notifikasi kemudian dikirim ke endpoint Anda setiap kali pengguna tersebut membuat data baru untuk jenis tersebut. Hal ini berlaku untuk pengguna yang memberikan izin sebelum atau setelah Anda membuat pelanggan. Notifikasi tidak diisi ulang untuk data yang dihasilkan sebelum pelanggan dibuat.

Jika pengguna mencabut izin, notifikasi untuk jenis data yang sesuai akan berhenti. Langganan otomatis dikelola oleh Google dan tidak dapat dicantumkan atau dihapus satu per satu; langganan tersebut hanya dihapus jika pelanggan induk dihapus.

Langganan manual

Jika pelanggan Anda dikonfigurasi dengan subscription_create_policy MANUAL untuk jenis data tertentu, Anda harus membuat dan mengelola langganan secara eksplisit untuk setiap pengguna. Langganan menautkan pengguna tertentu ke endpoint pelanggan Anda untuk kumpulan jenis data yang ditentukan. Developer dapat menggunakan API tertentu untuk:

  • Buat langganan (manual) per healthUserId - Membuat langganan baru untuk pengguna tertentu. Metode ini mengharuskan pelanggan memiliki SubscriptionCreatePolicy yang ditetapkan ke MANUAL untuk jenis data yang diminta.
  • Memperbarui langganan (manual) - Memperbarui jenis data untuk langganan pengguna yang ada.
  • Menghapus langganan (manual) - Menghapus langganan pengguna tertentu. Setelah dihapus, endpoint pelanggan Anda tidak akan lagi menerima notifikasi untuk pengguna ini untuk jenis data terkait.
  • Mencantumkan langganan (manual) - Mencantumkan semua langganan aktif untuk pelanggan tertentu. Anda dapat memfilter hasil menurut pengguna atau jenis data.

Notifikasi

Saat data pengguna berubah untuk jenis data yang berlangganan, Google Health API akan mengirimkan permintaan POST HTTPS ke URL endpoint pelanggan.

Format notifikasi

Payload notifikasi berisi detail tentang perubahan data, termasuk ID pengguna, jenis data, dan interval waktu, yang dapat Anda gunakan untuk mengkueri data yang diperbarui. Jika beberapa update terjadi atau update digabungkan, notifikasi akan dikelompokkan dan dikirim sebagai array JSON objek notifikasi.

Saat notifikasi webhook dipicu, Google Health API akan mengirimkan permintaan POST HTTPS ke endpoint pelanggan Anda dengan format berikut:

Upsert (data deret waktu)

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"
        }
      }
    ]
  }
}

Menghapus (data deret waktu)

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"
          }
        }
      ]
    }
  }
]

Menghapus (data yang dapat diidentifikasi)

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"
  }
}

Batch (multi-pengguna dan jenis data)

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"
    }
  }
]

Kolom operation menunjukkan jenis perubahan yang memicu notifikasi:

  • UPSERT: Dikirim untuk setiap penambahan atau modifikasi data.
  • DELETE: Dikirim saat pengguna secara eksplisit menghapus data (seperti menghapus dataPoints menggunakan aplikasi Fitbit atau melakukan penghapusan batch menggunakan API). Notifikasi webhook tidak dikirim untuk peristiwa penghapusan tingkat sistem, seperti pengguna yang mencabut izin aplikasi atau menghapus akun Fitbit mereka.

Sebaiknya buat logika penanganan notifikasi Anda idempoten, terutama untuk operasi UPSERT, karena percobaan ulang dapat menyebabkan notifikasi duplikat dikirim.

Kolom clientProvidedSubscriptionName adalah ID unik:

  • Untuk langganan dengan kebijakan MANUAL, kolom ini berisi nama langganan persisten yang disediakan developer dan ditentukan saat langganan dibuat. Hal ini memberikan ID yang stabil untuk mengelola langganan manual.
  • Untuk langganan yang dibuat dengan kebijakan AUTOMATIC, Google Health API akan otomatis membuat dan menetapkan ID dalam format: auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN>, dengan:
    • <project-number> adalah nomor project Google Cloud developer (misalnya, 362978535920).
    • <subscriber-id> adalah ID pendaftaran pelanggan / nama pelanggan (misalnya, google-health-sync).
    • <WEBHOOK_DATA_TYPE_TOKEN> adalah token yang mewakili jenis data tertentu. Lihat Kosakata token jenis data webhook untuk semua token yang memungkinkan.

Menyertakan clientProvidedSubscriptionName untuk kebijakan manual dan otomatis akan memastikan format payload notifikasi yang konsisten di semua jenis langganan.

Pemetaan ID pengguna

healthUserId adalah ID Google Health API untuk pengguna yang datanya telah berubah. Jika aplikasi Anda mendukung beberapa pengguna, Anda dapat menerima notifikasi untuk setiap pengguna yang telah memberikan izin kepada aplikasi Anda. Saat Anda menerima notifikasi, gunakan healthUserId untuk mengidentifikasi data pengguna mana yang telah berubah, sehingga Anda dapat menggunakan kredensial OAuth mereka untuk mengkueri data mereka.

Untuk memetakan kredensial OAuth pengguna ke healthUserId-nya, gunakan endpoint getIdentity. Panggil endpoint ini dengan kredensial pengguna selama orientasi pengguna untuk mengambil healthUserId mereka, dan simpan pemetaan ini. Pemetaan ini tidak berubah seiring waktu, sehingga dapat di-cache tanpa batas waktu. Misalnya, lihat Mendapatkan ID pengguna. Dengan begitu, Anda dapat memilih kredensial pengguna yang benar saat mengkueri data berdasarkan healthUserId dalam notifikasi.

Interval waktu

Array intervals berisi satu atau beberapa objek yang mewakili interval waktu untuk data yang diubah:

Kolom Jenis Wajib diisi Deskripsi
physicalTimeInterval object Tidak Interval waktu fisik UTC dari data yang diubah (format YYYY-MM-DDTHH:MM:SSZ). Diisi untuk metrik intrahari; dihilangkan untuk rollup harian dan aktivitas sepanjang hari.
civilDateTimeInterval object Ya Interval tanggal dan waktu kalender sipil (lokal) dari data yang diubah, tanpa offset UTC atau zona waktu.
civilIso8601TimeInterval object Ya Interval waktu sipil (lokal) yang direpresentasikan sebagai string ISO 8601 (format YYYY-MM-DDTHH:MM:SS) tanpa offset UTC atau zona waktu.

Untuk aktivitas sepanjang hari dan agregat harian, kolom physicalTimeInterval sengaja dihilangkan dari payload notifikasi. Hal ini karena peristiwa sepanjang hari dikelompokkan berdasarkan tanggal sipil nominalnya, dan durasi waktu fisik bisa jadi tidak akurat saat melintasi zona waktu. Saat memproses acara sepanjang hari, sebaiknya Anda mengandalkan kolom civilDateTimeInterval atau civilIso8601TimeInterval sebagai praktik terbaik. Untuk mengetahui detail tentang cara membuat kueri titik data ini menggunakan filter waktu sipil, lihat Memfilter data berdasarkan waktu mulai sipil interval.

Berikut adalah contoh payload notifikasi untuk aktivitas sepanjang hari dengan kolom physicalTimeInterval dihilangkan:

{
  "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"
        }
      }
    ]
  }
}

Kosakata token jenis data webhook

Saat menggunakan kebijakan AUTOMATIC, clientProvidedSubscriptionName yang dihasilkan berisi salah satu token akhiran WEBHOOK_DATA_TYPE_* yang menggunakan huruf kapital berikut, bergantung pada jenis datanya:

Token Jenis Data Webhook Jenis Data Terkait
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 (peristiwa sistem)
WEBHOOK_DATA_TYPE_USER_REVOKED_ACCESS user-revoked-access (peristiwa sistem)

Agregasi update

Untuk meminimalkan traffic jaringan, Google Health API mengelompokkan pesan. Sistem dikonfigurasi untuk mengirim maksimum 99 pesan per batch, yang otomatis dikirim saat tersedia.

Saat pesan dikelompokkan, payload dikirim sebagai array JSON yang berisi beberapa objek notifikasi. Satu batch dapat menyertakan update untuk beberapa pengguna di beberapa jenis data yang berlangganan. Untuk contoh payload, lihat contoh Batch (multi-pengguna dan jenis data) di bagian Format notifikasi.

Merespons notifikasi

Server Anda harus memvalidasi header Authorization, menangani handshake verifikasi endpoint sinkron, dan segera merespons notifikasi perubahan data dengan kode status HTTP 204 No Content. Untuk menghindari waktu tunggu habis, proses payload notifikasi secara asinkron setelah mengirim respons. Jika Google Health API menerima kode status lain atau waktu permintaan habis, Google Health API akan mencoba mengirim ulang notifikasi nanti.

Contoh 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).
        }
    });
});

Untuk mengambil data yang diperbarui untuk setiap interval yang diberi tahu, panggil endpoint reconcile dengan filter untuk rentang waktu interval. Tindakan ini akan otomatis menghapus duplikat interval yang tumpang-tindih dari perangkat terhubung. Untuk mengetahui detail dan contohnya, lihat Mendapatkan tampilan data interval yang direkonsiliasi.

Ada tiga strategi umum untuk mengupdate database setelah menerima notifikasi perubahan data:

  • Antrean (direkomendasikan): Tempatkan notifikasi yang masuk ke dalam antrean pesan dan proses dari antrean berdasarkan resource sistem.
  • Pengambilan langsung: Arahkan backend Anda untuk segera memanggil Google Health API guna mengambil data yang diperbarui dan meng-commit-nya ke database Anda.
  • Update lambat: Tandai data database lokal pengguna sebagai kotor dan hanya ambil data yang diperbarui dari Google Health API saat pengguna mengunjungi aplikasi Anda lagi.

Strategi antrean memungkinkan Anda memproses data hampir real-time sekaligus tetap tangguh terhadap banjir notifikasi (misalnya, jika pemadaman layanan menyebabkan backlog notifikasi dikirim sekaligus). Meskipun Anda dapat memilih untuk memproses antrean ini pada jadwal batch interval panjang (misalnya, setiap 3 jam) untuk menghemat resource, kelemahannya adalah pengguna biasanya berharap melihat data mereka diperbarui hampir secara real-time.

Praktik terbaik

Ikuti praktik terbaik berikut untuk memastikan penanganan update data yang andal dan efisien:

  • Gunakan Subscription API: Gunakan Subscription API untuk mendapatkan notifikasi saat pengguna memiliki data baru untuk didownload, bukan melakukan polling endpoint.
  • Antrekan dan proses notifikasi secara terpisah: Saat menerima notifikasi langganan, tempatkan notifikasi tersebut ke dalam antrean dan proses secara terpisah berdasarkan resource sistem Anda. Buat endpoint webhook tetap ringan dengan segera mengonfirmasi permintaan.

Verifikasi tanda tangan

Untuk memastikan keaslian notifikasi Webhook, payload JSON mentah dari setiap notifikasi webhook keluar ditandatangani dengan kunci pribadi menggunakan PublicKeySign Tink, yang memberikan tanda tangan berenkode Base64 di header HTTP GOOGLE-HEALTH-API-SIGNATURE dalam permintaan. Kunci penandatanganan ini dirotasi secara otomatis setiap 30 hari, dan keyset publik resmi yang sesuai didistribusikan sebagai file JSON di URL permanen https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.

Cara memverifikasi tanda tangan

Menggunakan Tink (Direkomendasikan): Developer dapat memverifikasi tanda tangan menggunakan primitif PublicKeyVerify Tink. Ambil keyset publik dari URL permanen, buat instance primitif PublicKeyVerify dengan keyset, dan verifikasi header GOOGLE-HEALTH-API-SIGNATURE yang didekode terhadap payload JSON webhook mentah.

Verifikasi Manual (Tanpa Tink): Jika developer memilih untuk tidak menggunakan Tink, mereka dapat memverifikasi tanda tangan secara manual dengan mengikuti langkah-langkah berikut:

  1. Mendekode header GOOGLE-HEALTH-API-SIGNATURE dengan Base64 untuk memisahkan awalan Tink 5 byte (yang berisi awalan versi 1 byte dan keyId 4 byte) dari tanda tangan berenkode DER yang sebenarnya.
  2. Ambil keyset JSON dari https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
  3. Temukan kunci yang cocok dengan keyId yang diuraikan dan dekode Base64 kolom nilainya, yang berisi buffering protokol EcdsaPublicKey yang diserialisasi.
  4. Ekstrak koordinat x dan y big-endian (tag Protobuf 3 dan 4) dari payload biner ini.
  5. Buat instance kunci publik ECDSA P-256 standar di library kriptografi bawaan menggunakan koordinat x dan y yang diekstrak.
  6. Verifikasi payload JSON webhook mentah terhadap tanda tangan DER yang diekstrak menggunakan algoritma SHA-256.

Status dan pemulihan pelanggan

Jika endpoint pelanggan Anda tidak tersedia atau menampilkan kode status error (apa pun selain 204), Google Health API akan menyimpan notifikasi yang tertunda hingga 7 hari dan mencoba kembali pengiriman dengan backoff eksponensial.

Setelah endpoint Anda kembali online dan merespons dengan 204, API akan otomatis mengirimkan backlog pesan yang disimpan. Notifikasi yang sudah lebih dari 7 hari akan dihapus dan tidak dapat dipulihkan.

Error yang biasa terjadi

Kode error Pesan Deskripsi Rekomendasi
400 Bad Request (400 Permintaan Tidak Valid) Nomor project tidak valid dalam nama resource Saat menghapus atau memperbarui pelanggan menggunakan ID project Google Cloud Anda di URL permintaan, bukan nomor project. Hal ini berlaku untuk langganan webhook yang menggunakan endpoint projects.subscribers. Gunakan nomor project Google Cloud Anda di URL permintaan, bukan project ID.
403 Forbidden Pemanggil tidak memiliki izin Saat membuat atau mencantumkan pelanggan menggunakan project ID Google Cloud Anda di URL permintaan, bukan nomor project. Hal ini berlaku untuk langganan webhook yang menggunakan endpoint projects.subscribers. Gunakan nomor project Google Cloud Anda di URL permintaan, bukan project ID.