---
title: "Today's and tomorrow's prescription"
method: GET
path: "/training/recommendation"
tags: ["Training"]
---

# Today's and tomorrow's prescription

`GET /training/recommendation`

## Request

- Base URL: `https://bodylab.lol/api/v1`
- URL: `https://bodylab.lol/api/v1/training/recommendation`
- Auth: HTTP bearer

## Query parameters

- `days` integer: How many days back from today.
- `week` boolean: Also project the next seven days. Off by default: it's five more days through the engine, for surfaces that draw the week.

## Headers

- `X-Client-Platform` string
- `X-Client-Version` string

## Response `200`

OK

- object
  - `units` 'metric' | 'imperial', required: How to display quantities; the API always speaks SI.
  - `metrics` object, required
    - `fitness` object, required
      - `ctl` number, required: Chronic training load (fitness): 42-day exponentially weighted load.
      - `atl` number, required: Acute training load (fatigue): 7-day exponentially weighted load.
      - `form` number, required: ctl − atl.
      - `acwr` number, nullable, required: Acute:chronic ratio; null until there's enough chronic load to divide by.
      - `acwrChronic` number, required: The chronic load acwr divides by — a 28-day EWMA, not ctl's 42-day one.
      - `rampRate` number, nullable, required: Weekly change in ctl.
    - `strain` object, required
      - `monotony` number, nullable, required
      - `strain` number, nullable, required
      - `weeklyLoad` number, required
      - `monotonyHigh` boolean, required
      - `strainHigh` boolean, required: The fixed rule: monotony at or above 2.0 on a week at or above the load the athlete is conditioned for.
      - `personalHigh` number, nullable, required: The athlete's own threshold: their highest 7-day strain in the 12 weeks before this week. Null until 8 weeks of that history have a strain.
      - `aboveOwnHigh` boolean, required: strainHigh, and this week's strain above personalHigh too: what holds today's session easy. False while personalHigh is null.
    - `polarization` object, required
      - `easyFraction` number, required
      - `moderateFraction` number, required
      - `hardFraction` number, required
      - `totalSeconds` number, required
      - `sufficient` boolean, required: Enough heart-rate data in the window to judge.
    - `debt` object, required
      - `daysSinceStrength` number, nullable, required
      - `strengthSessionsLast7` integer, required
      - `strengthSessionsLast14` integer, required
      - `meetsStrengthFloor` boolean, required
      - `daysSinceImpact` number, nullable, required
      - `daysSinceHardSession` number, nullable, required
      - `supportedLoadFraction` number, required
      - `windowDays` integer, required
    - `readiness` object, required
      - `band` 'green' | 'amber' | 'red' | 'unknown', required
      - `score` number, nullable, required
      - `contributors` object[], required: Every signal readiness knows about, heaviest first, whether or not it counted today — see ReadinessContributor.
        - `key` string, required: Which wellness signal this is. Known values: hrv, restingHr, sleep, energy. Treat unknown values as "other".
        - `label` string, required: What to call the signal to an athlete, lower case for mid-sentence use: "resting heart rate".
        - `state` string, required: Whether this signal counted towards today's score. `baselining` means readings are arriving but there isn't enough history to judge them against yet — it must never be shown as a zero, which on this scale means "exactly normal for you". `missing` means nothing recent enough was recorded. Known values: scored, baselining, missing. Treat unknown values as "other".
        - `score` number, nullable, required: This signal's own score, −1 to +1, positive meaning recovered. Null unless state is `scored`.
        - `weight` number, required: Share of today's composite this signal carries, 0 to 1 — not a fixed weight. Readiness reweights to the signals it has, so one signal alone carries all of it. 0 unless state is `scored`.
        - `z` number, nullable, required: Signed deviation from this signal's own rolling baseline in standard deviations, in the direction of the reading: resting heart rate above baseline is positive, though that's the unfavourable direction — `score` is the field that always points the same way. Null when the signal isn't judged against a baseline at all, as a device's already-calibrated sleep or energy score isn't.
      - `hrvZ` number, nullable, required
      - `restingHrDelta` number, nullable, required
    - `block` object, required
      - `consecutiveTrainingDays` integer, required
      - `consecutiveLoadingWeeks` integer, required
      - `week` object, required
        - `startDate` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
        - `loadToDate` number, required
        - `chronicAtStart` number, nullable, required
        - `daysTrained` integer, required
        - `daysRemaining` integer, required
        - `typicalSessionsPerWeek` number, required
    - `seriesDays` integer, required
  - `excludedActivities` integer, required: Activities in the window with no evidence to score them; non-zero means the metrics understate training.
  - `events` object[], required: Upcoming events, nearest first — so Today can show a countdown without a second round trip.
    - `id` string, required: Opaque identifier.
    - `name` string, required
    - `sport` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
    - `priority` 'A' | 'B' | 'C', required
    - `eventDate` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
    - `daysUntil` integer, required
  - `nearestEvent` object, nullable, required: The nearest A event and its phase, for Today's countdown chip — or, on the day of an event of any priority, that event, in the event_day phase. The same summary training-status uses.
    - `name` string, required
    - `daysUntil` integer, required: Negative once the event has passed — only true while still inside its recovery window.
    - `phase` 'build' | 'taper' | 'event_day' | 'recovery', required
    - `totalDays` integer, nullable, required: Length of the taper or recovery window in days; null in the build and event_day phases.
  - `preparation` object, nullable, required: The emphasis of the current block of training before the athlete's nearest A event. Recomputed every morning from the event's date and expected duration; readiness still decides each day, and a phase never overrides a rest day, a taper or a recovery window.
    - `eventId` string, required: Opaque identifier.
    - `eventName` string, required
    - `phase` string, required: Which part of the run-up to an A event a day sits in. An emphasis worked out each morning from the event's date, not a stored plan. Known values: base, build, specific, taper. Treat unknown values as "other".
    - `week` integer, required: Which week of this phase today is in, from 1.
    - `weeks` integer, required: How many weeks the phase runs for.
    - `startsOn` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
    - `endsOn` string, date, required: Inclusive: the last day of this phase.
    - `nextPhase` string, nullable, required: Which part of the run-up to an A event a day sits in. An emphasis worked out each morning from the event's date, not a stored plan. Known values: base, build, specific, taper. Treat unknown values as "other".
    - `nextStartsOn` string, date, nullable, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
  - `recentDays` object[], required: The six days before today, oldest first. With today — which `completed` and `todayIsDone` describe — this is the week behind the athlete. Mostly for drawing: `list_activities` and the weekly review answer "what did I do on Tuesday" in more detail.
    - `date` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
    - `trained` boolean, required: Whether anything was recorded or logged that day. From the activities and strength sessions themselves, not from training load, so a session that scored nothing still counts.
    - `committed` object[], required: Sessions the athlete committed to that day, and how each went. Empty on a day with no committed session, which is most days.
      - `workoutName` string, required
      - `status` string, required: as_prescribed: matched, with the work there. differently: matched but short of the work, or done as another sport. skipped: nothing in its place that day, on a day Body Lab could have seen. unmeasured: done in the same sport but not matched to the plan (usually no heart-rate or power recording), so not measured. no_data: nothing was matched to it and no data for that day could have reached Body Lab — no connected service or device was delivering and nothing was entered by hand — so there is no evidence either way. not_recorded: a session the athlete told Body Lab was happening (a commitment) that nothing was matched to; Body Lab never prescribed it, so it is reported without a verdict. Never read no_data or not_recorded as a session missed. Known values: as_prescribed, differently, skipped, unmeasured, no_data, not_recorded. Treat unknown values as "other".
  - `today` object, required
    - `additional` boolean, required: Today's main session is done; this is an optional extra.
    - `deload` boolean, required
    - `sessionClass` 'rest' | 'recovery' | 'easy' | 'build' | 'quality' | 'strength' | 'complete', required
    - `primary` union, required: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
      - object
        - `kind` 'endurance', required
        - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
        - `durationMin` number, required
        - `intendedDurationMin` number, required
        - `zone` integer, required
        - `hrLow` number, nullable, required
        - `hrHigh` number, nullable, required
        - `powerLow` number, nullable, required
        - `powerHigh` number, nullable, required
        - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
        - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
        - `blocks` object[], required
          - `label` string, required
          - `repeat` integer, required
          - `work` object, required
            - `minutes` number, required
            - `zone` integer, required: Heart-rate zone, 1–5.
            - `hrLow` number, nullable, required
            - `hrHigh` number, nullable, required
            - `powerLow` number, nullable, required
            - `powerHigh` number, nullable, required
            - `speedLowMs` number, nullable: The slow end of this block's pace, as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
            - `speedHighMs` number, nullable: The fast end of this block's pace, metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `recovery` object, nullable, required
            - `minutes` number, required
            - `zone` integer, required: Heart-rate zone, 1–5.
            - `hrLow` number, nullable, required
            - `hrHigh` number, nullable, required
            - `powerLow` number, nullable, required
            - `powerHigh` number, nullable, required
            - `speedLowMs` number, nullable: The slow end of this block's pace, as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
            - `speedHighMs` number, nullable: The fast end of this block's pace, metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
        - `workoutType` object, nullable, required
          - `id` string, required
          - `name` string, required
          - `stimulus` string, required
        - `note` string, required
        - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
      - object
        - `kind` 'strength', required
        - `template` string, required
        - `durationMin` number, required
        - `exercises` object[], required
          - `exerciseId` string, required: Opaque identifier.
          - `name` string, required
          - `sets` integer, required
          - `reps` integer, required: Reps, or seconds when unit is seconds.
          - `unit` 'reps' | 'seconds', required
          - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
          - `rir` number, required: Reps in reserve to stop at.
          - `intensity` number, nullable, required
          - `progression` object, nullable, required
            - `axis` 'reps' | 'load', required
            - `direction` 'up' | 'hold', required
            - `previous` number, required
            - `daysAgo` number, required
            - `reason` string, required
          - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
          - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
            - unknown
            - unknown
          - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
          - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
          - `note` string
          - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
          - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
          - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
        - `note` string, required
        - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
          - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
          - `name` string, required: What the routine was called: "Upper day".
          - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
      - object
        - `kind` 'rest', required
        - `note` string, required
        - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `alternates` union[], required
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `variants` union[], required
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `otherSports` union[]: Sports past the three on the card that could still carry today's session.
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `unavailable` object[], required
      - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
      - `reason` string, required
    - `flags` string[], required
    - `flagItems` object[]
      - `key` string, required: Stable for the condition, e.g. `strength.floor`, `load.ramp`, `plan.recovery_week_next`.
      - `urgency` 'timely' | 'ahead' | 'standing', required: timely: changes today. ahead: about a coming day or next week. standing: a slow structural note.
      - `text` string, required
    - `rationale` object[], required: Why, citing the metric behind each call — the deciding reason first.
      - `role` string, required: What this reason is doing. Known values: decision, phase, context. Treat unknown values as "other".
      - `headline` string, required: The claim: metric, value, threshold. One line.
      - `detail` string, nullable, required: The mechanism, and any citation. Null when the headline is all of it.
      - `plain` string, nullable, required: The reason in plain words for a one-line surface; null when there isn't one.
      - `day` object: Present on the few headlines that name a day, and absent — not null — on every other, which is most of them. A client that ignores it renders `headline` and is right.
        - `date` string, date, required: The day the headline names, on the athlete's own calendar.
        - `before` string, required: The headline up to where the day goes. Never ends in a space: the phrase carries its own.
        - `after` string, required: And from there on. On the day being prescribed — which the card is already headed with — the engine's own phrase is empty and `before` + `after` is the whole headline, so a surface with a word for that day fills the same hole as any other.
    - `environment` object, nullable, required: Altitude and recent heat, where either applies today; null otherwise.
      - `altitude` object, nullable, required: Set when watt targets were lowered for altitude. Heart-rate targets never are.
        - `todayAltitudeM` number, required: The altitude today's session is expected at, metres.
        - `basis` string, required: Where that came from, most trustworthy first: confirmed means the athlete confirmed it for today; recent_recordings means the last two days' recordings both started there; home is the profile's home altitude. Known values: confirmed, recent_recordings, home. Treat unknown values as "other".
        - `ftpAltitudeM` number, required: The altitude the FTP behind the watt targets was set at, metres: from rides around its date, or home.
        - `powerFactor` number, required: What every watt target was multiplied by: below 1, never above.
      - `heat` object, nullable, required: Set when a recent session was recorded in hot air. Nothing is known about today's weather; sessions and targets are unchanged.
        - `temperatureC` number, required: Air temperature of the hottest session in the last few days, °C. Never a wrist reading.
        - `recordedOn` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
        - `daysAgo` integer, required
        - `hotDaysLast14` integer, required: Days in the last two weeks with a hot session of an hour or more.
        - `acclimatised` boolean, required: Whether that is enough hot days for heat adaptation to be under way.
      - `pace` object, nullable: Set when today's pace targets differ for altitude from what the runs behind them showed, by enough to say. The size is an estimate, not a measurement of the athlete. Heart-rate targets are unchanged.
        - `todayAltitudeM` number, required: The altitude today's session is expected at, metres.
        - `basis` string, required: Where that came from, as in `altitude.basis`. Known values: confirmed, recent_recordings, home. Treat unknown values as "other".
        - `effortsAltitudeM` number, required: The median altitude of the runs the critical speed behind today's paces was worked out from, metres.
        - `speedFactor` number, required: Today's paces as a fraction of the paces at the runs' altitude, as speed: below 1 is slower (higher today), above 1 faster.
    - `equipmentNote` object, nullable: What to say beside the targets about the kit this was written for — a place that isn't home, or a pattern the kit can't fill. Null when there's nothing to say.
      - `placeName` string, nullable, required: The equipment place this session was written for, when it isn't the athlete's home one; null when it is.
      - `justForToday` boolean: Whether that kit is the one-off the athlete set for today, which expires on its own tonight. It has no name, so placeName is null and this says which kind of null it is — a client still draws the line, because the line is how the athlete changes it.
      - `gaps` object[], required: Patterns the athlete's kit could not fill, in reading order. Empty when the session is whole, in which case placeName is what the note is for.
        - `pattern` string, required: A movement pattern the session has none of, in the athlete's words: "vertical pull".
        - `wouldAdd` string[], required: Items that would each restore the pattern, best first. Never empty.
    - `alsoCommitted` union[]: Other sessions the athlete (or an app they connected) put on this date, beside `primary`: a strength routine after a club ride, a second ride, a morning and an evening routine. Each is done as well as `primary`, not instead of it, and none is cut by a taper or a recovery week. Absent when the date holds one committed session or none.
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `primaryRole` string, required: What the primary is to the day: the day's session; a session the athlete arranged that is what's left after the day's training or a report; or an optional extra after the main session is done. Known values: main, handover, extra. Treat unknown values as "other".
  - `tomorrow` object, required
    - `additional` boolean, required: Today's main session is done; this is an optional extra.
    - `deload` boolean, required
    - `sessionClass` 'rest' | 'recovery' | 'easy' | 'build' | 'quality' | 'strength' | 'complete', required
    - `primary` union, required: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
      - object
        - `kind` 'endurance', required
        - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
        - `durationMin` number, required
        - `intendedDurationMin` number, required
        - `zone` integer, required
        - `hrLow` number, nullable, required
        - `hrHigh` number, nullable, required
        - `powerLow` number, nullable, required
        - `powerHigh` number, nullable, required
        - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
        - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
        - `blocks` object[], required
          - `label` string, required
          - `repeat` integer, required
          - `work` object, required
            - `minutes` number, required
            - `zone` integer, required: Heart-rate zone, 1–5.
            - `hrLow` number, nullable, required
            - `hrHigh` number, nullable, required
            - `powerLow` number, nullable, required
            - `powerHigh` number, nullable, required
            - `speedLowMs` number, nullable: The slow end of this block's pace, as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
            - `speedHighMs` number, nullable: The fast end of this block's pace, metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `recovery` object, nullable, required
            - `minutes` number, required
            - `zone` integer, required: Heart-rate zone, 1–5.
            - `hrLow` number, nullable, required
            - `hrHigh` number, nullable, required
            - `powerLow` number, nullable, required
            - `powerHigh` number, nullable, required
            - `speedLowMs` number, nullable: The slow end of this block's pace, as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
            - `speedHighMs` number, nullable: The fast end of this block's pace, metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
        - `workoutType` object, nullable, required
          - `id` string, required
          - `name` string, required
          - `stimulus` string, required
        - `note` string, required
        - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
      - object
        - `kind` 'strength', required
        - `template` string, required
        - `durationMin` number, required
        - `exercises` object[], required
          - `exerciseId` string, required: Opaque identifier.
          - `name` string, required
          - `sets` integer, required
          - `reps` integer, required: Reps, or seconds when unit is seconds.
          - `unit` 'reps' | 'seconds', required
          - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
          - `rir` number, required: Reps in reserve to stop at.
          - `intensity` number, nullable, required
          - `progression` object, nullable, required
            - `axis` 'reps' | 'load', required
            - `direction` 'up' | 'hold', required
            - `previous` number, required
            - `daysAgo` number, required
            - `reason` string, required
          - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
          - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
            - unknown
            - unknown
          - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
          - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
          - `note` string
          - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
          - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
          - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
        - `note` string, required
        - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
          - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
          - `name` string, required: What the routine was called: "Upper day".
          - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
      - object
        - `kind` 'rest', required
        - `note` string, required
        - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `alternates` union[], required
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `variants` union[], required
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `otherSports` union[]: Sports past the three on the card that could still carry today's session.
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
    - `unavailable` object[], required
      - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
      - `reason` string, required
    - `flags` string[], required
    - `flagItems` object[]
      - `key` string, required: Stable for the condition, e.g. `strength.floor`, `load.ramp`, `plan.recovery_week_next`.
      - `urgency` 'timely' | 'ahead' | 'standing', required: timely: changes today. ahead: about a coming day or next week. standing: a slow structural note.
      - `text` string, required
    - `rationale` object[], required: Why, citing the metric behind each call — the deciding reason first.
      - `role` string, required: What this reason is doing. Known values: decision, phase, context. Treat unknown values as "other".
      - `headline` string, required: The claim: metric, value, threshold. One line.
      - `detail` string, nullable, required: The mechanism, and any citation. Null when the headline is all of it.
      - `plain` string, nullable, required: The reason in plain words for a one-line surface; null when there isn't one.
      - `day` object: Present on the few headlines that name a day, and absent — not null — on every other, which is most of them. A client that ignores it renders `headline` and is right.
        - `date` string, date, required: The day the headline names, on the athlete's own calendar.
        - `before` string, required: The headline up to where the day goes. Never ends in a space: the phrase carries its own.
        - `after` string, required: And from there on. On the day being prescribed — which the card is already headed with — the engine's own phrase is empty and `before` + `after` is the whole headline, so a surface with a word for that day fills the same hole as any other.
    - `environment` object, nullable, required: Altitude and recent heat, where either applies today; null otherwise.
      - `altitude` object, nullable, required: Set when watt targets were lowered for altitude. Heart-rate targets never are.
        - `todayAltitudeM` number, required: The altitude today's session is expected at, metres.
        - `basis` string, required: Where that came from, most trustworthy first: confirmed means the athlete confirmed it for today; recent_recordings means the last two days' recordings both started there; home is the profile's home altitude. Known values: confirmed, recent_recordings, home. Treat unknown values as "other".
        - `ftpAltitudeM` number, required: The altitude the FTP behind the watt targets was set at, metres: from rides around its date, or home.
        - `powerFactor` number, required: What every watt target was multiplied by: below 1, never above.
      - `heat` object, nullable, required: Set when a recent session was recorded in hot air. Nothing is known about today's weather; sessions and targets are unchanged.
        - `temperatureC` number, required: Air temperature of the hottest session in the last few days, °C. Never a wrist reading.
        - `recordedOn` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
        - `daysAgo` integer, required
        - `hotDaysLast14` integer, required: Days in the last two weeks with a hot session of an hour or more.
        - `acclimatised` boolean, required: Whether that is enough hot days for heat adaptation to be under way.
      - `pace` object, nullable: Set when today's pace targets differ for altitude from what the runs behind them showed, by enough to say. The size is an estimate, not a measurement of the athlete. Heart-rate targets are unchanged.
        - `todayAltitudeM` number, required: The altitude today's session is expected at, metres.
        - `basis` string, required: Where that came from, as in `altitude.basis`. Known values: confirmed, recent_recordings, home. Treat unknown values as "other".
        - `effortsAltitudeM` number, required: The median altitude of the runs the critical speed behind today's paces was worked out from, metres.
        - `speedFactor` number, required: Today's paces as a fraction of the paces at the runs' altitude, as speed: below 1 is slower (higher today), above 1 faster.
    - `equipmentNote` object, nullable: What to say beside the targets about the kit this was written for — a place that isn't home, or a pattern the kit can't fill. Null when there's nothing to say.
      - `placeName` string, nullable, required: The equipment place this session was written for, when it isn't the athlete's home one; null when it is.
      - `justForToday` boolean: Whether that kit is the one-off the athlete set for today, which expires on its own tonight. It has no name, so placeName is null and this says which kind of null it is — a client still draws the line, because the line is how the athlete changes it.
      - `gaps` object[], required: Patterns the athlete's kit could not fill, in reading order. Empty when the session is whole, in which case placeName is what the note is for.
        - `pattern` string, required: A movement pattern the session has none of, in the athlete's words: "vertical pull".
        - `wouldAdd` string[], required: Items that would each restore the pattern, best first. Never empty.
    - `alsoCommitted` union[]: Other sessions the athlete (or an app they connected) put on this date, beside `primary`: a strength routine after a club ride, a second ride, a morning and an evening routine. Each is done as well as `primary`, not instead of it, and none is cut by a taper or a recovery week. Absent when the date holds one committed session or none.
      - union: One prescribed session. `kind` is the discriminator; new kinds are a breaking change.
        - object
          - `kind` 'endurance', required
          - `modality` string, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
          - `durationMin` number, required
          - `intendedDurationMin` number, required
          - `zone` integer, required
          - `hrLow` number, nullable, required
          - `hrHigh` number, nullable, required
          - `powerLow` number, nullable, required
          - `powerHigh` number, nullable, required
          - `speedLowMs` number, nullable: The slow end of the pace the session is about (its hardest block's, as with hrLow and powerLow), as a speed in metres per second. Null with speedHighMs set means a ceiling only ("no faster than"); null with both null means no pace. Show it as pace in the athlete's units; each edge already sits on a whole five seconds of pace.
          - `speedHighMs` number, nullable: The fast end of the pace the session is about (its hardest block's, as with hrLow and powerLow), metres per second, or null for no pace. Pace comes from the athlete's critical speed and only on road, track and treadmill runs; heart rate stays the limit.
          - `blocks` object[], required
            - `label` string, required
            - `repeat` integer, required
            - `work` object, required
              - …
            - `recovery` object, nullable, required
              - …
          - `workoutType` object, nullable, required
            - `id` string, required
            - `name` string, required
            - `stimulus` string, required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
        - object
          - `kind` 'strength', required
          - `template` string, required
          - `durationMin` number, required
          - `exercises` object[], required
            - `exerciseId` string, required: Opaque identifier.
            - `name` string, required
            - `sets` integer, required
            - `reps` integer, required: Reps, or seconds when unit is seconds.
            - `unit` 'reps' | 'seconds', required
            - `weightKg` number, nullable, required: The total load, in kilograms, including the bar or both dumbbells — never the weight of one implement or one side of a bar. Negative is assistance: the part of the athlete's bodyweight a machine or band is taking off. Null when the movement carries no external load, or when there is no history to prescribe one from.
            - `rir` number, required: Reps in reserve to stop at.
            - `intensity` number, nullable, required
            - `progression` object, nullable, required
              - …
            - `perSide` 'leg' | 'side', nullable, required: Reps are per leg or per side.
            - `implementCount` union, required: 2 when the load is split across two dumbbells or kettlebells; weightKg is still the total, so each hand holds half of it.
              - …
            - `restSeconds` integer, required: Suggested rest between this exercise's sets, in seconds: shorter for jumps and accessories, longer for the heaviest lifts.
            - `restReason` string, required: Why restSeconds is the length it is: a jump, lighter accessory work, one of the heaviest lifts, or nothing in particular. Stated rather than left to be inferred from the number, which an athlete's own adjustment moves off the table. Known values: jump, accessory, heavy, standard. Treat unknown values as "other".
            - `note` string
            - `cue` string: The routine's own cue for this exercise, as whoever scheduled it wrote it (`routine.scheduledBy`). Show it attributed to them, never as Body Lab's words, and apart from `note`, which is Body Lab's. Absent when there is none.
            - `supersetGroup` string: The routine's superset letter: exercises sharing one are done alternately, in the order listed. Absent when the exercise stands alone.
            - `weightCheck` string: A sentence to show where this exercise's sets are recorded, when the routine stated a weight more than 10% above the athlete's estimated one-rep max for it — the shape of pounds read as kilograms. It names the lift and the likely slip. Advice about the number only: `weightKg` is still the routine's, and logging is never blocked. Absent otherwise, including whenever there is no estimate to compare with.
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
          - `routine` object, nullable: The routine this session is. Absent or null for one of Body Lab's own; `template` is "routine" when it's set.
            - `id` string, required: The routine, as `GET /strength-routines` lists it and `POST /strength-sessions` takes it as `routineId`.
            - `name` string, required: What the routine was called: "Upper day".
            - `scheduledBy` string, nullable, required: The connected app that put this on the calendar, as its OAuth client id — the `clientId` that `GET /schedule/apps` names and `POST /schedule/apps/withdraw` takes. Null when the athlete entered it themselves, in any of their own apps.
        - object
          - `kind` 'rest', required
          - `note` string, required
          - `tradeoff` string, nullable, required: What choosing this instead of today's session costs or gains, one sentence. Set only on alternates; null on the prescribed session and on variants.
  - `week` object, nullable, required: The sessions the engine expects for the coming week if every day goes as shown. A projection, not a plan: each day is decided that morning.
    - `from` string, date, required: The day this was computed on. It recalculates every day.
    - `days` object[], required: Seven days, today first.
      - `date` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
      - `precision` string, required: session: today or tomorrow, the same session as that card. outline: a later day — class, sport and an approximate duration, no targets. Known values: session, outline. Treat unknown values as "other".
      - `sessionClass` 'rest' | 'recovery' | 'easy' | 'build' | 'quality' | 'strength' | 'complete', required
      - `kind` 'endurance' | 'strength' | 'rest', required
      - `modality` string, nullable, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
      - `durationMin` number, nullable, required: Exact on a session day, rounded to 5 minutes and approximate on an outline day; null on a rest day.
      - `workoutName` string, nullable, required: The workout's name, on a session day only.
      - `commitment` object, nullable, required: Set when this day's session is one the athlete arranged themselves rather than one Body Lab chose — a club ride, a run with friends — and carrying its name. Present on an outline day too, unlike `workoutName`: the sport and a duration alone are indistinguishable from a session Body Lab invented, and a surface drawing the week should name the athlete's own arrangement so they recognise it. Null on every other day, including a day with a commitment on it that something else took over (a race on the same date, or an illness that replaced the session).
        - `name` string, required: What the athlete called it: "Ride with Nick and Rachael".
      - `alsoCommitted` object[]: The other sessions arranged on this date, beside the one the day is drawn as — the routine after a club ride, a second ride. A strength session here after an endurance day also sets `strengthAfter`. Absent when the date holds one committed session or none.
        - `name` string, required: What the athlete or the app called it.
        - `kind` 'endurance' | 'strength', required
        - `modality` string, nullable, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
        - `durationMin` number, required: At the day's precision, like `durationMin`: exact on a session day, rounded to 5 minutes on an outline day.
      - `longest` boolean, required: The week's long session: its longest endurance day, when that's 90 minutes or more.
      - `strengthAfter` boolean, required: A strength session follows the day's endurance session: the strength floor is owed one, and it's offered once the ride or run is done.
      - `deload` boolean, required: A planned volume cut: a recovery week, a taper or post-event recovery.
      - `phase` string, nullable, required: What shapes the day beyond its session. Known values: event_day, taper, recovery, recovery_week. Treat unknown values as "other".
      - `preparation` string, nullable, required: Which part of the run-up to an A event a day sits in. An emphasis worked out each morning from the event's date, not a stored plan. Known values: base, build, specific, taper. Treat unknown values as "other".
      - `events` object[], required: Events on this date, any priority.
        - `id` string, required: Opaque identifier.
        - `name` string, required
        - `priority` 'A' | 'B' | 'C', required: What Body Lab does with the event. A: the plan is built around it — the run-up is shaped into preparation phases, volume tapers into it, the day before is short and easy, and a recovery window is protected after it. B and C: on the athlete's calendar, where they tilt which sport a nearby day picks but change no session; the two behave identically. Any event, at any priority, makes its own date an event day, when nothing is prescribed. The letters come from a coaching convention Body Lab never explains to the athlete, so say what the priority does rather than reading the letter back to them.
      - `assumedHealth` object, nullable, required: An illness or injury still open on this day, which the projection assumes hasn't cleared — nobody knows the day it will. Null today, and on days with none.
        - `illness` string, nullable, required: mild: symptoms above the neck only (runny or blocked nose, sneezing, sore throat). systemic: fever, chest symptoms, body aches or feeling generally unwell. The split is a widely used convention, not an evidence-based threshold. Known values: mild, systemic. Treat unknown values as "other".
        - `injuries` string[], required
      - `estimatedLoad` number, nullable, required: Estimated training load if the day goes as shown, on Body Lab's calibrated scale, rounded to 5: the load the projection counts the day as adding before it decides the next. Null when there's none — a rest day, today once a recording has settled it, and an event day, whose race the projection doesn't estimate.
      - `strain` object, nullable, required: The projected strain this day was decided with, against the athlete's own high; null when there's too little history for one. An estimate, like the rest of the projection.
        - `value` number, required: The projected strain this day was decided with (the last seven completed days' load times their monotony), rounded to 10.
        - `high` number, nullable, required: This day's own personal high, rounded to 10. It can move within the week as the projected days enter the history it's read over; null without enough history.
        - `above` boolean, required: Whether the engine counts this day's strain as above the athlete's high. Its own test, which also needs the week to be monotonous and heavy against chronic load, so a value over `high` can still be false; never compare the two numbers instead.
        - `reliable` boolean, required: False when an event day after today lies inside this day's seven-day window: the race's load is missing from the projection, so the strain understates.
      - `againstWarning` string, nullable, required: Why this day's class goes against a warning showing today. strain_eased: today's strain is above the athlete's high, and by this day the projection takes it back under, so a hard day the strain rule would have held easy is allowed. Only on a training day after today that the athlete didn't arrange, and never past an event the projection can't estimate. Known values: strain_eased. Treat unknown values as "other".
    - `note` string, required: Why the week will change, to show with it wherever it's shown.
    - `assumptions` string[], required: What every projection takes as given, one sentence each.
    - `healthAssumption` string, nullable, required: The illness or injury the week assumes is still active, in a sentence to show with the week; null when there's none.
  - `report` object, required: What the athlete told Body Lab about today, and any return window still running. Tomorrow's plan carries illnesses and injuries forward, not today's time or equipment.
    - `day` object, required
      - `date` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
      - `timeAvailableMin` integer, nullable, required: Minutes available today; the prescription is shortened to fit.
      - `equipment` string, nullable, required: Today's strength equipment when it differs from the place the athlete's sessions are written for. A rung named here wins outright for today, rather than narrowing what they own. Null hands today back to whichever place is in force. Known values: gym, dumbbells, bodyweight. Treat unknown values as "other".
      - `unavailableSports` string[], required: Sports the athlete can't do today.
      - `expectedAltitude` object, nullable, required: The altitude the athlete confirmed they're at today. It sets today's watt targets ahead of recent recordings and home altitude; heart-rate targets never change. Only the altitude is kept, never a location.
        - `altitudeM` integer, required: Metres, rounded to 10.
        - `source` string, required: How the altitude was worked out: device_altitude (the phone measured it), recordings (the athlete's own activities that started near where they are), elevation_data (a terrain model at their rough location), entered (typed in). Known values: device_altitude, recordings, elevation_data, entered. Treat unknown values as "other".
      - `woundDown` boolean, nullable, required: Whether the athlete was off screens by their chosen wind-down hour the night before today. Null is a night they didn't answer, which is a night not measured rather than a night failed. It changes nothing about the prescription — Body Lab records it so the athlete can see it beside their sleep, and asserts no link between the two.
    - `active` object[], required: Illnesses and injuries still going, oldest first.
      - `id` string, required: Opaque identifier.
      - `kind` string, required: What was reported. Known values: illness, injury. Treat unknown values as "other".
      - `severity` string, nullable, required: mild: symptoms above the neck only (runny or blocked nose, sneezing, sore throat). systemic: fever, chest symptoms, body aches or feeling generally unwell. The split is a widely used convention, not an evidence-based threshold. Known values: mild, systemic. Treat unknown values as "other".
      - `region` string, nullable, required: Where it hurts, coarsely: foot_ankle (foot or ankle), lower_leg (shin, calf or Achilles), knee, hip_thigh, lower_back, neck_upper_back, shoulder, arm_hand (elbow, wrist or hand). Known values: foot_ankle, lower_leg, knee, hip_thigh, lower_back, neck_upper_back, shoulder, arm_hand. Treat unknown values as "other".
      - `hurtsToLoad` boolean, nullable, required: Injury only: whether putting load through it hurts.
      - `startedOn` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
      - `confirmedOn` string, date, required: The last day the athlete said it was still going.
      - `clearedOn` string, date, nullable, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
      - `needsConfirmation` boolean, required: Still going, and last confirmed before today: ask the athlete whether it still is. It applies either way until cleared.
    - `returning` object[], required: Cleared recently enough that the first days back are still capped.
      - `reportId` string, required: Opaque identifier.
      - `kind` string, required: What is being returned from. Known values: illness, injury. Treat unknown values as "other".
      - `severity` string, nullable, required: mild: symptoms above the neck only (runny or blocked nose, sneezing, sore throat). systemic: fever, chest symptoms, body aches or feeling generally unwell. The split is a widely used convention, not an evidence-based threshold. Known values: mild, systemic. Treat unknown values as "other".
      - `region` string, nullable, required: Where it hurts, coarsely: foot_ankle (foot or ankle), lower_leg (shin, calf or Achilles), knee, hip_thigh, lower_back, neck_upper_back, shoulder, arm_hand (elbow, wrist or hand). Known values: foot_ankle, lower_leg, knee, hip_thigh, lower_back, neck_upper_back, shoulder, arm_hand. Treat unknown values as "other".
      - `day` integer, required: 1 on the day it was cleared.
      - `of` integer, required: How many days the window runs.
      - `maxMin` integer, required: Today's minute ceiling on the sessions it applies to.
  - `todayIsDone` boolean, required
  - `completed` object, required
    - `activities` object[], required
      - `id` string, required: Opaque identifier.
      - `durationS` number, nullable, required
      - `load` number, nullable, required
      - `activityType` string, nullable, required: Sport, from the engine's vocabulary (packages/engine/src/sports.ts), e.g. road_biking, running, hiking, strength_training. Known values: road_biking, running, hiking, strength_training, other. Treat unknown values as "other".
      - `startTime` string, date-time, required: An instant: ISO-8601 with offset, e.g. 2026-09-13T06:30:00Z.
    - `strengthSessions` object[], required
      - `id` string, required: Opaque identifier.
      - `activityId` string, nullable, required: Opaque identifier.
      - `exercises` string[], required
      - `performedAt` string, date-time, required: An instant: ISO-8601 with offset, e.g. 2026-09-13T06:30:00Z.
      - `durationS` number, nullable, required
      - `sessionRpe` number, nullable, required
      - `load` number, nullable, required
      - `setCount` integer, required

## Other responses

- `401`: Error: UNAUTHORIZED.

---

[API](https://skmtc.dev/bodylab/apis/body-lab-api.md) · [All operations](https://skmtc.dev/bodylab/apis/body-lab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bodylab/apis/body-lab-api/revisions/544c201eaf88?raw)
