Reference

Not a number: a measurement with its context

Every Oxynet result says what was measured, how good the recording was, what the reader must not drop, and exactly what produced it. That is what makes it safe to store, to report, and to hand to software or an agent that was not there when it was computed.

API v1 · docs 1.0 · updated 2026-09-22

The envelope

All three analyses return the same outer shape, so a consumer writes one parser.

analysis"vt", "eov" or "substrate".
status"ok", or "not_analysable" when the recording cannot support it.
findingsThe measurements. Shape depends on the analysis; null when not analysable.
quality"good", "acceptable" or "poor". Describes the INPUT recording (coverage of VE, VO2, VCO2 and whether the sampling interval is known). It is not a confidence in the result, and no field is.
notesPlain-language caveats that must travel with the numbers into any report built on them.
error, message, contextPresent when status is "not_analysable": the code, the reason, and what was needed.
provenanceanalysis_version, schema_version, computed_at, the model that ran and latency_ms; for thresholds also model_version and model_tier.

Why each part is there

provenance

A result can always be traced to the model, version and analysis version that produced it, and when. Inference is deterministic: same recording, same model version, same numbers.

quality

Describes the INPUT: coverage of the core gas channels and whether the sampling interval is known. It is not a confidence, and there is no field that is.

notes and flags

The caveats that must travel with the numbers: a channel the model ran without, windows where a fat rate is only an upper bound, a protocol that never reached steady state.

Refusal

When the recording cannot support an analysis the envelope says so, with a code and the reason, and the other analyses still run. Nothing is computed on inadequate input and returned as if it were fine.

Recorded responses

Where these come from

Real responses from the live API, recorded on 2026-09-04 (analysis version 2026.08) against the de-identified reference recording used in the manual. They were stored for the manual, which kept only the fields it prints, and some nesting was flattened; the envelope above is the exact live shape. Nothing below was typed by hand.

Upload summary (excerpt)

{
  "format": "exercise_threshold_app",
  "format_confidence": "detected",
  "bytes": 57514,
  "sampling": {
    "method": "breath_by_breath",
    "raw_interval_s": 2,
    "resampled_hz": 1,
    "n_raw": 353,
    "n_resampled": 929,
    "duration_s": 969
  },
  "channels_present": [
    "VO2",
    "VCO2",
    "VE",
    "HR",
    "RF",
    "PetO2",
    "PetCO2"
  ],
  "load": {
    "available": false,
    "reason": "the exercise_threshold_app export carries no work-rate or speed column, so this test has no external load. Nothing is inferred from VO2: back-calculating watts from oxygen uptake assumes the efficiency that a load channel would be used to measure"
  }
}

Note load.available: false with its reason: the export had no work-rate column, and nothing is back-calculated to invent one.

Thresholds (vt)

{
  "status": "ok",
  "findings": {
    "vt1_time_s": 619,
    "vt2_time_s": 763,
    "vt1_vo2_ml_min": 1605.2,
    "vt2_vo2_ml_min": 2028.9
  },
  "quality": "good",
  "provenance": {
    "model": "gandalf",
    "model_version": "v0.1.0",
    "model_tier": "free",
    "analysis_version": "2026.08",
    "latency_ms": 97.41
  }
}

Oscillatory ventilation (eov), research stage

Beta: developed on a single heart-failure cohort, transportability untested. The finding here is an oscillation too fast for the EOV definitions, flagged and not graded, with the gas analysis confirmed sound. Fields shown as stored for the manual.

