Webhook notifications
Webhook notifications enable your application to receive real-time updates from Suunto about users’ activities. With this feature, you can integrate Suunto data into your system efficiently. Here's how to configure, verify, and handle webhook notifications.
Setting Up
Configure Webhook URLs
Log in to your OAuth application settings at the profile -page.
Configure the notification URLs where webhook requests should be sent.
Set a Notification Secret
Define a notification secret in your app settings.
Store the notification secret securely so it's available to the service handling the webhook requests.

Types of Notifications
Suunto sends three types of webhook notifications:
New Workout: Triggered when a user uploads a new workout.
New Route: Triggered when a user creates a new route.
24/7 Activity Data: Triggered when a user uploads 24/7 activity samples (e.g., heart rate, step count).
24/7 Sleep Data: Triggered when a user uploads 24/7 sleep samples (e.g., sleep duration, hrv).
24/7 Recovery Data: Triggered when a user uploads 24/7 recovery samples (e.g., balance, stress).
The type of notification is specified in the type
field of the request body or form parameters.
Webhook Request Examples
New Workout Notification
Content-Type: application/json
{
"type": "WORKOUT_CREATED",
"username": "johndoe123
",
"workout": {
"workoutKey": "67604889401b942184624cb8",
"activityId": 3,
"startTime": 1702729200000,
"totalTime": 3600,
"energyConsumption": 500,
"startPosition": {
"x": 24.97242731,
"y": 60.27185791
},
"stepCount": 8500,
"totalAscent": 120.5,
"totalDescent": 110.3,
"totalDistance": 10000,
"hrdata": {
"workoutAvgHR": 145,
"workoutMaxHR": 175
},
"avgSpeed": 2.78,
"maxSpeed": 6,
"timeOffsetInMinutes": 120
},
"gear": {
"manufacturer": "Suunto",
"name": "Suunto Vertical",
"productType": "SPORT_WATCH"
}
}
Refer to Workout API Documentation for details about the workout
-object.
Legacy Form-Based Notifications
The legacy format for workout notifications is still supported. These notifications are sent to a separate notification URL and use form-encoded parameters in the request body.
Content-Type: application/x-www-form-urlencoded
workoutid=workout123&username=johndoe123
New Route Notification
Content-Type: application/json
{
"type": "ROUTE_CREATED",
"username": "johndoe123",
"route": {
"id": "route123",
"description": "A scenic trail through the mountains.",
"visibility": "public",
"activityIds": [101, 102],
"startPoint": { "altitude": 150.5, "latitude": 60.192059, "longitude": 24.945831 },
"centerPoint": { "altitude": 200.0, "latitude": 60.200000, "longitude": 24.950000 },
"endPoint": { "altitude": 100.0, "latitude": 60.203038, "longitude": 24.960817 },
"created": 1702215600000,
"averageSpeed": 5.8,
"totalDistance": 12345.67,
"modified": 1702302000000,
"watchEnabled": true,
"turnWaypointsEnabled": true
}
}
Refer to the Route API Documentation for details about the route
-object.
24/7 Activity Data Notification
Content-Type: application/json
{
"type": "SUUNTO_247_ACTIVITY_CREATED",
"username": "johndoe123",
"samples": [
{
"timestamp": "2024-12-10T14:50:00.000+02:00",
"entryData": { "HR": 71, "StepCount": 169, "EnergyConsumption": 29308 }
},
{
"timestamp": "2024-12-10T15:00:00.000+02:00",
"entryData": { "HR": 75, "StepCount": 200, "EnergyConsumption": 31000 }
}
]
}
Refer to the 247 activity API Documentation for details about the samples
-array.
24/7 Sleep Data Notification
Content-Type: application/json
{
"type": "SUUNTO_247_SLEEP_CREATED",
"username": "johndoe123",
"samples": [
{
"timestamp": "2025-01-05T23:29:00.000+02:00",
"entryData": {
"DeepSleepDuration": 2850,
"LightSleepDuration": 22470,
"REMSleepDuration": 5280,
"Duration": 32460,
"HRAvg": 45,
"HRMin": 42,
"SleepQualityScore": 81,
"SleepId": 1736112540,
"BedtimeStart": "2025-01-05T23:29:00.000+02:00",
"BedtimeEnd": "2025-01-06T08:30:00.000+02:00",
"MaxSpo2": 1,
"Altitude": 12,
"AvgHRV": 47,
"AvgHRVSampleCount": 107,
"IsNap": false,
"SleepOnsetLatencyDuration": 1200,
"WakeAfterSleepOnsetDuration": 630,
"WakeBeforeOffBedDuration": 30,
"DateTime": "2025-01-05T23:29:00.000+02:00"
}
}
]
}
Refer to the 247 sleep API Documentation for details about the samples
-array.
24/7 Recovery Data Notification
Content-Type: application/json
{
"type": "SUUNTO_247_RECOVERY_CREATED",
"username": "johndoe123",
"samples": [
{
"timestamp": "2025-01-05T23:29:00.000+02:00",
"entryData": {
"Balance": 0.93,
"StressState": 3
}
}
]
}
Refer to the 247 recovery API Documentation for details about the samples
-array.
Handling Webhook Requests
To handle webhook requests, follow these steps:
1. Verify the Signature
Webhook requests include an X-HMAC-SHA256-Signature
-header. This signature is calculated from the request body and your notification secret. To ensure the authenticity of the request:
Compute the HMAC SHA-256 signature of the request body using your notification secret. See example code below.
Compare your computed signature with the value in the
X-HMAC-SHA256-Signature
header. If they match, the request is authentic.
Example Request Headers:
POST /webhook-endpoint HTTP/1.1
Host: your-webhook-url.com
Content-Type: application/json
User-Agent: SuuntoPartnerIntegration/1.0.0
X-HMAC-SHA256-Signature: 0eb0ee2b4abeb58c34eafad037258cdd8e315eccb1b6509a5e5730f8aa1fa53f
2. Identify the User
Each request includes a
username
field containing the Suunto user ID. Use this ID to locate the corresponding user in your system.The Suunto user ID can be obtained during the OAuth authorization flow when exchanging the authorization code for an access token. The user ID is available as:
The
user
field in the OAuth access token response.A custom claim
user
in the JWT access token.
Refer to How to Start -page for instructions on exchanging the authorization code for an access token.
3. Respond to Webhooks
Respond with an HTTP
2XX
status code (e.g.,200 OK
).Any response content is acceptable.
Responses must be sent within 2 seconds.
Retry Behavior
If your system does not respond with a successful HTTP status (e.g., 200 OK) or times out, the webhook request will be retried using an exponential backoff delay. However, excessive failures within a defined time frame will trigger a circuit breaker mechanism, which pauses all notification sending temporarily for your app.
Example Code for Signature Calculation
The following example shows how to compute the HMAC SHA-256 signature in Kotlin:
package com.amersports.integration.partners.utils
import org.apache.commons.codec.binary.Hex
import java.io.InputStream
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
object NotificationSignature {
private const val BUFFER_SIZE = 8192 // 8 KB buffer
fun create(secret: String, inputStream: InputStream): String {
val signingKey = SecretKeySpec(secret.toByteArray(), "HmacSHA256")
val mac = Mac.getInstance("HmacSHA256")
mac.init(signingKey)
val buffer = ByteArray(BUFFER_SIZE)
while (true) {
val bytesRead = inputStream.read(buffer)
if (bytesRead == -1) break
mac.update(buffer, 0, bytesRead)
}
val signatureBytes = mac.doFinal()
return Hex.encodeHexString(signatureBytes)
}
}