Skip to main content

Overview

Garmin sends data to Open Wearables via two webhook types:
  • PING: Contains callback URLs with temporary tokens to fetch data
  • PUSH: Contains inline activity metadata and wellness summaries
Both endpoints handle all 16 Garmin data types, including activities, sleep, daily summaries, heart rate variability, and more.
These endpoints are designed to be called by Garmin’s servers, not by your application. Configure webhook URLs in the Garmin Connect Developer Portal.

Authentication

Requires garmin-client-id header to verify the request originates from Garmin.

PING Webhook

Endpoint

POST /api/v1/webhooks/garmin/ping

Description

Receives PING notifications when new data is available. The payload contains callback URLs that must be fetched to retrieve the actual data.

Headers

garmin-client-id
string
required
Garmin OAuth client ID for verification

Request Body

Garmin sends a JSON payload with data type arrays. When multiple backfill requests occur within 5 minutes, responses may be batched into a single payload.
Example PING Payload
{
  "activities": [
    {
      "userId": "9876543210",
      "callbackURL": "https://apis.garmin.com/wellness-api/rest/activities?uploadStartTimeInSeconds=1234567890&uploadEndTimeInSeconds=1234567900&token=XXXXX"
    }
  ],
  "sleeps": [
    {
      "userId": "9876543210",
      "callbackURL": "https://apis.garmin.com/wellness-api/rest/sleeps?uploadStartTimeInSeconds=1234567890&uploadEndTimeInSeconds=1234567900&token=YYYYY"
    }
  ],
  "dailies": [
    {
      "userId": "9876543210",
      "callbackURL": "https://apis.garmin.com/wellness-api/rest/dailies?uploadStartTimeInSeconds=1234567890&uploadEndTimeInSeconds=1234567900&token=ZZZZZ"
    }
  ]
}

Response

processed
integer
Number of items successfully processed
errors
array
List of error messages encountered during processing
activities
array
Results for activity processing:
  • garmin_user_id: Garmin user identifier
  • internal_user_id: Open Wearables user UUID
  • activities_count: Number of activities fetched
  • status: “fetched” or error state
wellness
object
Results per wellness data type (sleeps, dailies, epochs, etc.):
  • processed: Items fetched from callback URL
  • saved: Items successfully saved to database
  • errors: List of errors for this type
  • items: Detailed results per user
backfill_chained
array
List of user IDs for which next backfill type was triggered
userPermissionsChange
object
If present, results of permission scope updates:
  • updated: Number of connections updated
  • errors: List of errors
deregistrations
object
If present, results of connection revocations:
  • revoked: Number of connections revoked
  • errors: List of errors

Example Response

200 - Success
{
  "processed": 1,
  "errors": [],
  "activities": [
    {
      "garmin_user_id": "9876543210",
      "internal_user_id": "550e8400-e29b-41d4-a716-446655440000",
      "activities_count": 3,
      "status": "fetched"
    }
  ],
  "wellness": {
    "sleeps": {
      "processed": 1,
      "saved": 1,
      "errors": [],
      "items": [
        {
          "garmin_user_id": "9876543210",
          "internal_user_id": "550e8400-e29b-41d4-a716-446655440000",
          "type": "sleeps",
          "fetched": 1,
          "saved": 1
        }
      ],
      "new_success_users": ["550e8400-e29b-41d4-a716-446655440000"]
    },
    "dailies": {
      "processed": 1,
      "saved": 1,
      "errors": [],
      "items": [
        {
          "garmin_user_id": "9876543210",
          "internal_user_id": "550e8400-e29b-41d4-a716-446655440000",
          "type": "dailies",
          "fetched": 1,
          "saved": 1
        }
      ],
      "new_success_users": ["550e8400-e29b-41d4-a716-446655440000"]
    }
  },
  "backfill_chained": ["550e8400-e29b-41d4-a716-446655440000"]
}
401 - Unauthorized
{
  "detail": "Missing garmin-client-id header"
}
500 - Internal Error
{
  "detail": "Failed to process webhook"
}

PUSH Webhook

Endpoint

POST /api/v1/webhooks/garmin/push

Description

Receives PUSH notifications with inline data. Activity metadata and wellness summaries are included directly in the payload (no callback URL).

Headers

garmin-client-id
string
required
Garmin OAuth client ID for verification

Request Body

