---
title: "Wearable webhook payloads, as each vendor documents them"
canonical: "https://aifitnessapi.com/webhooks"
markdown: "https://aifitnessapi.com/webhooks.md"
type: "record"
last_reviewed: "2026-11-05"
description: "Documented webhook payloads from Oura, WHOOP, Strava, Polar, Withings, Wahoo, Terra and Google Health, with JSON Schemas, retries and a local mock sender."
publisher: "AIFitnessAPI — funded by KinesteX; first-party pages disclose it"
cite_as: "\"Wearable webhook payloads, as each vendor documents them\", AIFitnessAPI, https://aifitnessapi.com/webhooks"
---

# Wearable webhook payloads, as each vendor documents them

> 31 webhook payload examples from the documentation of Oura, WHOOP, Strava, Polar AccessLink, Withings, Wahoo, Terra and Google Health API, each reproduced as published with a link to the page it came from, plus a JSON Schema for each built from the fields the vendor documents. Each vendor's section states its signature scheme, the response it expects and its retry behavior in its current wording; Terra, for example, now says it retries "with exponential backoff for several hours". A zero-dependency Node script posts any of these examples, correctly signed, to a local URL so you can test your handler. Read on 2026-11-05.

- Canonical: https://aifitnessapi.com/webhooks
- Last reviewed: 2026-11-05
- Publisher: AIFitnessAPI (https://aifitnessapi.com)
- Cite as: "Wearable webhook payloads, as each vendor documents them", AIFitnessAPI, https://aifitnessapi.com/webhooks

---

## At a glance

| Vendor | Payloads | Signature | Retries |
|---|---|---|---|
| Oura | 1 | HMAC-SHA256 keyed by your client secret over the x-oura-timestamp value followed by JSON.stringify(body), hex, upper-case, in x-oura-signature. | 10 retries each for 4xx responses, 5xx responses and timeouts, for about an hour; a 410 response cancels the subscription. |
| WHOOP | 2 | Base64 of HMAC-SHA256, keyed by your client secret, over the X-WHOOP-Signature-Timestamp value (milliseconds since epoch) followed by the raw body, in X-WHOOP-Signature. | Five retries over about one hour; anything but a 2XX, or no response before the timeout, counts as a failure. |
| Strava | 1 | No signature is documented for event POSTs. Subscription validation is a GET with hub.mode, hub.challenge and hub.verify_token; echo hub.challenge as JSON. | Retried up to a total of three attempts if a 200 is not returned. |
| Polar AccessLink | 8 | HMAC-SHA256 of the payload, keyed by the signature_secret_key returned once when you create the webhook, hex, in Polar-Webhook-Signature; the event type is in Polar-Webhook-Event. | No retry schedule is documented. A webhook whose deliveries fail for seven days is deactivated; re-enable it with the activate endpoint, which pings the URL again. |
| Withings | 8 | No body signature. Append a secret query parameter to the callback URL (Withings' example: ?token=<random-secret>) and check it, plus an allowlist of the IP ranges Withings publishes as DNS TXT records. | If your endpoint does not return HTTP < 400: 5 retry cycles over about 5 hours (+10 s, +1 min, +1 h, +4 h), 2 HTTP attempts each. After 20 days of unresolved failures the subscription is canceled; missed notifications are not redelivered. |
| Wahoo | 1 | No signature. Wahoo sends the webhook_token you set in the app's webhook settings inside the JSON body; ignore any request without it. | Without an HTTP 200, retried after 30 minutes, 4 hours, 24 hours and 72 hours. |
| Terra | 5 | terra-signature: t=<timestamp>,v1=<signature>; HMAC-SHA256 keyed by the webhook's signing secret over the timestamp, a "." and the raw body. Ignore schemes other than v1. | Retried automatically with exponential backoff for several hours; Terra says the number and timing of retries may change without notice. After the retry budget is spent the event is dropped, with no notification. |
| Google Health API | 5 | 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. | Anything other than 204, or a timeout, is retried later. Pending notifications are stored for up to 7 days and retried with exponential backoff; older ones are discarded. |
| Suunto | 0 | Not documented on Suunto's FAQ. The webhook address is set in your app's OAuth settings in Suunto API Zone and must be publicly reachable. | Not documented on the pages read here. |

## Oura

- **Signature** HMAC-SHA256 keyed by your client secret over the x-oura-timestamp value followed by JSON.stringify(body), hex, upper-case, in x-oura-signature. [Source](https://cloud.ouraring.com/v2/static/json/openapi-1.43.json)
- **Respond** Respond with a 2xx in under 10 seconds and process the event asynchronously. Before events flow, Oura verifies the callback with a GET carrying verification_token and challenge; echo the challenge as JSON. [Source](https://cloud.ouraring.com/v2/static/json/openapi-1.43.json)
- **Retries** 10 retries each for 4xx responses, 5xx responses and timeouts, for about an hour; a 410 response cancels the subscription. [Source](https://cloud.ouraring.com/v2/static/json/openapi-1.43.json)
- **Note** 4xx responses are retried too. [Source](https://cloud.ouraring.com/v2/static/json/openapi-1.43.json)
- **Note** One subscription per event_type and data_type pair; the event carries the object_id to fetch. [Source](https://cloud.ouraring.com/v2/static/json/openapi-1.43.json)

[Verify a Oura delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-oura) · [Oura integration guide](https://aifitnessapi.com/integrate/oura-api)

### Data event

Oura sends a POST to your callback URL when subscribed data changes (the example: a user syncs new sleep data).

#### Headers in the example

```http
x-oura-signature: HMAC_SIGNATURE
x-oura-timestamp: 1234567890
```

#### Example body

```json
{
  "event_type": "update",
  "data_type": "sleep",
  "object_id": "12345abc",
  "event_time": "2023-01-01T08:00:00+00:00",
  "user_id": "user123"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "oura-event",
  "title": "Oura webhook: Data event",
  "description": "Built from the fields Oura documents at https://cloud.ouraring.com/v2/static/json/openapi-1.43.json. JSON body.",
  "type": "object",
  "properties": {
    "event_type": {
      "description": "create, update or delete (Oura's WebhookOperation).",
      "type": "string",
      "enum": [
        "create",
        "update",
        "delete"
      ],
      "x-type-from": "documented"
    },
    "data_type": {
      "description": "One of the data types partners can subscribe to (Oura's ExtApiV2DataType).",
      "type": "string",
      "enum": [
        "tag",
        "enhanced_tag",
        "workout",
        "session",
        "sleep",
        "daily_sleep",
        "daily_readiness",
        "daily_activity",
        "daily_spo2",
        "sleep_time",
        "rest_mode_period",
        "ring_configuration",
        "daily_stress",
        "daily_cardiovascular_age",
        "daily_resilience",
        "vo2_max"
      ],
      "x-type-from": "documented"
    },
    "object_id": {
      "description": "Use it to fetch the complete record from the API.",
      "type": "string",
      "x-type-from": "example"
    },
    "event_time": {
      "type": "string",
      "x-type-from": "example"
    },
    "user_id": {
      "type": "string",
      "x-type-from": "example"
    }
  },
  "additionalProperties": true
}
```

From [Oura API v2 OpenAPI document (1.43), “Webhooks for Real-Time Data Updates”](https://cloud.ouraring.com/v2/static/json/openapi-1.43.json), read 2026-11-05 (via the site's source-snapshot workflow).

## WHOOP

- **Signature** Base64 of HMAC-SHA256, keyed by your client secret, over the X-WHOOP-Signature-Timestamp value (milliseconds since epoch) followed by the raw body, in X-WHOOP-Signature. [Source](https://developer.whoop.com/docs/developing/webhooks/)
- **Respond** Return a 2XX, ideally within a second, and process on a queue. Every event type goes to every configured URL; answer 2XX to the ones you ignore. [Source](https://developer.whoop.com/docs/developing/webhooks/)
- **Retries** Five retries over about one hour; anything but a 2XX, or no response before the timeout, counts as a failure. [Source](https://developer.whoop.com/docs/developing/webhooks/)
- **Note** Duplicates happen: deduplicate on trace_id. [Source](https://developer.whoop.com/docs/developing/webhooks/)
- **Note** Creates arrive as update events. [Source](https://developer.whoop.com/docs/developing/webhooks/)
- **Note** v1 webhooks are no longer published; v2 uses UUIDs. [Source](https://developer.whoop.com/docs/developing/webhooks/)

[Verify a WHOOP delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-whoop) · [WHOOP integration guide](https://aifitnessapi.com/integrate/whoop-api)

### v2 example: sleep.updated

User 456 records a sleep and WHOOP posts to your v2 webhook endpoint; you then GET v2/activity/sleep/{id}.

#### Example body

```json
{
  "user_id": 456,
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "sleep.updated",
  "trace_id": "e369c784-5100-49e8-8098-75d35c47b31b"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "whoop-v2-sleep-updated",
  "title": "WHOOP webhook: v2 example: sleep.updated",
  "description": "Built from the fields WHOOP documents at https://developer.whoop.com/docs/developing/webhooks/. JSON body.",
  "type": "object",
  "properties": {
    "user_id": {
      "description": "The WHOOP User for the event",
      "type": "integer",
      "x-type-from": "documented"
    },
    "id": {
      "description": "Identifier of the object that triggered this webhook. For v2 webhooks a UUID; for v2 recovery events, the UUID of the sleep the recovery belongs to.",
      "type": [
        "integer",
        "string"
      ],
      "x-type-from": "documented"
    },
    "type": {
      "description": "The type of event that triggered this webhook",
      "type": "string",
      "enum": [
        "workout.updated",
        "workout.deleted",
        "sleep.updated",
        "sleep.deleted",
        "recovery.updated",
        "recovery.deleted"
      ],
      "x-type-from": "documented"
    },
    "trace_id": {
      "description": "Trace ID for the event that triggered this webhook",
      "type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [WHOOP Developer Platform, “Webhooks”](https://developer.whoop.com/docs/developing/webhooks/), read 2026-11-05 (via the site's source-snapshot workflow).

### Field-table example: workout.updated

The example beside WHOOP's field table, with integer ids (the table documents id as int64 or string).

#### Example body

```json
{
  "user_id": 10129,
  "id": 10235,
  "type": "workout.updated",
  "trace_id": "d3709ee7-104e-4f70-a928-2932964b017b"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "whoop-spec-workout-updated",
  "title": "WHOOP webhook: Field-table example: workout.updated",
  "description": "Built from the fields WHOOP documents at https://developer.whoop.com/docs/developing/webhooks/. JSON body.",
  "type": "object",
  "properties": {
    "user_id": {
      "description": "The WHOOP User for the event",
      "type": "integer",
      "x-type-from": "documented"
    },
    "id": {
      "description": "Identifier of the object that triggered this webhook. For v2 webhooks a UUID; for v2 recovery events, the UUID of the sleep the recovery belongs to.",
      "type": [
        "integer",
        "string"
      ],
      "x-type-from": "documented"
    },
    "type": {
      "description": "The type of event that triggered this webhook",
      "type": "string",
      "enum": [
        "workout.updated",
        "workout.deleted",
        "sleep.updated",
        "sleep.deleted",
        "recovery.updated",
        "recovery.deleted"
      ],
      "x-type-from": "documented"
    },
    "trace_id": {
      "description": "Trace ID for the event that triggered this webhook",
      "type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [WHOOP Developer Platform, “Webhooks”](https://developer.whoop.com/docs/developing/webhooks/), read 2026-11-05 (via the site's source-snapshot workflow).

## Strava

- **Signature** No signature is documented for event POSTs. Subscription validation is a GET with hub.mode, hub.challenge and hub.verify_token; echo hub.challenge as JSON. [Source](https://developers.strava.com/docs/webhooks/)
- **Respond** Acknowledge each POST with 200 OK within two seconds; do further work asynchronously. [Source](https://developers.strava.com/docs/webhooks/)
- **Retries** Retried up to a total of three attempts if a 200 is not returned. [Source](https://developers.strava.com/docs/webhooks/)
- **Note** One subscription per application, covering every athlete who authorized it. [Source](https://developers.strava.com/docs/webhooks/)
- **Note** One save can produce several events. [Source](https://developers.strava.com/docs/webhooks/)

[Verify a Strava delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-strava) · [Strava integration guide](https://aifitnessapi.com/integrate/strava-api)

### Activity update

Sent when an activity is created or deleted, when its title, type or privacy changes, and when an athlete revokes access (updates then holds "authorized": "false").

#### Example body

```json
{
  "aspect_type": "update",
  "event_time": 1516126040,
  "object_id": 1360128428,
  "object_type": "activity",
  "owner_id": 134815,
  "subscription_id": 120475,
  "updates": {
    "title": "Messy"
  }
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "strava-activity-update",
  "title": "Strava webhook: Activity update",
  "description": "Built from the fields Strava documents at https://developers.strava.com/docs/webhooks/. JSON body.",
  "type": "object",
  "properties": {
    "object_type": {
      "description": "Always either \"activity\" or \"athlete.\"",
      "type": "string",
      "enum": [
        "activity",
        "athlete"
      ],
      "x-type-from": "documented"
    },
    "object_id": {
      "description": "For activity events, the activity's ID. For athlete events, the athlete's ID.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "aspect_type": {
      "description": "Always \"create,\" \"update,\" or \"delete.\"",
      "type": "string",
      "enum": [
        "create",
        "update",
        "delete"
      ],
      "x-type-from": "documented"
    },
    "updates": {
      "description": "For activity updates: title, type and private. For app deauthorization events: always \"authorized\": \"false\".",
      "type": "object",
      "x-type-from": "documented"
    },
    "owner_id": {
      "description": "The athlete's ID.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "subscription_id": {
      "description": "The push subscription ID that is receiving this event.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "event_time": {
      "description": "The time that the event occurred.",
      "type": "integer",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Strava Developers, “Webhook Events API”](https://developers.strava.com/docs/webhooks/), read 2026-11-05 (via the site's source-snapshot workflow).

## Polar AccessLink

- **Signature** HMAC-SHA256 of the payload, keyed by the signature_secret_key returned once when you create the webhook, hex, in Polar-Webhook-Signature; the event type is in Polar-Webhook-Event. [Source](https://www.polar.com/accesslink-api/)
- **Respond** Answer with 200. The PING sent when a webhook is created or its URL changes must get 200 OK, or the change is not made. [Source](https://www.polar.com/accesslink-api/)
- **Retries** No retry schedule is documented. A webhook whose deliveries fail for seven days is deactivated; re-enable it with the activate endpoint, which pings the URL again. [Source](https://www.polar.com/accesslink-api/)
- **Note** A client application can have only one webhook. [Source](https://www.polar.com/accesslink-api/)

[Verify a Polar AccessLink delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-polar) · [Polar AccessLink integration guide](https://aifitnessapi.com/integrate/polar-api)

### PING

Sent as an HTTP POST when a webhook is created or modified, to verify the URL.

#### Headers in the example

```http
Polar-Webhook-Event: PING
```

#### Example body

```json
{
  "timestamp": "2018-05-15T14:22:24Z",
  "event": "PING"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-ping",
  "title": "Polar AccessLink webhook: PING",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "timestamp": {
      "type": "string",
      "format": "date-time",
      "x-type-from": "example"
    },
    "event": {
      "type": "string",
      "x-type-from": "example"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

### Exercise

This event is sent when new exercise data is available for the user.

#### Headers in the example

```http
Polar-Webhook-Event: EXERCISE
```

#### Example body

```json
{
  "event": "EXERCISE",
  "user_id": 475,
  "entity_id": "aQlC83",
  "timestamp": "2018-05-15T14:22:24Z",
  "url": "https://www.polaraccesslink.com/v3/exercises/aQlC83"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-exercise",
  "title": "Polar AccessLink webhook: Exercise",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "event": {
      "description": "Type of available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "Id of the user who has new data.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "entity_id": {
      "description": "Id of the available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "timestamp": {
      "description": "Time when webhook notification is sent.",
      "type": "string",
      "format": "date-time",
      "x-type-from": "documented"
    },
    "url": {
      "description": "Url to the new available data.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

### Sleep

This event is sent when new sleep data is available for the user.

#### Headers in the example

```http
Polar-Webhook-Event: SLEEP
```

#### Example body

```json
{
  "event": "SLEEP",
  "user_id": 475,
  "date": "2019-12-31",
  "timestamp": "2018-05-15T14:22:24Z",
  "url": "https://www.polaraccesslink.com/v3/users/sleep/2019-12-31"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-sleep",
  "title": "Polar AccessLink webhook: Sleep",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "event": {
      "description": "Type of available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "Id of the user who has new data.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "date": {
      "description": "Date of the available sleep data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "timestamp": {
      "description": "Time when webhook notification is sent.",
      "type": "string",
      "format": "date-time",
      "x-type-from": "documented"
    },
    "url": {
      "description": "Url to the new available data.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

### Continuous heart rate

This event is sent when new continuous heart rate data is available for the user.

#### Headers in the example

```http
Polar-Webhook-Event: CONTINUOUS_HEART_RATE
```

#### Example body

```json
{
  "event": "CONTINUOUS_HEART_RATE",
  "user_id": 475,
  "date": "2022-09-30",
  "timestamp": "2018-05-15T14:22:24Z",
  "url": "https://www.polaraccesslink.com/v3/users/continuous-heart-rate/2022-09-30"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-continuous_heart_rate",
  "title": "Polar AccessLink webhook: Continuous heart rate",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "event": {
      "description": "Type of available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "Id of the user who has new data.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "date": {
      "description": "Date of the available continuous hr data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "timestamp": {
      "description": "Time when webhook notification is sent.",
      "type": "string",
      "format": "date-time",
      "x-type-from": "documented"
    },
    "url": {
      "description": "Url to the new available data.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

### SleepWise circadian bedtime

This event is sent when new circadian bedtime data is available for the user.

#### Headers in the example

```http
Polar-Webhook-Event: SLEEP_WISE_CIRCADIAN_BEDTIME
```

#### Example body

```json
{
  "event": "SLEEP_WISE_CIRCADIAN_BEDTIME",
  "user_id": 475,
  "from": "2022-09-30",
  "to": "2022-10-02",
  "timestamp": "2022-10-02T14:22:24Z",
  "url": "https://www.polaraccesslink.com/v3/users/sleepwise/circadian-bedtime/date?from=2022-09-30&to=2022-10-02"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-sleep_wise_circadian_bedtime",
  "title": "Polar AccessLink webhook: SleepWise circadian bedtime",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "event": {
      "description": "Type of available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "Id of the user who has new data.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "from": {
      "description": "Start date of the available circadian bedtime data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "to": {
      "description": "End date of the available circadian bedtime data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "timestamp": {
      "description": "Time when webhook notification is sent.",
      "type": "string",
      "format": "date-time",
      "x-type-from": "documented"
    },
    "url": {
      "description": "Url to the new available data.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

### SleepWise alertness

This event is sent when new alertness data is available for the user.

#### Headers in the example

```http
Polar-Webhook-Event: SLEEP_WISE_ALERTNESS
```

#### Example body

```json
{
  "event": "SLEEP_WISE_ALERTNESS",
  "user_id": 475,
  "from": "2022-09-30",
  "to": "2022-10-02",
  "timestamp": "2022-10-02T14:22:24Z",
  "url": "https://www.polaraccesslink.com/v3/users/sleepwise/alertness/date?from=2022-09-30&to=2022-10-02"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-sleep_wise_alertness",
  "title": "Polar AccessLink webhook: SleepWise alertness",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "event": {
      "description": "Type of available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "Id of the user who has new data.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "from": {
      "description": "Start date of the available alertness data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "to": {
      "description": "End date of the available alertness data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "timestamp": {
      "description": "Time when webhook notification is sent.",
      "type": "string",
      "format": "date-time",
      "x-type-from": "documented"
    },
    "url": {
      "description": "Url to the new available data.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

### Activity summary

This event is sent when new activity summary data is available for the user.

#### Headers in the example

```http
Polar-Webhook-Event: ACTIVITY_SUMMARY
```

#### Example body

```json
{
  "event": "ACTIVITY_SUMMARY",
  "user_id": 475,
  "date": "2022-09-30",
  "timestamp": "2022-10-02T14:22:24Z",
  "url": "https://www.polaraccesslink.com/v3/users/activities/2022-09-30"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-activity_summary",
  "title": "Polar AccessLink webhook: Activity summary",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "event": {
      "description": "Type of available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "Id of the user who has new data.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "date": {
      "description": "Date of the available activity summary data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "timestamp": {
      "description": "Time when webhook notification is sent.",
      "type": "string",
      "format": "date-time",
      "x-type-from": "documented"
    },
    "url": {
      "description": "Url to the new available data.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

### Physical information

This event is sent when new physical information data is available for the user.

#### Headers in the example

```http
Polar-Webhook-Event: PHYSICAL_INFORMATION
```

#### Example body

```json
{
  "event": "PHYSICAL_INFORMATION",
  "user_id": 475,
  "timestamp": "2025-12-31T14:22:24Z",
  "url": "https://www.polaraccesslink.com/v3/users/physical-info"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "polar-physical_information",
  "title": "Polar AccessLink webhook: Physical information",
  "description": "Built from the fields Polar AccessLink documents at https://www.polar.com/accesslink-api/. JSON body.",
  "type": "object",
  "properties": {
    "event": {
      "description": "Type of available data.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "Id of the user who has new data.",
      "type": "integer",
      "x-type-from": "documented"
    },
    "timestamp": {
      "description": "Time when webhook notification is sent.",
      "type": "string",
      "format": "date-time",
      "x-type-from": "documented"
    },
    "url": {
      "description": "Url to the new available data.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Polar AccessLink API v3, “Accesslink webhooks” and data models](https://www.polar.com/accesslink-api/), read 2026-11-05 (via the site's source-snapshot workflow).

## Withings

- **Signature** No body signature. Append a secret query parameter to the callback URL (Withings' example: ?token=<random-secret>) and check it, plus an allowlist of the IP ranges Withings publishes as DNS TXT records. [Source](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-overview/)
- **Respond** An application/x-www-form-urlencoded POST; return HTTP 2xx within a few seconds, then fetch the data from the Data API. [Source](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-overview/)
- **Retries** If your endpoint does not return HTTP < 400: 5 retry cycles over about 5 hours (+10 s, +1 min, +1 h, +4 h), 2 HTTP attempts each. After 20 days of unresolved failures the subscription is canceled; missed notifications are not redelivered. [Source](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-overview/)
- **Note** Missed notifications are not redelivered: backfill with lastupdate. [Source](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-overview/)
- **Note** Typical delay is under 2 minutes, with no real-time SLA on the standard plan. [Source](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-overview/)

[Verify a Withings delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-withings) · [Withings integration guide](https://aifitnessapi.com/integrate/withings-api)

### appli 1: Weight & body composition

A new weight or body composition measurement was recorded by a Withings scale.

#### Example body (form-encoded fields)

```text
userid=12345
appli=1
startdate=1530576000
enddate=1530698753
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-1",
  "title": "Withings webhook: appli 1: Weight & body composition",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "startdate": {
      "description": "Start of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "enddate": {
      "description": "End of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

### appli 4: Blood pressure & heart rate

A new blood pressure measurement (and heart rate, when supported) was recorded.

#### Example body (form-encoded fields)

```text
userid=12345
appli=4
startdate=1530576000
enddate=1530576120
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-4",
  "title": "Withings webhook: appli 4: Blood pressure & heart rate",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "startdate": {
      "description": "Start of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "enddate": {
      "description": "End of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

### appli 16: Activity

New activity data is available — steps, distance, calories, intensity, or workouts.

#### Example body (form-encoded fields)

```text
userid=12345
appli=16
date=2018-07-02
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-16",
  "title": "Withings webhook: appli 16: Activity",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "date": {
      "description": "Single date for event-based notifications.",
      "type": "string",
      "x-documented-type": "string (YYYY-MM-DD) or int (unix epoch)",
      "pattern": "^([0-9]{4}-[0-9]{2}-[0-9]{2}|-?[0-9]+)$",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

### appli 44: Sleep summary

New sleep data is available — duration, sleep state, deep/light state duration (no REM), and wakeup counts.

#### Example body (form-encoded fields)

```text
userid=12345
appli=44
startdate=1530576000
enddate=1530604800
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-44",
  "title": "Withings webhook: appli 44: Sleep summary",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "startdate": {
      "description": "Start of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "enddate": {
      "description": "End of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

### appli 54: ECG measurement

A new ECG recording was completed.

#### Example body (form-encoded fields)

```text
userid=12345
appli=54
startdate=1530576000
enddate=1530576030
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-54",
  "title": "Withings webhook: appli 54: ECG measurement",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "startdate": {
      "description": "Start of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "enddate": {
      "description": "End of the data range.",
      "type": "string",
      "x-documented-type": "int (unix epoch)",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

### appli 55: ECG measurement failed

An ECG recording was attempted but failed; notification only.

#### Example body (form-encoded fields)

```text
userid=12345
appli=55
date=1530576000
deviceid=abc123
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-55",
  "title": "Withings webhook: appli 55: ECG measurement failed",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "date": {
      "description": "Single date for event-based notifications.",
      "type": "string",
      "x-documented-type": "string (YYYY-MM-DD) or int (unix epoch)",
      "pattern": "^([0-9]{4}-[0-9]{2}-[0-9]{2}|-?[0-9]+)$",
      "x-type-from": "documented"
    },
    "deviceid": {
      "description": "Source device, when applicable.",
      "type": "string",
      "x-documented-type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

### appli 50: Bed in

The user lay down on the bed (Withings Sleep sensor); notification only.

#### Example body (form-encoded fields)

```text
userid=12345
appli=50
date=1530576000
deviceid=abc123
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-50",
  "title": "Withings webhook: appli 50: Bed in",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "date": {
      "description": "Single date for event-based notifications.",
      "type": "string",
      "x-documented-type": "string (YYYY-MM-DD) or int (unix epoch)",
      "pattern": "^([0-9]{4}-[0-9]{2}-[0-9]{2}|-?[0-9]+)$",
      "x-type-from": "documented"
    },
    "deviceid": {
      "description": "Source device, when applicable.",
      "type": "string",
      "x-documented-type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

### appli 46: User profile change

The user account was deleted, unlinked from your app, or updated; notification only.

#### Example body (form-encoded fields)

```text
userid=12345
appli=46
action=update
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "withings-appli-46",
  "title": "Withings webhook: appli 46: User profile change",
  "description": "Built from the fields Withings documents at https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/. The payload is application/x-www-form-urlencoded; this schema describes the parsed key/value object.",
  "type": "object",
  "properties": {
    "userid": {
      "description": "The user this notification concerns.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "appli": {
      "description": "The notification category.",
      "type": "string",
      "x-documented-type": "int",
      "pattern": "^-?[0-9]+$",
      "x-type-from": "documented"
    },
    "action": {
      "description": "For profile-change events: delete, unlink, or update.",
      "type": "string",
      "enum": [
        "delete",
        "unlink",
        "update"
      ],
      "x-documented-type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Withings developer guide, “Notification categories”](https://developer.withings.com/developer-guide/v3/integration-guide/public-health-data-api/data-api/notifications/notification-content/), read 2026-11-05 (via the site's source-snapshot workflow).

## Wahoo

- **Signature** No signature. Wahoo sends the webhook_token you set in the app's webhook settings inside the JSON body; ignore any request without it. [Source](https://cloud-api.wahooligan.com/)
- **Respond** An HTTP POST with Content-Type application/json; return HTTP 200. The users need the offline_data scope. [Source](https://cloud-api.wahooligan.com/)
- **Retries** Without an HTTP 200, retried after 30 minutes, 4 hours, 24 hours and 72 hours. [Source](https://cloud-api.wahooligan.com/)
- **Note** Duplicates are possible when files are updated or deleted. [Source](https://cloud-api.wahooligan.com/)

[Verify a Wahoo delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-wahoo) · [Wahoo integration guide](https://aifitnessapi.com/integrate/wahoo-api)

### Workout summary

Sent when a workout summary is available for a user whose token has the offline_data scope.

#### Example body

```json
{
"event_type" : "workout_summary" ,
"webhook_token" : <webhook_token> ,
"user" : {
"id" : 60462
},
"workout_summary" : {
"id" : 8297 ,
"ascent_accum" : "450.00" ,
"cadence_avg" : "52.00" ,
"calories_accum" : "1500.00" ,
"distance_accum" : "24909.71" ,
"duration_active_accum" : "179.00" ,
"duration_paused_accum" : "85.00" ,
"duration_total_accum" : "275.20" ,
"heart_rate_avg" : "124.23" ,
"power_bike_np_last" : "150.01" ,
"power_bike_tss_last" : "304.90" ,
"power_avg" : "94.59" ,
"speed_avg" : "10.75" ,
"work_accum" : "104148000.01" ,
"created_at" : "2018-10-23T20:43:50.000Z" ,
"updated_at" : "2018-10-23T20:43:50.000Z" ,
"file" : {
"url" : "https://server.com/4_Mile_Segment_.fit"
},
"workout" : {
"id" : 56519 ,
"starts" : "2015-08-12T09:00:00.000Z" ,
"minutes" : 12 ,
"name" : "Friday Fun" ,
"created_at" : "2018-10-23T20:41:55.000Z" ,
"updated_at" : "2018-10-23T20:41:55.000Z" ,
"plan_id" : null ,
"workout_token" : "123" ,
"workout_type_id" : 40
}
}
}
```

Wahoo's example has the placeholder <webhook_token> where the token goes; the mock sender puts your token there and sends the rest unchanged.

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "wahoo-workout_summary",
  "title": "Wahoo webhook: Workout summary",
  "description": "Built from the fields Wahoo documents at https://cloud-api.wahooligan.com/. JSON body.",
  "type": "object",
  "properties": {
    "event_type": {
      "type": "string",
      "x-type-from": "example"
    },
    "webhook_token": {
      "description": "The token you configured; any request without it should be ignored.",
      "type": "string",
      "x-type-from": "documented"
    },
    "user": {
      "type": "object",
      "x-type-from": "example",
      "properties": {
        "id": {
          "type": "integer",
          "x-type-from": "example"
        }
      },
      "additionalProperties": true
    },
    "workout_summary": {
      "type": "object",
      "x-type-from": "example",
      "properties": {
        "id": {
          "type": "integer",
          "x-type-from": "example"
        },
        "ascent_accum": {
          "type": "string",
          "x-type-from": "example"
        },
        "cadence_avg": {
          "type": "string",
          "x-type-from": "example"
        },
        "calories_accum": {
          "type": "string",
          "x-type-from": "example"
        },
        "distance_accum": {
          "type": "string",
          "x-type-from": "example"
        },
        "duration_active_accum": {
          "type": "string",
          "x-type-from": "example"
        },
        "duration_paused_accum": {
          "type": "string",
          "x-type-from": "example"
        },
        "duration_total_accum": {
          "type": "string",
          "x-type-from": "example"
        },
        "heart_rate_avg": {
          "type": "string",
          "x-type-from": "example"
        },
        "power_bike_np_last": {
          "type": "string",
          "x-type-from": "example"
        },
        "power_bike_tss_last": {
          "type": "string",
          "x-type-from": "example"
        },
        "power_avg": {
          "type": "string",
          "x-type-from": "example"
        },
        "speed_avg": {
          "type": "string",
          "x-type-from": "example"
        },
        "work_accum": {
          "type": "string",
          "x-type-from": "example"
        },
        "created_at": {
          "type": "string",
          "x-type-from": "example"
        },
        "updated_at": {
          "type": "string",
          "x-type-from": "example"
        },
        "file": {
          "type": "object",
          "x-type-from": "example",
          "properties": {
            "url": {
              "type": "string",
              "x-type-from": "example"
            }
          },
          "additionalProperties": true
        },
        "workout": {
          "type": "object",
          "x-type-from": "example",
          "properties": {
            "id": {
              "type": "integer",
              "x-type-from": "example"
            },
            "starts": {
              "type": "string",
              "x-type-from": "example"
            },
            "minutes": {
              "type": "integer",
              "x-type-from": "example"
            },
            "name": {
              "type": "string",
              "x-type-from": "example"
            },
            "created_at": {
              "type": "string",
              "x-type-from": "example"
            },
            "updated_at": {
              "type": "string",
              "x-type-from": "example"
            },
            "workout_token": {
              "type": "string",
              "x-type-from": "example"
            },
            "workout_type_id": {
              "type": "integer",
              "x-type-from": "example"
            }
          },
          "additionalProperties": true
        }
      },
      "additionalProperties": true
    }
  },
  "additionalProperties": true
}
```

From [Wahoo Cloud API documentation, “Webhooks”](https://cloud-api.wahooligan.com/), read 2026-11-05 (via the site's source-snapshot workflow).

## Terra

- **Signature** terra-signature: t=<timestamp>,v1=<signature>; HMAC-SHA256 keyed by the webhook's signing secret over the timestamp, a "." and the raw body. Ignore schemes other than v1. [Source](https://docs.tryterra.co/unified-api/integration-setup/setting-up-data-destinations/webhooks)
- **Respond** Respond within 8 seconds; accept immediately (return 200) and process asynchronously. Sustained timeouts, 5xx and repeated 4xx can trip a per-destination circuit breaker. [Source](https://docs.tryterra.co/unified-api/integration-setup/setting-up-data-destinations/webhooks)
- **Retries** Retried automatically with exponential backoff for several hours; Terra says the number and timing of retries may change without notice. After the retry budget is spent the event is dropped, with no notification. [Source](https://docs.tryterra.co/unified-api/integration-setup/setting-up-data-destinations/webhooks)
- **Note** Do not depend on a specific retry schedule. [Source](https://docs.tryterra.co/unified-api/integration-setup/setting-up-data-destinations/webhooks)
- **Note** Each webhook has its own signing secret; rotating one re-signs its retries with the new secret. [Source](https://docs.tryterra.co/unified-api/integration-setup/setting-up-data-destinations/webhooks)
- **Note** Every event carries a terra-reference header tying it to the request that caused it. [Source](https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md)

[Verify a Terra delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-terra) · [Terra integration guide](https://aifitnessapi.com/integrate/terra-api)

### S3 payload (ping mode)

New data records are available and the destination uses ping mode: download url before it expires and process it as a normal data event.

#### Example body

```json
{
  "status": "success",
  "type": "s3_payload",
  "url": "https://example.com",
  "expires_in": 300,
  "user_id": "0d09d3a4-f6e8-4f1c-9a3e-2b6c1d5e7f80",
  "reference_id": "your-user-ref-123"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "terra-s3_payload",
  "title": "Terra webhook: S3 payload (ping mode)",
  "description": "Built from the fields Terra documents at https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md. JSON body.",
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "x-type-from": "example"
    },
    "type": {
      "type": "string",
      "x-type-from": "example"
    },
    "url": {
      "description": "A pre-signed link to the full data payload. Download it with a GET request.",
      "type": "string",
      "format": "uri",
      "x-type-from": "documented"
    },
    "expires_in": {
      "description": "URL validity in seconds (300 = 5 minutes).",
      "type": "integer",
      "x-type-from": "documented"
    },
    "user_id": {
      "description": "The Terra user the payload belongs to.",
      "type": "string",
      "x-type-from": "documented"
    },
    "reference_id": {
      "description": "The reference_id you set when connecting that user (empty if none was set).",
      "type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Terra API reference, “Event Types”](https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md), read 2026-11-05 (via the site's source-snapshot workflow).

### Healthcheck

Sent periodically to your destination to confirm its health status.

#### Example body

```json
{
  "type": "healthcheck",
  "status": String,
  "creation_timestamp": String,
  "trend_percentage": Number,
  "sent_webhooks_last_hour": Number
}
```

Terra documents this event as a format with type names (String, Number, User) rather than values, so there is no sample to send.

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "terra-healthcheck",
  "title": "Terra webhook: Healthcheck",
  "description": "Built from the fields Terra documents at https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md. JSON body.",
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "healthcheck"
      ],
      "x-type-from": "documented"
    },
    "status": {
      "type": "string",
      "x-type-from": "documented"
    },
    "creation_timestamp": {
      "type": "string",
      "x-type-from": "documented"
    },
    "trend_percentage": {
      "type": "number",
      "x-type-from": "documented"
    },
    "sent_webhooks_last_hour": {
      "type": "number",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Terra API reference, “Event Types”](https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md), read 2026-11-05 (via the site's source-snapshot workflow).

### Device account connection success

A user just finished connecting a device account to your app; save the user and link it with reference_id.

#### Example body

```json
{
  "type": "auth",
  "user": User,
  "status": "success",
  "reference_id": String,
  "widget_session_id": String
}
```

Terra documents this event as a format with type names (String, Number, User) rather than values, so there is no sample to send.

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "terra-auth",
  "title": "Terra webhook: Device account connection success",
  "description": "Built from the fields Terra documents at https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md. JSON body.",
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "auth"
      ],
      "x-type-from": "documented"
    },
    "user": {
      "description": "The Terra user (Terra's User model).",
      "type": "object",
      "x-type-from": "documented"
    },
    "status": {
      "type": "string",
      "x-type-from": "documented"
    },
    "reference_id": {
      "type": "string",
      "x-type-from": "documented"
    },
    "widget_session_id": {
      "type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Terra API reference, “Event Types”](https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md), read 2026-11-05 (via the site's source-snapshot workflow).

### Device account disconnection

A user just disconnected their device account from your app.

#### Example body

```json
{
  "type": "deauth",
  "user": User,
  "status": "success",
  "message": "User has deauthenticated"
}
```

Terra documents this event as a format with type names (String, Number, User) rather than values, so there is no sample to send.

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "terra-deauth",
  "title": "Terra webhook: Device account disconnection",
  "description": "Built from the fields Terra documents at https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md. JSON body.",
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "deauth"
      ],
      "x-type-from": "documented"
    },
    "user": {
      "description": "The Terra user (Terra's User model).",
      "type": "object",
      "x-type-from": "documented"
    },
    "status": {
      "type": "string",
      "x-type-from": "documented"
    },
    "message": {
      "type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Terra API reference, “Event Types”](https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md), read 2026-11-05 (via the site's source-snapshot workflow).

### Activity data event

New activity records are available for the user; overwrite earlier records by metadata.summary_id.

#### Example body

```json
{
  "data": [Activity],
  "user": User,
  "type": "activity",
  "version": String
}
```

Terra documents this event as a format with type names (String, Number, User) rather than values, so there is no sample to send.

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "terra-activity",
  "title": "Terra webhook: Activity data event",
  "description": "Built from the fields Terra documents at https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md. JSON body.",
  "type": "object",
  "properties": {
    "data": {
      "description": "Activity records (Terra's Activity model).",
      "type": "array",
      "x-type-from": "documented"
    },
    "user": {
      "description": "The Terra user (Terra's User model).",
      "type": "object",
      "x-type-from": "documented"
    },
    "type": {
      "type": "string",
      "enum": [
        "activity"
      ],
      "x-type-from": "documented"
    },
    "version": {
      "type": "string",
      "x-type-from": "documented"
    }
  },
  "additionalProperties": true
}
```

From [Terra API reference, “Event Types”](https://docs.tryterra.co/reference/health-and-fitness-api/event-types.md), read 2026-11-05 (via the site's source-snapshot workflow).

## Google Health API

- **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](https://developers.google.com/health/webhooks)
- **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. [Source](https://developers.google.com/health/webhooks)
- **Retries** Anything other than 204, or a timeout, is retried later. Pending notifications are stored for up to 7 days and retried with exponential backoff; older ones are discarded. [Source](https://developers.google.com/health/webhooks)
- **Note** Updates are batched as a JSON array, up to 99 messages per batch, possibly across users and data types. [Source](https://developers.google.com/health/webhooks)
- **Note** Retries can duplicate notifications: make handling idempotent. [Source](https://developers.google.com/health/webhooks)
- **Note** No notification is sent for revoked permissions or deleted accounts. [Source](https://developers.google.com/health/webhooks)

[Verify a Google Health API delivery](https://aifitnessapi.com/tools/webhook-verifier#vendor-google-health) · [Google Health API reference](https://aifitnessapi.com/google-health-api)

### Upsert (time-series data)

Sent for any data addition or modification.

#### Headers in the example

```http
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
```

#### Example body

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

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "google-health-upsert",
  "title": "Google Health API webhook: Upsert (time-series data)",
  "description": "Built from the fields Google Health API documents at https://developers.google.com/health/webhooks. JSON body.",
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "data": {
          "description": "One notification.",
          "type": "object",
          "x-type-from": "example",
          "properties": {
            "version": {
              "type": "string",
              "x-type-from": "example"
            },
            "clientProvidedSubscriptionName": {
              "description": "Your manual subscription's name, or auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN> for automatic subscriptions.",
              "type": "string",
              "x-type-from": "documented"
            },
            "healthUserId": {
              "description": "The Google Health API identifier for the user whose data has changed.",
              "type": "string",
              "x-type-from": "documented"
            },
            "operation": {
              "description": "UPSERT for any data addition or modification; DELETE when a user explicitly deletes data.",
              "type": "string",
              "enum": [
                "UPSERT",
                "DELETE"
              ],
              "x-type-from": "documented"
            },
            "dataType": {
              "type": "string",
              "x-type-from": "example"
            },
            "intervals": {
              "description": "One or more time intervals of the modified data.",
              "type": "array",
              "x-type-from": "documented",
              "items": {
                "description": "One time interval of the modified data.",
                "type": "object",
                "properties": {
                  "physicalTimeInterval": {
                    "description": "The UTC physical time interval of the modified data. Populated for intraday metrics; omitted for daily rollups and full-day activities.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startTime": {
                        "type": "string",
                        "x-type-from": "example"
                      },
                      "endTime": {
                        "type": "string",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  },
                  "civilDateTimeInterval": {
                    "description": "The civil (local) calendar date and time interval of the modified data, without UTC or timezone offsets. Google's field table marks it required, but its own delete examples omit it, so this schema does not require it.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startDateTime": {
                        "type": "object",
                        "x-type-from": "example"
                      },
                      "endDateTime": {
                        "type": "object",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  },
                  "civilIso8601TimeInterval": {
                    "description": "The civil (local) time interval represented as an ISO 8601 string without UTC or timezone offsets.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startTime": {
                        "type": "string",
                        "x-type-from": "example"
                      },
                      "endTime": {
                        "type": "string",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  }
                },
                "required": [
                  "civilIso8601TimeInterval"
                ],
                "additionalProperties": true,
                "x-type-from": "documented"
              }
            }
          },
          "additionalProperties": true
        }
      },
      "additionalProperties": true
    },
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "data": {
            "description": "One notification.",
            "type": "object",
            "x-type-from": "example",
            "properties": {
              "version": {
                "type": "string",
                "x-type-from": "example"
              },
              "clientProvidedSubscriptionName": {
                "description": "Your manual subscription's name, or auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN> for automatic subscriptions.",
                "type": "string",
                "x-type-from": "documented"
              },
              "healthUserId": {
                "description": "The Google Health API identifier for the user whose data has changed.",
                "type": "string",
                "x-type-from": "documented"
              },
              "operation": {
                "description": "UPSERT for any data addition or modification; DELETE when a user explicitly deletes data.",
                "type": "string",
                "enum": [
                  "UPSERT",
                  "DELETE"
                ],
                "x-type-from": "documented"
              },
              "dataType": {
                "type": "string",
                "x-type-from": "example"
              },
              "intervals": {
                "description": "One or more time intervals of the modified data.",
                "type": "array",
                "x-type-from": "documented",
                "items": {
                  "description": "One time interval of the modified data.",
                  "type": "object",
                  "properties": {
                    "physicalTimeInterval": {
                      "description": "The UTC physical time interval of the modified data. Populated for intraday metrics; omitted for daily rollups and full-day activities.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startTime": {
                          "type": "string",
                          "x-type-from": "example"
                        },
                        "endTime": {
                          "type": "string",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    },
                    "civilDateTimeInterval": {
                      "description": "The civil (local) calendar date and time interval of the modified data, without UTC or timezone offsets. Google's field table marks it required, but its own delete examples omit it, so this schema does not require it.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startDateTime": {
                          "type": "object",
                          "x-type-from": "example"
                        },
                        "endDateTime": {
                          "type": "object",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    },
                    "civilIso8601TimeInterval": {
                      "description": "The civil (local) time interval represented as an ISO 8601 string without UTC or timezone offsets.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startTime": {
                          "type": "string",
                          "x-type-from": "example"
                        },
                        "endTime": {
                          "type": "string",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "civilIso8601TimeInterval"
                  ],
                  "additionalProperties": true,
                  "x-type-from": "documented"
                }
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      }
    }
  ]
}
```

From [Google Health API, “Webhook subscriptions”](https://developers.google.com/health/webhooks), read 2026-11-05.

### Delete (time-series data)

Sent when a user explicitly deletes data. Google's example arrives as an array.

#### Example body

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

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "google-health-delete-time-series",
  "title": "Google Health API webhook: Delete (time-series data)",
  "description": "Built from the fields Google Health API documents at https://developers.google.com/health/webhooks. JSON body.",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "data": {
        "description": "One notification.",
        "type": "object",
        "x-type-from": "example",
        "properties": {
          "version": {
            "type": "string",
            "x-type-from": "example"
          },
          "clientProvidedSubscriptionName": {
            "description": "Your manual subscription's name, or auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN> for automatic subscriptions.",
            "type": "string",
            "x-type-from": "documented"
          },
          "healthUserId": {
            "description": "The Google Health API identifier for the user whose data has changed.",
            "type": "string",
            "x-type-from": "documented"
          },
          "operation": {
            "description": "UPSERT for any data addition or modification; DELETE when a user explicitly deletes data.",
            "type": "string",
            "enum": [
              "UPSERT",
              "DELETE"
            ],
            "x-type-from": "documented"
          },
          "dataType": {
            "type": "string",
            "x-type-from": "example"
          },
          "intervals": {
            "description": "One or more time intervals of the modified data.",
            "type": "array",
            "x-type-from": "documented",
            "items": {
              "description": "One time interval of the modified data.",
              "type": "object",
              "properties": {
                "physicalTimeInterval": {
                  "description": "The UTC physical time interval of the modified data. Populated for intraday metrics; omitted for daily rollups and full-day activities.",
                  "type": "object",
                  "x-type-from": "documented",
                  "properties": {
                    "startTime": {
                      "type": "string",
                      "x-type-from": "example"
                    },
                    "endTime": {
                      "type": "string",
                      "x-type-from": "example"
                    }
                  },
                  "additionalProperties": true
                },
                "civilDateTimeInterval": {
                  "description": "The civil (local) calendar date and time interval of the modified data, without UTC or timezone offsets. Google's field table marks it required, but its own delete examples omit it, so this schema does not require it.",
                  "type": "object",
                  "x-type-from": "documented",
                  "properties": {
                    "startDateTime": {
                      "type": "object",
                      "x-type-from": "example"
                    },
                    "endDateTime": {
                      "type": "object",
                      "x-type-from": "example"
                    }
                  },
                  "additionalProperties": true
                },
                "civilIso8601TimeInterval": {
                  "description": "The civil (local) time interval represented as an ISO 8601 string without UTC or timezone offsets.",
                  "type": "object",
                  "x-type-from": "documented",
                  "properties": {
                    "startTime": {
                      "type": "string",
                      "x-type-from": "example"
                    },
                    "endTime": {
                      "type": "string",
                      "x-type-from": "example"
                    }
                  },
                  "additionalProperties": true
                }
              },
              "required": [
                "civilIso8601TimeInterval"
              ],
              "additionalProperties": true,
              "x-type-from": "documented"
            }
          }
        },
        "additionalProperties": true
      }
    },
    "additionalProperties": true
  }
}
```

From [Google Health API, “Webhook subscriptions”](https://developers.google.com/health/webhooks), read 2026-11-05.

### Delete (identifiable data)

A delete of identifiable data such as a sleep carries the deleted record's recordId.

#### Example body

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

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "google-health-delete-identifiable",
  "title": "Google Health API webhook: Delete (identifiable data)",
  "description": "Built from the fields Google Health API documents at https://developers.google.com/health/webhooks. JSON body.",
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "data": {
          "description": "One notification.",
          "type": "object",
          "x-type-from": "example",
          "properties": {
            "version": {
              "type": "string",
              "x-type-from": "example"
            },
            "clientProvidedSubscriptionName": {
              "description": "Your manual subscription's name, or auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN> for automatic subscriptions.",
              "type": "string",
              "x-type-from": "documented"
            },
            "healthUserId": {
              "description": "The Google Health API identifier for the user whose data has changed.",
              "type": "string",
              "x-type-from": "documented"
            },
            "operation": {
              "description": "UPSERT for any data addition or modification; DELETE when a user explicitly deletes data.",
              "type": "string",
              "enum": [
                "UPSERT",
                "DELETE"
              ],
              "x-type-from": "documented"
            },
            "dataType": {
              "type": "string",
              "x-type-from": "example"
            },
            "intervals": {
              "description": "One or more time intervals of the modified data.",
              "type": "array",
              "x-type-from": "documented",
              "items": {
                "description": "One time interval of the modified data.",
                "type": "object",
                "properties": {
                  "physicalTimeInterval": {
                    "description": "The UTC physical time interval of the modified data. Populated for intraday metrics; omitted for daily rollups and full-day activities.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startTime": {
                        "type": "string",
                        "x-type-from": "example"
                      },
                      "endTime": {
                        "type": "string",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  },
                  "civilDateTimeInterval": {
                    "description": "The civil (local) calendar date and time interval of the modified data, without UTC or timezone offsets. Google's field table marks it required, but its own delete examples omit it, so this schema does not require it.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startDateTime": {
                        "type": "object",
                        "x-type-from": "example"
                      },
                      "endDateTime": {
                        "type": "object",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  },
                  "civilIso8601TimeInterval": {
                    "description": "The civil (local) time interval represented as an ISO 8601 string without UTC or timezone offsets.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startTime": {
                        "type": "string",
                        "x-type-from": "example"
                      },
                      "endTime": {
                        "type": "string",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  }
                },
                "required": [
                  "civilIso8601TimeInterval"
                ],
                "additionalProperties": true,
                "x-type-from": "documented"
              }
            },
            "recordId": {
              "type": "string",
              "x-type-from": "example"
            }
          },
          "additionalProperties": true
        }
      },
      "additionalProperties": true
    },
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "data": {
            "description": "One notification.",
            "type": "object",
            "x-type-from": "example",
            "properties": {
              "version": {
                "type": "string",
                "x-type-from": "example"
              },
              "clientProvidedSubscriptionName": {
                "description": "Your manual subscription's name, or auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN> for automatic subscriptions.",
                "type": "string",
                "x-type-from": "documented"
              },
              "healthUserId": {
                "description": "The Google Health API identifier for the user whose data has changed.",
                "type": "string",
                "x-type-from": "documented"
              },
              "operation": {
                "description": "UPSERT for any data addition or modification; DELETE when a user explicitly deletes data.",
                "type": "string",
                "enum": [
                  "UPSERT",
                  "DELETE"
                ],
                "x-type-from": "documented"
              },
              "dataType": {
                "type": "string",
                "x-type-from": "example"
              },
              "intervals": {
                "description": "One or more time intervals of the modified data.",
                "type": "array",
                "x-type-from": "documented",
                "items": {
                  "description": "One time interval of the modified data.",
                  "type": "object",
                  "properties": {
                    "physicalTimeInterval": {
                      "description": "The UTC physical time interval of the modified data. Populated for intraday metrics; omitted for daily rollups and full-day activities.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startTime": {
                          "type": "string",
                          "x-type-from": "example"
                        },
                        "endTime": {
                          "type": "string",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    },
                    "civilDateTimeInterval": {
                      "description": "The civil (local) calendar date and time interval of the modified data, without UTC or timezone offsets. Google's field table marks it required, but its own delete examples omit it, so this schema does not require it.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startDateTime": {
                          "type": "object",
                          "x-type-from": "example"
                        },
                        "endDateTime": {
                          "type": "object",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    },
                    "civilIso8601TimeInterval": {
                      "description": "The civil (local) time interval represented as an ISO 8601 string without UTC or timezone offsets.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startTime": {
                          "type": "string",
                          "x-type-from": "example"
                        },
                        "endTime": {
                          "type": "string",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "civilIso8601TimeInterval"
                  ],
                  "additionalProperties": true,
                  "x-type-from": "documented"
                }
              },
              "recordId": {
                "type": "string",
                "x-type-from": "example"
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      }
    }
  ]
}
```

From [Google Health API, “Webhook subscriptions”](https://developers.google.com/health/webhooks), read 2026-11-05.

### Full-day activity

Full-day activities and daily aggregates omit physicalTimeInterval; rely on the civil fields.

#### Example body

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

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "google-health-full-day",
  "title": "Google Health API webhook: Full-day activity",
  "description": "Built from the fields Google Health API documents at https://developers.google.com/health/webhooks. JSON body.",
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "data": {
          "description": "One notification.",
          "type": "object",
          "x-type-from": "example",
          "properties": {
            "version": {
              "type": "string",
              "x-type-from": "example"
            },
            "clientProvidedSubscriptionName": {
              "description": "Your manual subscription's name, or auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN> for automatic subscriptions.",
              "type": "string",
              "x-type-from": "documented"
            },
            "healthUserId": {
              "description": "The Google Health API identifier for the user whose data has changed.",
              "type": "string",
              "x-type-from": "documented"
            },
            "operation": {
              "description": "UPSERT for any data addition or modification; DELETE when a user explicitly deletes data.",
              "type": "string",
              "enum": [
                "UPSERT",
                "DELETE"
              ],
              "x-type-from": "documented"
            },
            "dataType": {
              "type": "string",
              "x-type-from": "example"
            },
            "intervals": {
              "description": "One or more time intervals of the modified data.",
              "type": "array",
              "x-type-from": "documented",
              "items": {
                "description": "One time interval of the modified data.",
                "type": "object",
                "properties": {
                  "physicalTimeInterval": {
                    "description": "The UTC physical time interval of the modified data. Populated for intraday metrics; omitted for daily rollups and full-day activities.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startTime": {
                        "type": "string",
                        "x-type-from": "example"
                      },
                      "endTime": {
                        "type": "string",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  },
                  "civilDateTimeInterval": {
                    "description": "The civil (local) calendar date and time interval of the modified data, without UTC or timezone offsets. Google's field table marks it required, but its own delete examples omit it, so this schema does not require it.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startDateTime": {
                        "type": "object",
                        "x-type-from": "example"
                      },
                      "endDateTime": {
                        "type": "object",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  },
                  "civilIso8601TimeInterval": {
                    "description": "The civil (local) time interval represented as an ISO 8601 string without UTC or timezone offsets.",
                    "type": "object",
                    "x-type-from": "documented",
                    "properties": {
                      "startTime": {
                        "type": "string",
                        "x-type-from": "example"
                      },
                      "endTime": {
                        "type": "string",
                        "x-type-from": "example"
                      }
                    },
                    "additionalProperties": true
                  }
                },
                "required": [
                  "civilIso8601TimeInterval"
                ],
                "additionalProperties": true,
                "x-type-from": "documented"
              }
            }
          },
          "additionalProperties": true
        }
      },
      "additionalProperties": true
    },
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "data": {
            "description": "One notification.",
            "type": "object",
            "x-type-from": "example",
            "properties": {
              "version": {
                "type": "string",
                "x-type-from": "example"
              },
              "clientProvidedSubscriptionName": {
                "description": "Your manual subscription's name, or auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN> for automatic subscriptions.",
                "type": "string",
                "x-type-from": "documented"
              },
              "healthUserId": {
                "description": "The Google Health API identifier for the user whose data has changed.",
                "type": "string",
                "x-type-from": "documented"
              },
              "operation": {
                "description": "UPSERT for any data addition or modification; DELETE when a user explicitly deletes data.",
                "type": "string",
                "enum": [
                  "UPSERT",
                  "DELETE"
                ],
                "x-type-from": "documented"
              },
              "dataType": {
                "type": "string",
                "x-type-from": "example"
              },
              "intervals": {
                "description": "One or more time intervals of the modified data.",
                "type": "array",
                "x-type-from": "documented",
                "items": {
                  "description": "One time interval of the modified data.",
                  "type": "object",
                  "properties": {
                    "physicalTimeInterval": {
                      "description": "The UTC physical time interval of the modified data. Populated for intraday metrics; omitted for daily rollups and full-day activities.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startTime": {
                          "type": "string",
                          "x-type-from": "example"
                        },
                        "endTime": {
                          "type": "string",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    },
                    "civilDateTimeInterval": {
                      "description": "The civil (local) calendar date and time interval of the modified data, without UTC or timezone offsets. Google's field table marks it required, but its own delete examples omit it, so this schema does not require it.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startDateTime": {
                          "type": "object",
                          "x-type-from": "example"
                        },
                        "endDateTime": {
                          "type": "object",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    },
                    "civilIso8601TimeInterval": {
                      "description": "The civil (local) time interval represented as an ISO 8601 string without UTC or timezone offsets.",
                      "type": "object",
                      "x-type-from": "documented",
                      "properties": {
                        "startTime": {
                          "type": "string",
                          "x-type-from": "example"
                        },
                        "endTime": {
                          "type": "string",
                          "x-type-from": "example"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "civilIso8601TimeInterval"
                  ],
                  "additionalProperties": true,
                  "x-type-from": "documented"
                }
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      }
    }
  ]
}
```

From [Google Health API, “Webhook subscriptions”](https://developers.google.com/health/webhooks), read 2026-11-05.

### Endpoint verification

Two POSTs when you create a subscriber or change its endpoint: one with your Authorization header, one without.

#### Example body

```json
{
  "type": "verification"
}
```

#### JSON Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$anchor": "google-health-verification",
  "title": "Google Health API webhook: Endpoint verification",
  "description": "Built from the fields Google Health API documents at https://developers.google.com/health/webhooks. JSON body.",
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "verification"
          ],
          "x-type-from": "documented"
        }
      },
      "additionalProperties": true
    },
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "verification"
            ],
            "x-type-from": "documented"
          }
        },
        "additionalProperties": true
      }
    }
  ]
}
```

From [Google Health API, “Webhook subscriptions”](https://developers.google.com/health/webhooks), read 2026-11-05.

## Suunto

- **Signature** Not documented on Suunto's FAQ. The webhook address is set in your app's OAuth settings in Suunto API Zone and must be publicly reachable. [Source](https://apizone.suunto.com/faq)
- **Respond** Fetch the workout as soon as the notification arrives; the FAQ states no response code or time limit. [Source](https://apizone.suunto.com/faq)
- **Retries** Not documented on the pages read here. [Source](https://apizone.suunto.com/faq)
- **Note** Workouts only: there is no notification for daily activity. [Source](https://apizone.suunto.com/faq)

[Suunto integration guide](https://aifitnessapi.com/integrate/suunto-api)

Two parameters in the POST: username (find the user's token by the JWT's user claim) and workoutid (fetch the workout's FIT file). No example, encoding or signature is shown, so there is no schema or sample here. [Source](https://apizone.suunto.com/faq)

## Send a signed sample to your own handler

[send-sample.mjs](https://aifitnessapi.com/webhooks/send-sample.mjs) posts any example above to a URL on your machine, signed the way its vendor documents.

```sh
curl -O https://aifitnessapi.com/webhooks/send-sample.mjs
node send-sample.mjs --list
node send-sample.mjs whoop v2-sleep-updated --url http://localhost:3000/webhooks/whoop --secret "$WHOOP_CLIENT_SECRET"
node send-sample.mjs withings appli-44 --url http://localhost:3000/webhooks/withings --secret my-callback-token
node send-sample.mjs terra s3_payload --dry-run
```

It needs Node 18 or newer and nothing else. It sends only to localhost, 127.0.0.1 or ::1 unless you pass --allow-remote, and --dry-run prints the request without sending it.

Each request is a vendor's published example, signed now with your --secret the way that vendor documents: HMAC for Oura, WHOOP, Terra and Polar, the token on the URL for Withings, webhook_token in the body for Wahoo, the Authorization header for the Google Health API. Strava's events are unsigned. Without --secret it uses the demo secret the webhook verifier's examples use, so a --dry-run request pasted into the verifier checks as valid.

It cannot produce Google's GOOGLE-HEALTH-API-SIGNATURE header, which Google signs with its own private key, so it sends none; test that check against a real delivery.

## Download every schema

[schemas.json](https://aifitnessapi.com/webhooks/schemas.json) is one JSON Schema (draft 2020-12) bundle with each payload under $defs; reference one as schemas.json#<vendor>-<payload>, the id on its section above. Each property says in x-type-from whether its type comes from the vendor’s field table or only from its example, and only fields a vendor calls required are required.

## Not covered, and why

- **Garmin.** No public Garmin page read for this library shows a push payload. [Garmin guide](https://aifitnessapi.com/integrate/garmin-api).
- **COROS.** COROS's help center lists webhook push as part of its Partner API but shows no payload. [COROS guide](https://aifitnessapi.com/integrate/coros-api).
- **Dexcom.** Dexcom's webhook documentation was not read for this library, so nothing is reproduced. [Dexcom guide](https://aifitnessapi.com/integrate/dexcom-api).
- **Fitbit Web API.** Google says the legacy Fitbit Web API is turned off on November 2, 2026; its successor's notifications are under the Google Health API above. [Fitbit Web API retirement](https://aifitnessapi.com/fitbit-api-shutdown).

## Related pages

- [What webhooks are and why fitness APIs use them](https://aifitnessapi.com/learn/what-are-webhooks)
- [Webhooks or polling for fitness data](https://aifitnessapi.com/learn/webhooks-vs-polling-for-fitness-data)
- [Designing idempotent webhook ingestion](https://aifitnessapi.com/architecture/webhook-ingestion)
- [A replay-safe webhook receiver, in code](https://aifitnessapi.com/cookbook/webhook-receiver)
- [Testing webhook handlers locally with replays](https://aifitnessapi.com/test/webhooks-locally)
- [Why a Strava webhook stops firing](https://aifitnessapi.com/fix/strava-webhook-not-firing)
- [Check a received webhook's signature](https://aifitnessapi.com/tools/webhook-verifier)
- [Build each vendor's OAuth request](https://aifitnessapi.com/tools/oauth-playground)

## Next step

Turning this into a choice of API:

- [The Best Wearable Health-Data Aggregator APIs (2026)](https://aifitnessapi.com/fitness-apis/health-data-aggregator-apis)
- [Health data aggregator API pricing: what Terra, Junction, Rook, Spike and four more charge](https://aifitnessapi.com/pricing/health-data-aggregator-pricing)
- [Integration Guides](https://aifitnessapi.com/integrate)
