Skip to main content

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

  1. Create the activity with type and duration, plus intensity and thermal stress if you have them.
  2. Calculate a nutrition prescription for it.
  3. 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 one type, 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

This combines the activity’s parameters with the athlete’s profile. Recalculating overwrites the previous prescription, so call it again whenever the activity changes: a revised duration, a new weather forecast. The response is tier-aware, with the same semantics as /v1/nutrition/calculate:
  • Full tier (subscribed, covered, or in-trial athletes): tier: "full" with the prescription object (carb, sodium, and fluid totals and per-hour rates), stored on the activity. Trial responses also carry tier_source: "trial", trial_ends_at, and trial_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), a subscription_cta carrying the athlete’s subscribe link, and required attribution. Nothing is stored, so GET .../prescription keeps returning the last full-tier result if there was one.
safety metadata is included on every tier.

Getting a stored prescription

Returns the most recently calculated prescription for this activity, wrapped with safety metadata:
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.
The {prescription, safety} envelope is the same on every Saturday endpoint that returns prescription data. Read prescription.* for values and safety.* for guardrails. The not_instructions: true flag marks these as recommendations for a person to consider, not commands to execute.
The activity-scoped endpoints populate the two ceilings and not_instructions, but not the rest of the block: confidence_score comes back as 0 and warnings as null from both this read and POST .../calculate. Handle the null, and do not render that zero as a confidence of zero. When you need a confidence score or prescription warnings, call POST /v1/nutrition/calculate with the same parameters. See Safety.

Getting an activity with prescription

The activity response carries the stored prescription inline.

Listing activities

Activities come back newest first, paginated by cursor. limit defaults to 50 and is clamped to 1-200; pass pagination.next_cursor back as cursor for the next page. 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. It does not currently feed back into the calculation: prescriptions come from the athlete’s profile and the activity’s parameters, and 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:
Response:
type is one of the seven activity keys or "unknown". Inference never fails the request: when the model is unavailable or the evidence is too thin, you get "unknown" with confidence: 0, so branch on the type rather than on an error. Sending more than a title sharpens the result. The endpoint also reads pre_activity_comment, workout_type (your provider’s own coarse type), velocity in m/s, has_power, and cadence, and those structured signals resolve most cases before the model is consulted. For multi-sport titles like “Bike/run brick”, use POST /v1/infer/brick-types. It returns sub_types (for example ["bike", "run"]) with a single confidence, and you create one activity per leg.