Skip to content

Workouts

Beta Functionality

The workouts API is in beta and breaking changes can land at short notice. If you plan to use this API in production, get in direct contact with us so we can warn you before a change ships and help you migrate. Email [email protected] before integrating, not after.

SweatStack workouts are planned training sessions: a structured prescription tied to a future start date. They're distinct from activities, which are recorded sessions. A workout is what the athlete plans to do. An activity is what they actually did.

When you create a scheduled workout, SweatStack stores it and pushes it to the athlete's connected platforms (for example Garmin Connect) so users can execute them directly from their wearables.

What you can do today

  • Create a workout scheduled for a specific date and time.
  • List the scheduled workouts for an athlete.
  • Read a single scheduled workout by id.

What will be added later

  • Updating and deleting workouts, also after they are synced to integrations
  • Workout library workouts (no start date, used as templates for later use)

Structured Workout Format (SWF)

The workout content uses the Structured Workout Format, an open JSON schema for prescribing structured workouts. At the moment, SweatStack accepts a subset of the format:

Aspect Currently supported
Sports running (and its sub-sports, e.g. running.road, running.track)
Intensity quantity speed (m/s)
Volume quantity duration (seconds) and distance (meters)
Shapes constant, range, ramp, zone
References absolute values and ParameterRef (named placeholders resolved at fanout time)

Anything outside this set is rejected at creation with a structured 422 error. The list grows as SweatStack expands what it can fan out (cycling with power, etc.). Get in touch if you have a usecase that is not yet supported.

Create a scheduled workout

POST /api/v1/workouts/schedule accepts a body with three top-level fields: swf, start, and an optional parameters map.

curl -X POST "https://app.sweatstack.no/api/v1/workouts/schedule" \
    -H "Authorization: Bearer {your_access_token}" \
    -H "Content-Type: application/json" \
    -d '{
        "swf": {
            "version": "0.3.0",
            "title": "5K with 4x1K Intervals",
            "sport": "running",
            "content": [
                {
                    "type": "step",
                    "effort": "work",
                    "volume": {"type": "constant", "quantity": "distance", "value": 5000},
                    "intensity": {
                        "type": "range",
                        "quantity": "speed",
                        "min": {"percent": 70, "of": "threshold_pace"},
                        "max": {"percent": 80, "of": "threshold_pace"}
                    }
                },
                {
                    "type": "repeat",
                    "count": 4,
                    "content": [
                        {
                            "type": "step",
                            "effort": "work",
                            "volume": {"type": "constant", "quantity": "distance", "value": 1000},
                            "intensity": {
                                "type": "constant",
                                "quantity": "speed",
                                "value": {"percent": 110, "of": "threshold_pace"}
                            }
                        },
                        {
                            "type": "step",
                            "effort": "rest",
                            "volume": {"type": "constant", "quantity": "duration", "value": 90}
                        }
                    ]
                }
            ]
        },
        "start": "2026-05-25T07:00:00+02:00",
        "parameters": {"threshold_pace": 4.45}
    }'
  • start is a TZ-aware ISO 8601 datetime.
  • sport is read from the SWF.
  • parameters resolves ParameterRefs in the SWF. Every parameter referenced by the workout must be present here, or the request is rejected. For workouts with only absolute values, you can omit parameters.

Validation errors

Validation runs against a single SweatStack-level strategy at creation time. Failures return 422 Unprocessable Entity with a structured detail array. Each entry has a code and a path into the workout tree where the problem lives:

{
    "detail": [
        {"code": "unsupported_sport", "message": "Sport 'cycling' is not supported", "path": []}
    ]
}

Common codes:

Code Meaning
unsupported_sport Sport isn't accepted yet (only running and sub-sports in v1).
unsupported_intensity Intensity quantity outside the supported set (only speed in v1).
unsupported_volume Volume quantity outside the supported set.
unsupported_shape Shape (range/ramp/zone) not supported.
missing_parameters The workout references ParameterRefs the request didn't provide. detail[0].missing lists them.

For more details and development tools for creating and validating SWF workouts, see https://structuredworkoutformat.dev.

Success response

{
    "id": "01JW3K...",
    "sport": "running",
    "start": "2026-05-25T07:00:00+02:00",
    "swf": { "..." },
    "parameters": {"threshold_pace": 4.45},
    "application_id": "01JW3J...",
    "application_name": "My App"
}
  • application_id / application_name identify the third-party app that created this workout.

Fanout: how the workout reaches the athlete

After SweatStack persists the workout, an asynchronous task pushes it to the athlete's connected integrations. The push runs in the background and typically completes within seconds.

Fanout status is not returned on the API response. Persistent issues (like missing permission to push workouts to Garmin) surface to the athlete directly through a warning in their SweatStack profile, which can also be retrieved through the profile-status API endpoint.

See also