Activities
An activity is one training ride, run, swim, or race. Each activity belongs to an athlete and can carry a nutrition prescription and post-activity feedback. Whatever your platform calls these (workouts, sessions, events), the Saturday object is an activity, and every field and route below uses that word.Activity lifecycle
- Create the activity with type and duration, plus intensity and thermal stress if you have them.
- Calculate a nutrition prescription for it.
- Submit feedback afterward, if you collect it.
Creating an activity
Activity fields
Create accepts these fields and no others. There is no field for a display name or a scheduled start time; keep those on your side and join on
external_id.
external_id is worth sending. Saturday’s hosted onboarding finish screen uses it to deep-link the athlete back into the matching activity in your app.
Multi-sport activities
Each activity carries onetype, and Saturday prescribes for that type: an athlete fuels differently on the bike than on the run. For a triathlon or a brick, create one activity per leg.
To work out which legs a multi-sport title implies, use POST /v1/infer/brick-types.
Calculating a prescription
/v1/nutrition/calculate:
- Full tier (subscribed, covered, or in-trial athletes):
tier: "full"with theprescriptionobject (carb, sodium, and fluid totals and per-hour rates), stored on the activity. When the fueling profile is incomplete, the stored prescription carriescarb_range_g_per_hrand the other range fields withprofile_complete: falsein place of exact numbers. Trial responses also carrytier_source: "trial",trial_ends_at, andtrial_calls_remaining_today. Each calculation debits the trial’s daily call allowance, exactly like nutrition calculate. - Teaser tier:
tier: "teaser"with per-hour ranges (carb_range_g_per_hr,sodium_range_mg_per_hr,fluid_range_ml_per_hr), asubscription_ctacarrying the athlete’s subscribe link, and required attribution. Nothing is stored, soGET .../prescriptionkeeps returning the last full-tier result if there was one.
safety metadata is included on every tier.
Getting a stored prescription
calculated_at is a Unix timestamp in seconds, not an ISO 8601 string. Timestamps across the athlete and activity objects (created_at, updated_at) use the same encoding.
On this stored-prescription endpoint, read
prescription.* for values and safety.* for guardrails. POST /v1/nutrition/calculate instead returns flat nutrition fields. The activity calculation uses the tier-aware envelope described above. The not_instructions: true flag marks these as recommendations for a person to consider, not commands to execute.Getting an activity with prescription
Listing activities
limit defaults to 50 and is clamped to 1-200; pass pagination.next_cursor back as cursor for the next page, URL-encoded and unchanged. There are no date-range or type filters; filter on your side, or track activities by external_id.
Submitting feedback
Record how the fueling went:Feedback fields
Feedback is stored on the activity and returned with it on read, so your UI can show an athlete what they said last time and a coach can review a block of training. Feedback does not change the calculation: prescriptions come from the athlete’s profile and the activity’s parameters, so a rated activity does not change the next one. Update the profile fields to change future prescriptions.
Activity type inference
If your platform has activity metadata but not a clean type, Saturday can infer one:activity_type is one of the seven activity keys or "unknown", and type carries the same value. When the model is unavailable or the evidence is too thin, you get "unknown" with low or zero confidence rather than an error, so branch on the type. A 502 means the inference service itself could not be reached; it is not counted as usage, so retry with backoff.
Sending more than a title sharpens the result. The endpoint also reads pre_activity_comment, workout_type (your provider’s own coarse type), tss_calculation_method, structure_summary, velocity in m/s, has_power, and cadence, and those structured signals resolve many cases before the model is consulted. The API reference names two of these differently, and both spellings work: activity_name is title, and source_type is workout_type. You can also send device, the recording device’s name.
A strength session also carries intensity: 1 light, 2 medium, 3 heavy. Lift uses levels 1-3 of intensity_level, so pass the value unchanged as intensity_level when you create the lift activity; rescaling it onto 1-9 raises the carbohydrate ceiling. An entry that marks a day with no training, such as “Rest day”, comes back with "rest_day": true and type "unknown"; it is not a workout to create.
For multi-sport titles like “Bike/run brick”, use POST /v1/infer/brick-types. It takes the same fields plus an optional segments list of the legs you already know, in order, and returns every leg in order:
sub_types is the same list without the order numbers. Create one activity per leg.