Trainer Chat Endpoint
The unified conversational endpoint. Your users talk to the trainer in natural language — "give me a 30-minute dumbbell chest workout", "make it harder", "let's start" — and the API classifies each message's intent, generates or modifies a workout plan, answers fitness questions, and manages the user's fitness profile.
1POST https://data.kinestex.com/api/trainer/chatIntegration is minimal by design:
- One required field per message (
message). Everything else is optional. - The fitness profile is stored server-side. Send it once and never again.
- Workout history is stored server-side. The AI plans around recently worked muscle groups automatically.
- Workout preferences are extracted from the message. "45 minutes, dumbbells, chest and triceps" needs no separate preferences object.
Request fields:
| Field | Type | Required | Description |
message | string | Yes | The user's natural-language message |
session_id | string | No | Chat session ID. Omit to use the user's default session (created automatically). Pass the value from a previous response to target a specific session. |
stage | string | No | Client-declared intent that skips AI classification when you already know it: "CREATE_WORKOUT" (first message of a session) or "RECOMMEND_NEXT" (first message after a completed workout). Anything else falls back to the classifier. |
profile_data | object | No | The user's fitness profile (see below). Only needed once — it is saved server-side and reused. Sending it again updates the stored profile. |
workout_details | object | No | Explicit workout preferences (see below). If omitted on a create, preferences are extracted from the message itself. |
readiness | object | No | Free-form JSON describing the user's current condition (sleep, soreness, energy…), passed to the AI as context. |
Advanced AI controls (model, enable_thinking, thinking_effort: "minimal" / "low" / "medium" / "high") are also accepted — available model identifiers are provided by your KinesteX contact.
Profile object (persists server-side per user, always bound to the authenticated user):
| Field | Type | Values / Unit | |||
age | int | years | |||
weight | float | kg | |||
height | float | cm | |||
gender | string | "MALE", "FEMALE", "OTHER" | |||
fitness_goals | string[] | "strength", "muscle_gain", "weight_loss", "cardio_endurance", "general_fitness", "wellness_flexibility" | |||
fitness_level_pushups | string | e.g. "0-5", "6-15", "16+" | |||
fitness_level_cardio | string | e.g. "1 mile", "2 mile", "3+ mile" | |||
fitness_level_squats | string | e.g. "0-10", "10-21", "22+" | |||
injuries | object[] | `{ "body_part", "preference": "avoid" \ | "include", "severity": "severe" \ | "moderate" \ | "light" } — "avoid" hard-filters exercises targeting that body part; "include"` keeps them but picks light/rehabilitative variants |
health_conditions | string[] | free text, e.g. ["Hypertension"] | |||
other_preferences | string | free text, passed verbatim to the AI | |||
preferred_duration_minutes | int | default workout length (also learned from explicit duration requests) | |||
training_intensity | int | 1–10, intensity ceiling for the plan | |||
structured_program | bool | true = has followed a structured weight-training program (heavier low-rep sets) |
Removing an injury restriction via chat (e.g. "my knee is fine now") triggers an are-you-sure confirmation before the profile actually changes.
Workout details object:
| Field | Type | Description |
equipment | string[] | e.g. ["dumbbells", "kettlebells"] or ["bodyweight"] |
duration | int | Minutes. If omitted: explicit in message → previous workout in this conversation → profile preference → 30-minute default |
body_parts | string[] | e.g. ["Chest", "Shoulders", "Triceps"] |
include_warmup / include_cooldown | bool | Prepend warmup / append cooldown exercises |
post_workout_feedback | object | Check-in from the just-completed workout, sent with stage: "RECOMMEND_NEXT" — rpe (1–10, primary progression signal), discomfort ("no_pain" / "mild" / "sharp"), and when sharp: pain_severity (1–10), pain_stopped, pain_body_parts |
Intents — every message is classified into one of these (returned in the response so your UI can react). There are no magic strings — "confirm", "let's go", and "start the workout" all classify as START_WORKOUT:
| Intent | Triggered by | Effect |
CREATE_WORKOUT | "make me a workout…" | Semantic search + AI plan generation |
MODIFY_WORKOUT | "make it harder", "remove the squats" | AI edits the current plan |
START_WORKOUT | "let's start", "confirm", "looks good" | Returns the final plan with workout_plan.action = "CONFIRMED" |
UNDO / REDO | "undo" / "redo" | Instant plan history navigation (no AI call) |
ASK_QUESTION | "what's a superset?" | Conversational answer |
DISCUSS_RESULTS | "how did I do yesterday?" | Discusses the user's saved workout results |
RECOMMEND_NEXT | first message after a completed workout | Recovery-aware next workout using stored history + feedback |
UPDATE_PROFILE | "I weigh 78kg now", "my knee hurts" | Updates the stored profile |
OFF_TOPIC | anything non-fitness | Polite redirect |
Response — 200 OK:
1{
2 "message": "Here's your 30-minute upper-body dumbbell workout! ...",
3 "intent": "CREATE_WORKOUT",
4 "action": "WORKOUT_UPDATED",
5 "session_id": "9c1e37a2-4b7f-4f6e-9a2d-1f2e3d4c5b6a",
6 "workout_plan": {
7 "step": "PLAN_REFINEMENT",
8 "action": null,
9 "data": {
10 "turn_action": "WORKOUT_UPDATED",
11 "can_undo": false,
12 "can_redo": false,
13 "estimated_duration_seconds": 1820,
14 "sequences": [ ... ],
15 "exercises": [ ... ]
16 }
17 }
18}| Field | Description |
message | The trainer's reply — render this in your chat UI |
intent | The classified intent (table above) |
action | What this turn actually did — WORKOUT_UPDATED (plan content changed), NO_CHANGE, QUESTION (trainer awaits a reply), PROFILE_UPDATED (refresh cached profile data), INFO (plain reply) |
session_id | The persistent chat session ID — store it and send it on subsequent requests |
workout_plan | Present only on turns that involve a plan. workout_plan.action is "CONFIRMED" once the user starts/confirms the workout. |
session_id is the persistent chat session — the one you store and send back. The response may also carry a nested workout_plan.session_id; that is internal planning state and you never need to send it.workout_plan.data.sequences is the ordered plan: each item is an "exercise", "warmup", "cooldown" (with exercise_id, repeats) or a "rest" (with rest_countdown seconds). workout_plan.data.exercises holds the full exercise objects referenced by the sequences, including media URLs and per-language translations — pick the entry matching your requested language, falling back to "en".
Error responses:
| Status | Cause |
400 | Missing message, invalid JSON, unsupported advanced option |
401 | Missing or invalid JWT |
403 | { "error": "Subscription required to generate workouts", "code": "not_subscribed" } — only for generation intents when your company manages subscriptions (see Session Auth & Managed Subscriptions) |
404 | session_id doesn't exist or belongs to another user |
429 | Rate limit exceeded (see Sessions, Profile & Limits) |
500 | AI call or session storage failure — { "error": "Failed to process trainer chat", "details": "..." } |
Example — create, refine, confirm:
1# 1. First message (profile inline, preferences in the message)
2curl -X POST "https://data.kinestex.com/api/trainer/chat" \
3 -H "Authorization: Bearer <jwt>" -H "Content-Type: application/json" \
4 -d '{
5 "message": "Create a 30-minute dumbbell workout for chest and triceps",
6 "stage": "CREATE_WORKOUT",
7 "profile_data": { "age": 28, "weight": 75.0, "height": 180.0, "gender": "MALE", "fitness_goals": ["strength"] }
8 }'
9
10# 2. Refine (session_id from the previous response)
11curl -X POST "https://data.kinestex.com/api/trainer/chat" \
12 -H "Authorization: Bearer <jwt>" -H "Content-Type: application/json" \
13 -d '{ "session_id": "<session_id>", "message": "make it harder and add more core work" }'
14
15# 3. Confirm — natural language, no magic string required
16curl -X POST "https://data.kinestex.com/api/trainer/chat" \
17 -H "Authorization: Bearer <jwt>" -H "Content-Type: application/json" \
18 -d '{ "session_id": "<session_id>", "message": "looks good, let'\''s start" }'
19
20# 4. Next workout after completing one (recovery-aware, with post-workout feedback)
21curl -X POST "https://data.kinestex.com/api/trainer/chat" \
22 -H "Authorization: Bearer <jwt>" -H "Content-Type: application/json" \
23 -d '{
24 "session_id": "<session_id>",
25 "stage": "RECOMMEND_NEXT",
26 "message": "what should I do next?",
27 "workout_details": {
28 "post_workout_feedback": { "rpe": 8, "discomfort": "no_pain" }
29 }
30 }'See the full TypeScript lifecycle example in Guides & Examples.