{
  "info": {
    "_postman_id": "aifitnessapi-google-health",
    "name": "Google Health API (aifitnessapi.com)",
    "description": "Google Health API: OAuth 2.0 and Webhook samples requests built from the vendor documentation aifitnessapi.com quotes. Generated by scripts/build-collections.mjs; no secrets included. https://aifitnessapi.com/collections",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "OAuth 2.0",
      "description": "Google Health API's authorization-code flow as its documentation describes it, read 2026-11-05. Fill client_id, redirect_uri and scope in an environment of your own and keep client_secret there, never in a shared collection.\n\nThe same facts, with each source sentence, at https://aifitnessapi.com/google-health-api and https://aifitnessapi.com/tools/oauth-playground.",
      "item": [
        {
          "name": "1. Authorization URL (open in a browser)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id={{client_id}}&redirect_uri={{redirect_uri}}&scope={{scope}}&state={{state}}&access_type=offline&include_granted_scopes=true",
              "protocol": "https",
              "host": [
                "accounts",
                "google",
                "com"
              ],
              "path": [
                "o",
                "oauth2",
                "v2",
                "auth"
              ],
              "query": [
                {
                  "key": "response_type",
                  "value": "code"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}"
                },
                {
                  "key": "redirect_uri",
                  "value": "{{redirect_uri}}"
                },
                {
                  "key": "scope",
                  "value": "{{scope}}"
                },
                {
                  "key": "state",
                  "value": "{{state}}"
                },
                {
                  "key": "access_type",
                  "value": "offline"
                },
                {
                  "key": "include_granted_scopes",
                  "value": "true"
                }
              ]
            },
            "description": "https://accounts.google.com/o/oauth2/v2/auth. Google's OAuth 2.0 authorization endpoint (HTTPS only). Open this URL in a browser (in Postman: copy it from the request bar); the user signs in and Google Health API redirects to {{redirect_uri}} with ?code=… Put that value in the auth_code variable.\n\nSource: Google Identity, “Using OAuth 2.0 for Web Server Applications”, https://developers.google.com/identity/protocols/oauth2/web-server\n\nScope: Space-separated full scope URLs. The Google Health API's scopes are listed with Google's consent wording; add prompt=consent when you change them. Source: Google Health API, “Set up Google Cloud and OAuth”, https://developers.google.com/health/setup\n\nDocumented scopes (joined with spaces):\n- https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly: See your Google Health activity and fitness data.\n- https://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly: Add activity and fitness data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.ecg.readonly: See your Google Health ECG data.\n- https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonly: See your Google Health health metrics and measurement data.\n- https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly: Add health metric and measurements data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.irn.readonly: See your Google Health Irregular Rhythm Notifications data.\n- https://www.googleapis.com/auth/googlehealth.location.readonly: See your Google Health GPS location recorded during an exercise.\n- https://www.googleapis.com/auth/googlehealth.logged_symptoms.writeonly: Add logged symptoms data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.mindfulness.writeonly: Add mindfulness data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.nutrition.readonly: See your Google Health nutrition data.\n- https://www.googleapis.com/auth/googlehealth.nutrition.writeonly: Add nutrition data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.profile.readonly: See your Google Health profile data.\n- https://www.googleapis.com/auth/googlehealth.profile.writeonly: Add profile data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.reproductive_health.writeonly: Add reproductive health data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.settings.readonly: See your Google Health settings.\n- https://www.googleapis.com/auth/googlehealth.settings.writeonly: Add settings data to Google Health, and edit or delete the data it adds.\n- https://www.googleapis.com/auth/googlehealth.sleep.readonly: See your Google Health sleep data.\n- https://www.googleapis.com/auth/googlehealth.sleep.writeonly: Add sleep data to Google Health, and edit or delete the data it adds.\n\nSource: Google Health API, “Scopes” (via this site's generated Google Health API dataset), https://developers.google.com/health/scopes\n\nPKCE: Not described on Google's web-server OAuth page, which this tool follows."
          },
          "response": []
        },
        {
          "name": "2. Exchange the code for tokens",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded",
                "type": "text"
              }
            ],
            "url": {
              "raw": "https://oauth2.googleapis.com/token",
              "protocol": "https",
              "host": [
                "oauth2",
                "googleapis",
                "com"
              ],
              "path": [
                "token"
              ]
            },
            "description": "https://oauth2.googleapis.com/token. Token URL, for the exchange and refresh.\n\nSource: Google Identity, “Using OAuth 2.0 for Web Server Applications”, https://developers.google.com/identity/protocols/oauth2/web-server\n\nClient credentials go in the form body.\n\nToken lifetime: expires_in gives the remaining lifetime in seconds; Google's sample shows 3920. Source: Google Identity, “Using OAuth 2.0 for Web Server Applications”, https://developers.google.com/identity/protocols/oauth2/web-server\n\nNote: access_type=offline is what makes Google return a refresh token. Source: Google Identity, “Using OAuth 2.0 for Web Server Applications”, https://developers.google.com/identity/protocols/oauth2/web-server\n\nNote: Google limits refresh tokens per client/user pair and per user. Source: Google Identity, “Using OAuth 2.0 for Web Server Applications”, https://developers.google.com/identity/protocols/oauth2/web-server\n\nNote: A new OAuth client is unverified and capped at 100 users, each added as a test user. Source: Google Health API, “Set up Google Cloud and OAuth”, https://developers.google.com/health/setup",
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "grant_type",
                  "value": "authorization_code",
                  "type": "text"
                },
                {
                  "key": "code",
                  "value": "{{auth_code}}",
                  "type": "text"
                },
                {
                  "key": "redirect_uri",
                  "value": "{{redirect_uri}}",
                  "type": "text"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}",
                  "type": "text"
                },
                {
                  "key": "client_secret",
                  "value": "{{client_secret}}",
                  "type": "text"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "3. Refresh the access token",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded",
                "type": "text"
              }
            ],
            "url": {
              "raw": "https://oauth2.googleapis.com/token",
              "protocol": "https",
              "host": [
                "oauth2",
                "googleapis",
                "com"
              ],
              "path": [
                "token"
              ]
            },
            "description": "Refresh: access_type=offline gets a refresh token on the first exchange only. Refresh tokens last until revoked or expired, and Google caps how many it issues per client and user.\n\nSource: Google Identity, “Using OAuth 2.0 for Web Server Applications”, https://developers.google.com/identity/protocols/oauth2/web-server",
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "grant_type",
                  "value": "refresh_token",
                  "type": "text"
                },
                {
                  "key": "refresh_token",
                  "value": "{{refresh_token}}",
                  "type": "text"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}",
                  "type": "text"
                },
                {
                  "key": "client_secret",
                  "value": "{{client_secret}}",
                  "type": "text"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Webhook samples",
      "description": "Google Health API's documented example notifications, sent to {{local_url}} (your local handler) unchanged. Signature: Google sends the endpointAuthorization secret you configured in the Authorization header. It also signs the raw JSON with Tink (ECDSA P-256, SHA-256), base64 in GOOGLE-HEALTH-API-SIGNATURE, keys rotated every 30 days and published as a JSON keyset. Source: Google Health API, “Webhook subscriptions”, https://developers.google.com/health/webhooks\n\nHeaders that carry a signature hold the placeholder Google Health API prints, so a handler that verifies signatures will reject these: to send correctly signed requests, use https://aifitnessapi.com/webhooks/send-sample.mjs. Respond: Validate the Authorization header and respond 204 No Content immediately; process asynchronously. On subscriber create or endpoint change, two POSTs with {\"type\": \"verification\"}: answer the authorized one with 200 or 201 and the unauthenticated one with 401 or 403.\n\nPayload library with JSON Schemas: https://aifitnessapi.com/webhooks#google-health.",
      "item": [
        {
          "name": "Upsert (time-series data)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Authorization",
                "value": "Bearer example-secret-token",
                "type": "text",
                "description": "As in Google Health API's example."
              },
              {
                "key": "GOOGLE-HEALTH-API-SIGNATURE",
                "value": "signature-value",
                "type": "text",
                "description": "As in Google Health API's example."
              },
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text",
                "description": "As in Google Health API's example."
              }
            ],
            "url": {
              "raw": "{{local_url}}",
              "host": [
                "{{local_url}}"
              ]
            },
            "description": "Sent for any data addition or modification.\n\nGoogle's notification format. Source: Google Health API, “Webhook subscriptions”, https://developers.google.com/health/webhooks",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"data\": {\n    \"version\": \"1\",\n    \"clientProvidedSubscriptionName\": \"subscription-name\",\n    \"healthUserId\": \"health-user-id\",\n    \"operation\": \"UPSERT\",\n    \"dataType\": \"steps\",\n    \"intervals\": [\n      {\n        \"physicalTimeInterval\": {\n          \"startTime\": \"2026-03-08T01:29:00Z\",\n          \"endTime\": \"2026-03-08T01:34:00Z\"\n        },\n        \"civilDateTimeInterval\": {\n          \"startDateTime\": {\n            \"date\": {\n              \"year\": 2026,\n              \"month\": 3,\n              \"day\": 7\n            },\n            \"time\": {\n              \"hours\": 17,\n              \"minutes\": 29\n            }\n          },\n          \"endDateTime\": {\n            \"date\": {\n              \"year\": 2026,\n              \"month\": 3,\n              \"day\": 7\n            },\n            \"time\": {\n              \"hours\": 17,\n              \"minutes\": 34\n            }\n          }\n        },\n        \"civilIso8601TimeInterval\": {\n          \"startTime\": \"2026-03-07T17:29:00\",\n          \"endTime\": \"2026-03-07T17:34:00\"\n        }\n      }\n    ]\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Delete (time-series data)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{local_url}}",
              "host": [
                "{{local_url}}"
              ]
            },
            "description": "Sent when a user explicitly deletes data. Google's example arrives as an array.\n\nDELETE is sent when a user explicitly deletes data. Source: Google Health API, “Webhook subscriptions”, https://developers.google.com/health/webhooks",
            "body": {
              "mode": "raw",
              "raw": "[\n  {\n    \"data\": {\n      \"version\": \"1\",\n      \"clientProvidedSubscriptionName\": \"subscription-name\",\n      \"healthUserId\": \"health-user-id\",\n      \"operation\": \"DELETE\",\n      \"dataType\": \"steps\",\n      \"intervals\": [\n        {\n          \"physicalTimeInterval\": {\n            \"startTime\": \"2026-08-12T12:00:00Z\",\n            \"endTime\": \"2026-08-12T12:30:00Z\"\n          },\n          \"civilIso8601TimeInterval\": {\n            \"startTime\": \"2026-08-12T12:00:00\",\n            \"endTime\": \"2026-08-12T12:30:00\"\n          }\n        }\n      ]\n    }\n  }\n]",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Delete (identifiable data)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{local_url}}",
              "host": [
                "{{local_url}}"
              ]
            },
            "description": "A delete of identifiable data such as a sleep carries the deleted record's recordId.\n\nGoogle's identifiable-data delete example. Source: Google Health API, “Webhook subscriptions”, https://developers.google.com/health/webhooks",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"data\": {\n    \"version\": \"1\",\n    \"clientProvidedSubscriptionName\": \"subscription-name\",\n    \"healthUserId\": \"health-user-id\",\n    \"operation\": \"DELETE\",\n    \"dataType\": \"sleep\",\n    \"intervals\": [\n      {\n        \"physicalTimeInterval\": {\n          \"startTime\": \"2026-08-12T22:00:00Z\",\n          \"endTime\": \"2026-08-13T06:00:00Z\"\n        },\n        \"civilIso8601TimeInterval\": {\n          \"startTime\": \"2026-08-12T22:00:00\",\n          \"endTime\": \"2026-08-13T06:00:00\"\n        }\n      }\n    ],\n    \"recordId\": \"9875412\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Full-day activity",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{local_url}}",
              "host": [
                "{{local_url}}"
              ]
            },
            "description": "Full-day activities and daily aggregates omit physicalTimeInterval; rely on the civil fields.\n\nGoogle's full-day example omits physicalTimeInterval. Source: Google Health API, “Webhook subscriptions”, https://developers.google.com/health/webhooks",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"data\": {\n    \"version\": \"1\",\n    \"clientProvidedSubscriptionName\": \"auto-503461148219-lindasubscriber-WEBHOOK_DATA_TYPE_STEPS\",\n    \"healthUserId\": \"343864966078399341\",\n    \"operation\": \"UPSERT\",\n    \"dataType\": \"steps\",\n    \"intervals\": [\n      {\n        \"civilDateTimeInterval\": {\n          \"startDateTime\": {\n            \"date\": {\n              \"year\": 2026,\n              \"month\": 7,\n              \"day\": 14\n            },\n            \"time\": {}\n          },\n          \"endDateTime\": {\n            \"date\": {\n              \"year\": 2026,\n              \"month\": 7,\n              \"day\": 15\n            },\n            \"time\": {}\n          }\n        },\n        \"civilIso8601TimeInterval\": {\n          \"startTime\": \"2026-07-14T00:00:00\",\n          \"endTime\": \"2026-07-15T00:00:00\"\n        }\n      }\n    ]\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Endpoint verification",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{local_url}}",
              "host": [
                "{{local_url}}"
              ]
            },
            "description": "Two POSTs when you create a subscriber or change its endpoint: one with your Authorization header, one without.\n\nThe verification handshake body. Source: Google Health API, “Webhook subscriptions”, https://developers.google.com/health/webhooks",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"type\": \"verification\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "client_id",
      "value": "",
      "type": "string",
      "description": "Your app's client ID, from the vendor's developer console."
    },
    {
      "key": "client_secret",
      "value": "",
      "type": "string",
      "description": "Keep it in a Postman or Bruno environment marked secret. This collection ships it empty."
    },
    {
      "key": "redirect_uri",
      "value": "",
      "type": "string",
      "description": "The redirect URI registered with the vendor."
    },
    {
      "key": "scope",
      "value": "",
      "type": "string",
      "description": "Scopes to request, joined as the vendor documents (see the authorization request)."
    },
    {
      "key": "state",
      "value": "",
      "type": "string",
      "description": "A random value you check on the redirect."
    },
    {
      "key": "auth_code",
      "value": "",
      "type": "string",
      "description": "The ?code= value from the redirect."
    },
    {
      "key": "refresh_token",
      "value": "",
      "type": "string",
      "description": "The refresh_token from the token response."
    },
    {
      "key": "local_url",
      "value": "http://localhost:3000/webhooks/google-health",
      "type": "string",
      "description": "Where your webhook handler listens."
    }
  ]
}