{
  "status": "ok",
  "grade": "none",
  "headline": "OSCILLATION PRESENT: TOO FAST FOR EOV, NOT GRADED",
  "summary": "Breathing cycled every 21 seconds for 0.7 minutes, swinging 17% of ventilation, 7× above background, with CO₂ moving opposite. Faster than the 40–140 s the EOV definitions cover, so not graded as EOV, but not a normal pattern either.",
  "event": {
    "t_start": 588,
    "t_end": 632,
    "duration_s": 44,
    "period_peak_s": 20.87,
    "period_lo_s": 20,
    "period_hi_s": 25.86,
    "clarity": 6.65,
    "n_cycles": 2.11,
    "amplitude_l_min": 8.58,
    "amplitude_pct_ve": 16.78,
    "phase_deg": -164.37,
    "phase_error_deg": 15.63,
    "corroborated": true,
    "envelope": "stable",
    "drive": "respiratory rate",
    "drive_rf_fraction": 0.755,
    "drive_vt_fraction": 0.245,
    "co2_coupling": "tight antiphase",
    "co2_amplitude_mmhg": 1.97,
    "co2_amplitude_pct": 5.77,
    "graded": false
  },
  "burden": 0,
  "period_floor_s": 25,
  "sampling_interval_s": 2,
  "gas_quality": "confirmed",
  "gas_quality_note": "Gas analysis confirmed: CO₂ output followed ventilation to within 14°, and end-tidal CO₂ and O₂ held opposite to within 2°, and the end-tidal CO₂ waveform kept its shape (distortion 0.71, against a limit of 3.0), all as the physiology requires, so the traces behind this assessment are sound.",
  "notes": [
    "No external load was recorded, so the protocol is unknown and nothing here is read against a work rate."
  ],
  "provenance": {
    "model": "oxynet-eob",
    "analysis_version": "2026.08",
    "latency_ms": 153.57
  }
}

Substrate use (substrate)

windows cut to the first 2 of 12. The flags are the part to read: RER above 1.0 in half the windows, so the fat rate there is a bound, not a measurement. The equations are returned in method.

{
  "status": "ok",
  "vo2peak": 2307,
  "window_source": "clock bins",
  "window_s": 60,
  "n_windows": 12,
  "windows": [
    {
      "t_start": 235,
      "vo2": 1.0031,
      "vco2": 0.8612,
      "rer": 0.859,
      "pct_vo2peak": 43.5,
      "fat_g_min": 0.2353,
      "cho_g_min": 0.7127,
      "pct_ee_fat": 43.5,
      "fat_valid": true
    },
    {
      "t_start": 295,
      "vo2": 0.9542,
      "vco2": 0.8442,
      "rer": 0.885,
      "pct_vo2peak": 41.4,
      "fat_g_min": 0.1814,
      "cho_g_min": 0.7924,
      "pct_ee_fat": 34.8,
      "fat_valid": true
    }
  ],
  "fatmax": {
    "found": true,
    "axis": "pct_vo2peak",
    "pct_vo2peak": 42.3,
    "fat_g_min": 0.182,
    "cho_g_min": 0.8196,
    "pct_ee_fat": 34.1,
    "load": null,
    "fit_r2_fat": 0.9925,
    "fit_r2_cho": 0.9972
  },
  "crossover": {
    "found": false,
    "reason": "Carbohydrate was already the larger fuel at the lowest intensity measured, so the crossover happened below anything this test covered."
  },
  "efficiency": {
    "available": false,
    "reason": "Gross efficiency is mechanical work over metabolic energy, so it needs a work rate in watts. This test recorded no external load."
  },
  "flags": [
    {
      "code": "trimmed_to_exercise",
      "severity": "info",
      "detail": "Rest and recovery are excluded: 37 resting samples were dropped before the windows were taken. Warm-up is kept: it is a real intensity and often the one FATMAX lands on."
    },
    {
      "code": "no_load",
      "severity": "info",
      "detail": "This recording carries no external load, so substrate use is placed against %VO2peak only. FATMAX is reported as an intensity and cannot be given as a work rate, and gross efficiency needs mechanical power, so it is not computed."
    },
    {
      "code": "rer_above_one",
      "severity": "warning",
      "detail": "6 of 12 windows sit above RER 1.0, where exhaled CO2 includes a non-metabolic part from buffering the metabolic acidosis. The stoichiometry does not hold there, so the fat rate at those intensities is an upper bound that reads low and can read negative, not a measurement."
    }
  ],
  "method": [
    "Fat (g/min) = 1.695 × VO2 − 1.701 × VCO2, carbohydrate (g/min) = 4.585 × VCO2 − 3.226 × VO2, both with the gases in L/min (Frayn 1983; Jeukendrup and Wallis 2001), assuming protein oxidation is negligible.",
    "Energy from each: 9.75 kcal per gram of fat and 4.18 kcal per gram of carbohydrate.",
    "FATMAX is the peak of a cubic through the origin fitted to the fat rate against the axis shown (Achten and Jeukendrup). It is a fitted turning point, not a measured sample, and it carries no confidence."
  ],
  "provenance": {
    "model": "oxynet-substrate",
    "analysis_version": "2026.08",
    "latency_ms": 59.03
  }
}

