First-run setup
Request
- Base URL: https://bodylab.lol/api/v1
- URL: https://bodylab.lol/api/v1/profile/onboarding
- Auth: HTTP bearer
Headers
The native client's platform, e.g. ios. Server-side and non-native callers omit it.
The native client's own semver version. With X-Client-Platform, a version below that platform's minimum is refused with CLIENT_UPGRADE_REQUIRED (426).
Request body
12h or 24h. Not nullable: there is always a clock, and 12h is the default.
How to display quantities; the API always speaks SI.
Millimetres of mercury. Send it with bloodPressureDiastolicMmHg, and the pair is recorded as a reading taken now. Null removes the most recent reading, and the profile falls back to the one before it.
Millimetres of mercury.
Null clears an entered value back to estimated.
Null clears an entered value.
True leaves jumps out of strength sessions. False puts them back, and so does null, which clears it to the default.
Metres. Null clears an entered value back to the one observed from recordings.
Replaces the whole set. Sending it also re-derives strengthEquipment, so the two can never disagree.
The hour to be off screens by, on the athlete's clock. 18-23 or 0-2 — the wrap is deliberate, for a late finisher or a shift worker. Any other hour is refused by the handler against isWindDownHour (@bodylab/engine/sleep), which is where that rule lives so the contract and the database CHECK can't drift from it. Null clears it.
Kilograms. Send it with barKind 'custom'; any other kind clears it.
daily, unusual_only or off.
Replaces the list of hidden Daily Update chips. An empty list brings them all back.
auto, or off for "I don't have periods".
Kilograms, for one clip or collar. 2.5 is a competition collar and is the most any federation recognises; 0 is an ordinary gym's spring clips and is the default.
Response
OK
IANA time zone, e.g. America/Denver.
How the athlete reads clock times: 12h ("6:26 AM", the default) or 24h ("06:26"). A display preference for the apps, in the athlete's time zone; every time the API sends is still an ISO instant or an HH:MM field, whatever this says. Known values: 12h, 24h. Treat unknown values as "other".
How to display quantities; the API always speaks SI.
The athlete's most recent blood pressure, upper number, in millimetres of mercury: the newest reading they have, whether their cuff synced it or they typed it in. Always set together with bloodPressureDiastolicMmHg, and always the two numbers of that one reading — they are stored in one row and a pair can't be assembled from two readings. Every reading is kept, not just this one. A measurement and nothing more: Body Lab attaches no category, threshold or judgement to it, and nothing it prescribes reads it.
The lower number of the same reading, millimetres of mercury. Null exactly when bloodPressureSystolicMmHg is.
Where a heart-rate anchor comes from: typed in, estimated from age, or observed in the athlete's own data (observed replaces estimated; entered is never overwritten). Known values: entered, estimated, observed. Treat unknown values as "other".
Where a heart-rate anchor comes from: typed in, estimated from age, or observed in the athlete's own data (observed replaces estimated; entered is never overwritten). Known values: entered, estimated, observed. Treat unknown values as "other".
Lactate threshold heart rate, bpm.
Functional threshold power, watts.
An instant: ISO-8601 with offset, e.g. 2026-09-13T06:30:00Z.
Sports the athlete does, most important first.
The altitude the athlete lives and usually trains at, metres. Watt targets are lowered only where today's altitude differs from the one their FTP was set at, so an athlete at altitude with an FTP from there sees no change.
Where home altitude comes from: typed in (never overwritten), or observed from where the athlete's recorded runs and rides usually start (follows the recordings). Known values: entered, observed. Treat unknown values as "other".
The kit the athlete's sessions are currently written for — whichever of their places is active, which is their home one unless they have switched. Each item maps to the heaviest single implement of it in kilograms, or null for "enough" (a rack needs no number). Absent keys are not owned. This is what strength sessions are written for; strengthEquipment above is the nearest of three old rungs, derived from this, and kept so a client that hasn't learned the set still gets a sensible answer. A ceiling is the heaviest one of a pair: an exercise taking two implements can load twice it.
The hour on the athlete's own clock they chose to be off screens by, 18-23 or 0-2, or null for not set. A convention the athlete picked, never a target Body Lab derived: no published study supports a particular cutoff, and the one popularly quoted was tested as a behaviour and did not hold up. Nothing is measured against it but what the athlete reports, and nothing about it reaches a prescription.
What the athlete lifts with. Strength sessions name only exercises this allows: gym is a barbell and rack (the default), dumbbells is dumbbells or kettlebells, bodyweight is no weights. Known values: gym, dumbbells, bodyweight. Treat unknown values as "other".
What the athlete wants training to build, one of three options that train differently. endurance (Endurance first, the default): two heavy strength sessions a week, fitted around the rest of the training. strength (Strength and endurance equally): three strength sessions a week, never straight after an interval session and often first in the day, with interval sessions unchanged. strength_first (Strength first): three longer strength sessions with more sets of the main lifts, usually first in the day, and endurance cut to a maintenance dose: shorter sessions at the same intensity and one interval session a week. A value a client doesn't recognise is read as endurance. Known values: endurance, strength, strength_first. Treat unknown values as "other".
True when the athlete has chosen to leave jumps out. When most of their training is supported (cycling, rowing, swimming), strength sessions open with a few minutes of jumps; this keeps them out of every session. False by default. Body Lab doesn't record why.
Which barbell the athlete loads: standard is their unit system's own standard bar (20 kg metric, 45 lb imperial — different bars, not conversions), custom reads barWeightKg, unknown is them saying don't guess, which a Smith machine or a trap bar deserves. Standard by default. Known values: standard, custom, unknown. Treat unknown values as "other".
The athlete's own barbell, in kilograms. Non-null exactly when barKind is custom.
The weight of ONE clip or collar, in kilograms; both sleeves get one. Zero by default: gym spring clips weigh next to nothing, while a competition collar is 2.5 kg each and a pair would add 5 kg to every barbell lift.
How often the Daily Update asks: daily (the default) on the first open of each day, unusual_only when last night's readings give a reason, or off. Patterns need ordinary nights as well as unusual ones. Known values: daily, unusual_only, off. Treat unknown values as "other".
Daily Update chips the athlete hid for good. They never show; Settings lists them so each can be brought back.
Whether the Daily Update may ask about a period starting: auto asks whom the profile says could have a cycle; off is the athlete saying they don't have periods (pregnancy, menopause and contraception that stops bleeding are covered, and Body Lab doesn't ask which). Known values: auto, off. Treat unknown values as "other".
An instant: ISO-8601 with offset, e.g. 2026-09-13T06:30:00Z.
True once this account has signed in from a phone app (iOS or Android), so a browser needn't offer the download again. Which device and when are deliberately left out. Only sign-ins that named their platform count: an athlete who was already using the app before platforms were recorded reads false until their next sign-in there.
Changes
No recorded changes to this operation across all 1 revision of this API.