All our webhook events have the following standard structure:

  • A top-level event_type field, specifying the payload type contained in the data field.
  • A top-level data field containing the event-specific payload.

Schemas and examples are available through the Event Catalog tab in the Webhooks section of our Web app.

{
  "data": {
    # ... event specific data
  },
  "event_type": "daily.data.glucose.created"
}

Wearable data events

Wearable data events have the following extended standard structure:

  • An event_type field for differentiating the payload contained in the data field.
  • A data field, which comprises of:
    • The UUID of the Vital user for which this event is intended — note that this is not the client_user_id of the Vital user. (JSON path: $.data.user_id)
    • The metadata about the data source of this event (JSON path: $.data.source)
      • name: The human-readable name of the source
      • slug: The stable identifier Vital assigned to the source — see Supported providers for the complete list.
      • logo: The URL to an icon representing the source.
    • Any number of event-specific fields.
Wearable data event structure
{
  "data": {
    # ... event specific fields

    # Common wearable data event fields
    "source": {
      "logo": "https://path/to/example",
      "name": "Fitbit",
      "slug": "fitbit"
    },
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
  },
  "event_type": "daily.data.glucose.created"
}

Summary types

Each time a summary object is created or updated, Vital emits a data event for the occurrence. The object is made available directly at the top-level data field (JSON path: $.data), alongside the standard fields listed above.

Summary types are uniquely identified by its id. If multiple events are received at the same id, the last event replaces all its previous versions.
{
  "data": {
    # ... All summary object fields

    # Common wearable data event fields
    "source": {
      "logo": "https://path/to/example",
      "name": "Fitbit",
      "slug": "fitbit"
    },
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
  },
  "event_type": "daily.data.activity.created"
}

Timeseries types (except for Workout Streams)

Each time a batch of timeseries samples has been created or updated, Vital emits a data event for the batch of samples. The samples are made available under an inner data field under the top-level data field (JSON path: $.data.data).

Each sample can be uniquely identified by a triple key (3-tuple) comprising of its resource type name, its source (ID or slug) and its timestamp. If multiple samples are received at the same tuple, the last sample replaces all its previous versions.
While most timeseries data are immutable, there have been exceptions of some sources aggregating some data types using time bucketing, and some may even send updates ASAP on time buckets that have incomplete data. For example, activity timeseries data from Apple HealthKit (integration in production) and Garmin (integration planned) do exhibit such behaviour.

These exceptions are the rationale behind the recommended deduplication semantic as stated above.
{
  "data": {
    "data": [
      {
        "timestamp": "2023-05-16T10:00:00+00:00",
        "timezone_offset": 3600,
        "type": null,
        "unit": "count",
        "value": 237
      },
      {
        "timestamp": "2023-05-16T11:00:00+00:00",
        "timezone_offset": 3600,
        "type": null,
        "unit": "count",
        "value": 451
      },
      # ... other timeseries samples in the batch
    ],
    "source_id": 16,

    # Common wearable data event fields
    "source": {
      "logo": "https://storage.googleapis.com/vital-assets/apple_health.png",
      "name": "Apple HealthKit",
      "slug": "apple_health_kit"
    },
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
  },
  "event_type": "daily.data.steps.created"
}

Workout Streams

While Workout Streams are classified as a timeseries type, it contains a huge amount of timeseries data which cannot be delivered by our webhook message transport.

So events for Webhook Streams are shallow, with only some minimal metadata about the workout. Successful reception of the event indicates that the data are available to be read via our REST API, e.g., the Workout Stream endpoint GET /v2/timeseries/workouts/{workout_id}/stream.

Workout Stream created
{
  "data": {
    "message": "Due to payload size limits, to access the workout stream, please use the /workouts/edc80dd0-8cc8-4ec8-8b80-931fd9a3309a/stream endpoint.",
    "provider_id": "51278201336",
    "sport": {
      "id": 57,
      "name": "Other",
      "slug": "other"
    },
    "workout_id": "fdc70dd0-8cc8-4ec8-8b80-121fd9a3302a"

    # Common wearable data event fields
    "source": {
      "logo": "https://storage.googleapis.com/vital-assets/fitbit.png",
      "name": "Fitbit",
      "slug": "fitbit"
    },
    "user_id": "bf6222fb-3b81-4201-9630-118bfee01e03",
  },
  "event_type": "daily.data.workout_stream.created"
}