Skip to content

Tests

A Test is a structured performance evaluation: a lactate test, a VO2max test, an FTP test, or any similar protocol that produces a defined set of physiological markers. Tests live alongside activities and traces, and can be referenced from either.

Where a trace is a single point measurement and an activity is a continuous recording, a test is the structured output of a deliberate evaluation protocol. The shape is rich enough to capture both interoperable, consumer-facing thresholds (first/second threshold, FatMax) and methodology-specific outputs (LT1/LT2, VT1/VT2, MLSS).

Test results

The results field on a Test is a structured JSON object. Every field is optional. Tests can carry any combination.

Thresholds (consumer-facing)

These are interoperable across testing methodologies. Most apps building on top of SweatStack should read from these.

Field Type Description
first_threshold Marker The aerobic threshold (LT1, VT1, depending on protocol).
second_threshold Marker The anaerobic threshold (LT2, VT2, MLSS, depending on protocol).
fatmax Marker The point of maximal fat oxidation.

Thresholds (methodology-specific)

For testing protocols where the underlying methodology matters.

Field Type Description
lt1 Marker First lactate threshold.
lt2 Marker Second lactate threshold.
vt1 Marker First ventilatory threshold.
vt2 Marker Second ventilatory threshold.
mlss Marker Maximal lactate steady state.

Capacity and ceiling metrics

Field Type Unit Description
vo2max float mL/min Confirmed VO2 plateau.
vo2peak float mL/min Highest observed VO2.
vlamax float mmol/L/s Maximal lactate accumulation rate.
heart_rate_max int bpm Observed maximal heart rate.
critical_power int watts Critical power (cycling).
critical_speed float m/s Critical speed (running).
w_prime float kJ W′ (anaerobic work capacity).
d_prime float meters D′ (anaerobic distance capacity).

Economy and efficiency

Field Type Unit Description
economy float mL O₂/kg/km Running or cycling economy.
efficiency float % Gross mechanical efficiency.

Marker shape

A Marker is a point on the intensity-duration curve, expressed in any of the metrics SweatStack supports.

{
    "power": 285,
    "heart_rate": 168,
    "speed": 4.2,
    "lactate": 4.0,
    "vo2": 3200
}

All fields are optional. Populate the ones that the testing protocol actually measured.

Create a test

POST /api/v1/tests/ creates a test.

curl -X POST "https://app.sweatstack.no/api/v1/tests/" \
    -H "Authorization: Bearer {your_access_token}" \
    -H "Content-Type: application/json" \
    -d '{
        "title": "Lactate step test",
        "sport": "cycling.road",
        "start": "2026-05-06T10:00:00+02:00",
        "end": "2026-05-06T11:30:00+02:00",
        "results": {
            "first_threshold": {"power": 220, "heart_rate": 150, "lactate": 2.0},
            "second_threshold": {"power": 285, "heart_rate": 168, "lactate": 4.0},
            "vo2max": 4100
        },
        "tags": ["lab", "step-test"]
    }'

Required fields: sport, start. Everything else is optional. Returns the created test.

Required scope

Creating tests requires data:write.

List tests

GET /api/v1/tests/ returns tests filtered by date range, sport, tags, and creator.

curl -X GET "https://app.sweatstack.no/api/v1/tests/?start=2026-01-01&sport=cycling.road" \
    -H "Authorization: Bearer {your_access_token}"

Available query parameters:

  • start: start date (YYYY-MM-DD).
  • end: end date (YYYY-MM-DD).
  • sport: sport identifier, repeatable for multiple values (e.g. ?sport=cycling.road&sport=running.road).
  • tags: filter by tag(s).
  • created_by: filter by the user or app that created the test.
  • limit: number of results (default 50).
  • offset: pagination offset.

Get a single test

GET /api/v1/tests/{test_id} returns the test plus any traces and activities linked by timestamp.

curl -X GET "https://app.sweatstack.no/api/v1/tests/{test_id}" \
    -H "Authorization: Bearer {your_access_token}"

Update a test

PUT /api/v1/tests/{test_id} replaces the test (full replace, not patch). Omitted optional fields are set to null.

curl -X PUT "https://app.sweatstack.no/api/v1/tests/{test_id}" \
    -H "Authorization: Bearer {your_access_token}" \
    -H "Content-Type: application/json" \
    -d '{
        "title": "Lactate step test (revised)",
        "sport": "cycling.road",
        "start": "2026-05-06T10:00:00+02:00",
        "results": {
            "second_threshold": {"power": 290, "heart_rate": 170, "lactate": 4.0}
        }
    }'

Returns 403 if the test was created by a different app and your token doesn't have access. Returns 404 if the test doesn't exist.

Delete a test

DELETE /api/v1/tests/{test_id} removes the test.

curl -X DELETE "https://app.sweatstack.no/api/v1/tests/{test_id}" \
    -H "Authorization: Bearer {your_access_token}"

Same 403 / 404 semantics as update.

App metadata

Apps can attach per-app JSON metadata to tests via PUT /api/v1/tests/{test_id}/app-metadata and DELETE /api/v1/tests/{test_id}/app-metadata. See Application metadata for the full pattern (it's the same shape across activities, traces, and tests).

Python client

Tests aren't yet exposed in the Python client. Use the API directly via Client.session or any HTTP library until the methods land.