Derived quantities (compute)

Values only; the live response wraps each as {status: "ok", value}. The V̇E/V̇CO₂ slope is a profile over the test, not one number, and the landmarks carry their own reading note: they corroborate, they never override a model.

{
  "vo2max": 2307,
  "vemax": 111.6,
  "rermax": 1.274,
  "o2_pulse": 16.03,
  "gas_quality": {
    "verdict": "confirmed",
    "n_checks": 2,
    "n_passed": 2,
    "vco2_coherence": 0.9829,
    "vco2_phase_error_deg": 6.5657,
    "closure_coherence": 0.8515,
    "closure_phase_error_deg": 16.0412,
    "note": "Gas channels agree with each other as the physiology requires."
  },
  "sampling_adequacy": {
    "raw_interval_s": 2,
    "method": "breath_by_breath",
    "period_floor_s": 25,
    "period_ceiling_s": 140,
    "band_narrowed": false,
    "gradable": true,
    "note": "Sampling resolves the full band."
  },
  "clock_integrity": {
    "raw_span_s": 969,
    "repaired_span_s": 969,
    "samples_dropped": 0,
    "repair_applied": false,
    "note": "Clock is monotonic; no repair needed."
  },
  "ve_vco2_slope": [
    {
      "label": "25%",
      "slope": 33.1,
      "ci_low": 24.4,
      "ci_high": 43.7,
      "n": 38
    },
    {
      "label": "50%",
      "slope": 32.6,
      "ci_low": 31.1,
      "ci_high": 34.3,
      "n": 96
    },
    {
      "label": "75%",
      "slope": 34.3,
      "ci_low": 33.5,
      "ci_high": 35,
      "n": 183
    },
    {
      "label": "100%",
      "slope": 37.9,
      "ci_low": 37.2,
      "ci_high": 38.3,
      "n": 323
    }
  ],
  "landmarks": {
    "vo2peak": 2306.5,
    "vo2peak_window_s": 30,
    "points": {
      "vo2peak_30s": {
        "t_s": 924,
        "vo2": 2307.7,
        "pct_vo2peak": 100.1
      },
      "v_slope_breakpoint": {
        "t_s": 787,
        "vo2": 2163.1,
        "pct_vo2peak": 93.8,
        "slope_before": 1.2997,
        "slope_after": 1.4175
      },
      "rcp_breakpoint": {
        "t_s": 848,
        "vo2": 2189,
        "pct_vo2peak": 94.9,
        "slope_before": 35.9035,
        "slope_after": 52.9992
      },
      "rer_crossing_1.0": {
        "t_s": 670,
        "vo2": 1796.2,
        "pct_vo2peak": 77.9
      },
      "ve_vco2_nadir": {
        "t_s": 508,
        "vo2": 1197.5,
        "pct_vo2peak": 51.9
      },
      "petco2_peak": {
        "t_s": 508,
        "vo2": 1197.5,
        "pct_vo2peak": 51.9
      }
    },
    "reading": "Corroboration only. These are classical criteria computed on the file's own signal; they are not calibrated against outcomes, they carry no confidence, and they never override a model's thresholds. A V-slope with S1 below 1 and S2 above it is the expected shape; slopes that barely differ mean no breakpoint was found."
  }
}

A refusal

The shape of an analysis that could not run, inside a 200 that carries the others:

shape, not a recording
{
  "analysis": "eov",
  "status": "not_analysable",
  "findings": null,
  "error": "MISSING_CHANNEL",
  "message": "<why, in words>",
  "context": { "<what was needed and what was present>": "..." },
  "provenance": { "analysis_version": "...", "schema_version": "...", "computed_at": "..." }
}

Not yet documented

A complete, unabridged response on the downloadable synthetic sample, recorded with a key, so the example and the sample file match one to one.