AI Trainer Events
Events emitted by the AI Trainer Chat view. On Swift/Kotlin/Flutter they arrive through the generic custom_type / CustomType case with the parsed JSON as a dictionary/map.
| Event | Data Fields | Description |
| trainer_schedule_next_workout | scheduledFor: string, sessionType: string | User picked a date/time for their next session. scheduledFor is YYYY-MM-DDTHH:MM device-local time; sessionType is "workout" or "assessment" (treat missing as "workout") |
| open_subscription_flow | source: string, date: string | A non-subscribed user tried to generate a workout — present your subscription flow and post subscription_result back ("purchased" or "dismissed") |
| trainer_assessment_started | date: string | User began the in-app fitness assessment |
| trainer_assessment_completed | date: string, results: object, fitnessLevel: string | Assessment finished, with per-exercise reps and level (results.squats / results.pushups) plus an overall fitnessLevel ("beginner" / "intermediate" / "advanced"). A test that was skipped (for example because of an injury) arrives as { reps: null, level: null } |
| trainer_assessment_skipped | date: string, reason: string | User chose to set their fitness level manually (reason: "set_manually") |
| trainer_profile_updated | source: string, profile: object | The user's fitness profile changed. Carries the full current profile; source is "onboarding", "assessment", "chat", or "host" (an echo of your own update_trainer_profile). Persist it on your side |
| trainer_profile_onboarding | stage: string, data: object, date: string | One applied step of the onboarding wizard. data holds just that step's answer. Fields that also exist in the profile object use the same names; body_parts, training_style, equipment and duration carry fields the profile doesn't have. Stages: profile, fitness_goals, goal_recommendation, body_parts, injuries, injury_detail, training_style, fitness_level, equipment, duration, other_preferences, workout_frequency. Also sent again when the user edits an answer card in the chat before the plan starts generating |
| trainer_profile_clicked | profile: object or null, date: string | User opened the profile while customProfileAction: true. Open your own profile screen. profile has the same shape as in trainer_profile_updated (null when no profile is stored yet or it could not be loaded) |
| trainer_calendar_clicked | date: string | User tapped the calendar button while customCalendarAction: true. Open your own progress or calendar screen |
| trainer_workout_days_updated | days: string[], daysPerWeek: number, preferredTime: string | The user picked (or edited) the weekdays they want to train on. days are lowercase "mon".."sun" in week order; preferredTime is device-local HH:MM. Same answer also arrives as the workout_frequency onboarding stage and in the profile. See the workout days guide |
| workout_exit_request | - | The user exited a workout that was launched from the trainer |
Full payload examples and the subscription handshake: AI Trainer Chat guide and the subscription gating guide.