Workout-Recommendation Chatbot
Pair the Semantic Exercise Search API with your own LLM to build a conversational fitness assistant with your own generation logic:
1. The user describes their goal or constraint in natural language.
2. Your app calls /api/exercises/search with the user's message as the query.
3. The returned exercises are passed as context to your LLM.
4. The LLM generates a personalized recommendation using real exercise data.
typescript
1User message
2 │
3 ▼
4/api/exercises/search ◄── passes user message as query
5 │
6 │ returns matching exercises (title, description, media URLs, …)
7 ▼
8Your LLM ◄── receives exercises as context + user message
9 │
10 ▼
11Workout recommendation with exercise detailsUse each exercise's thumbnail_url and video_url to show visual previews of recommended exercises directly in your chat UI alongside the LLM's text response.
If you'd rather have KinesteX handle the generation logic end-to-end (intents, plan state, undo/redo, progression), use the Trainer Chat API instead.
Minimal chatbot turn (TypeScript)
KinesteX exercise search + an LLM (Claude shown here — any LLM works) → a grounded workout recommendation.
React TS (Web) example:
1const KINESTEX_BASE_URL = "https://data.kinestex.com/api";
2const CLAUDE_API_URL = "https://api.anthropic.com/v1/messages";
3
4// --- Step 1: Search exercises using the user's message as the query ---
5async function searchExercises(userMessage: string, jwtToken: string) {
6 const params = new URLSearchParams({
7 query: userMessage,
8 limit: "8",
9 basic_info: "true", // lightweight payload is sufficient for LLM context
10 });
11
12 const response = await fetch(
13 `${KINESTEX_BASE_URL}/exercises/search?${params}`,
14 { headers: { Authorization: `Bearer ${jwtToken}` } }
15 );
16 if (!response.ok) {
17 throw new Error(`Exercise search failed: ${await response.text()}`);
18 }
19
20 const data = await response.json();
21 return data.exercises as Array<{
22 id: string;
23 title: string;
24 description: string;
25 thumbnail_url: string;
26 video_url: string;
27 }>;
28}
29
30// --- Step 2: Ask the LLM to build a recommendation from the results ---
31async function getWorkoutRecommendation(
32 userMessage: string,
33 exercises: Awaited<ReturnType<typeof searchExercises>>,
34 claudeApiKey: string
35): Promise<string> {
36 const exerciseContext = exercises
37 .map((ex) => `- ${ex.title}: ${ex.description}`)
38 .join("\n");
39
40 const response = await fetch(CLAUDE_API_URL, {
41 method: "POST",
42 headers: {
43 "x-api-key": claudeApiKey,
44 "anthropic-version": "2023-06-01",
45 "content-type": "application/json",
46 },
47 body: JSON.stringify({
48 model: "claude-haiku-4-5-20251001",
49 max_tokens: 400,
50 system: `You are a friendly fitness coach.
51The user will describe their fitness goal or constraint.
52Recommend a short workout using only the exercises provided.
53Be concise — 3-5 exercises, 2-3 sentences of explanation.`,
54 messages: [
55 {
56 role: "user",
57 content: `User request: "${userMessage}"
58
59Available exercises:
60${exerciseContext}
61
62Please recommend a workout using these exercises.`,
63 },
64 ],
65 }),
66 });
67
68 const result = await response.json();
69 return result.content[0].text;
70}
71
72// --- Step 3: Combine into a single chatbot turn ---
73async function chatbotTurn(userMessage: string, jwtToken: string, claudeApiKey: string) {
74 const exercises = await searchExercises(userMessage, jwtToken);
75 const recommendation = await getWorkoutRecommendation(userMessage, exercises, claudeApiKey);
76 return {
77 recommendation,
78 exercises, // includes thumbnail_url and video_url for your chat UI
79 };
80}
81
82// --- Usage ---
83// const result = await chatbotTurn(
84// "I have bad knees and want a gentle 10-minute workout",
85// "your_kinestex_jwt",
86// "your_claude_api_key"
87// );