KinesteX

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
234/api/exercises/search    ◄── passes user message as query
56     │  returns matching exercises (title, description, media URLs,)
78Your LLM                 ◄── receives exercises as context + user message
91011Workout recommendation with exercise details

Use 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// );