Garmin sends a JSON payload with inline data arrays.
Example PUSH Payload
{
  "activities": [
    {
      "userId": "9876543210",
      "summaryId": "21047282990",
      "activityId": 21047282990,
      "activityName": "Morning Run",
      "startTimeInSeconds": 1763597760,
      "startTimeOffsetInSeconds": 3600,
      "durationInSeconds": 3600,
      "activityType": "RUNNING",
      "deviceName": "Forerunner 965",
      "manual": false,
      "isWebUpload": false,
      "distanceInMeters": 10000,
      "steps": 12500,
      "activeKilocalories": 650,
      "averageHeartRateInBeatsPerMinute": 155,
      "maxHeartRateInBeatsPerMinute": 182
    }
  ],
  "sleeps": [
    {
      "userId": "9876543210",
      "summaryId": "123456789",
      "calendarDate": "2024-03-15",
      "startTimeInSeconds": 1710460800,
      "startTimeOffsetInSeconds": 3600,
      "durationInSeconds": 28800,
      "unmeasurableSleepInSeconds": 0,
      "deepSleepDurationInSeconds": 7200,
      "lightSleepDurationInSeconds": 18000,
      "remSleepInSeconds": 3600,
      "awakeDurationInSeconds": 0
    }
  ]
}

Supported Data Types

All 16 Garmin data types are processed:
  • activities: Workouts and exercises
  • activityDetails: Detailed activity metrics
  • sleeps: Sleep sessions
  • dailies: Daily summaries (steps, calories, etc.)
  • epochs: Intraday activity samples
  • bodyComps: Body composition measurements
  • hrv: Heart rate variability
  • stressDetails: Stress level data
  • respiration: Respiration rate
  • pulseOx: Blood oxygen saturation
  • bloodPressures: Blood pressure readings
  • userMetrics: User profile metrics
  • skinTemp: Skin temperature
  • healthSnapshot: Health snapshots
  • moveiq: Auto-detected activities
  • mct: Movement data

Response

processed
integer
Number of activities processed
saved
integer
Number of activities saved to database
errors
array
List of error messages
activities
array
Results for each activity:
  • activity_id: Garmin activity ID
  • name: Activity name
  • type: Activity type (RUNNING, CYCLING, etc.)
  • garmin_user_id: Garmin user ID
  • internal_user_id: Open Wearables user UUID
  • record_ids: Array of created EventRecord UUIDs
  • status: “saved”, “duplicate”, “user_not_found”, or “validation_error”
wellness
object
Results per wellness data type:
  • processed: Number of items received
  • saved: Number of items saved
backfill_chained
array
List of user IDs for which next backfill type was triggered

Example Response

200 - Success
{
  "processed": 1,
  "saved": 1,
  "errors": [],
  "activities": [
    {
      "activity_id": 21047282990,
      "name": "Morning Run",
      "type": "RUNNING",
      "garmin_user_id": "9876543210",
      "internal_user_id": "550e8400-e29b-41d4-a716-446655440000",
      "record_ids": ["abc12345-e89b-12d3-a456-426614174000"],
      "status": "saved"
    }
  ],
  "wellness": {
    "sleeps": {
      "processed": 1,
      "saved": 1
    },
    "dailies": {
      "processed": 1,
      "saved": 1
    }
  },
  "backfill_chained": []
}

Special Events

Permission Changes

When a user changes their data sharing permissions in Garmin Connect, a userPermissionsChange array is included:
{
  "userPermissionsChange": [
    {
      "userId": "9876543210",
      "permissions": ["ACTIVITY_EXPORT", "HEALTH_EXPORT"]
    }
  ]
}
The connection’s scope field is updated to reflect the new permissions.

Deregistrations

When a user removes the app from Garmin Connect, a deregistrations array is included:
{
  "deregistrations": [
    {
      "userId": "9876543210"
    }
  ]
}
The connection is marked as revoked.

Health Check

Endpoint

GET /api/v1/webhooks/garmin/health

Description

Health check endpoint for monitoring webhook availability.

Response

200 - OK
{
  "status": "ok",
  "service": "garmin-webhooks"
}

Setup Guide

  1. Configure webhook URLs in the Garmin Connect Developer Portal
  2. Set both PING and PUSH notification URLs:
    • PING: https://your-domain.com/api/v1/webhooks/garmin/ping
    • PUSH: https://your-domain.com/api/v1/webhooks/garmin/push
  3. Ensure your server includes the garmin-client-id header in responses
  4. Test with Garmin’s webhook testing tool

Webhook Batching

When multiple backfill requests occur within 5 minutes, Garmin may batch responses into a single webhook call. This is normal behavior and handled automatically.

Error Handling

  • Webhooks always return HTTP 200 to prevent Garmin from retrying
  • Errors are logged and included in the errors array
  • Individual item failures don’t stop processing of other items
  • User not found errors are logged but don’t cause webhook failure

Build docs developers (and LLMs) love