# Workout-Recommendation Chatbot

Pair the [Semantic Exercise Search API](/docs/trainer-api/trainer-api-search) 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.

```
User message
     │
     ▼
/api/exercises/search    ◄── passes user message as query
     │
     │  returns matching exercises (title, description, media URLs, …)
     ▼
Your LLM                 ◄── receives exercises as context + user message
     │
     ▼
Workout 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](/docs/trainer-api/trainer-api-chat) instead.

**Minimal chatbot turn (TypeScript)**

KinesteX exercise search + an LLM (Claude shown here — any LLM works) → a grounded workout recommendation.

_React (TypeScript)_
```tsx
const KINESTEX_BASE_URL = "https://data.kinestex.com/api";
const CLAUDE_API_URL    = "https://api.anthropic.com/v1/messages";

// --- Step 1: Search exercises using the user's message as the query ---
async function searchExercises(userMessage: string, jwtToken: string) {
  const params = new URLSearchParams({
    query: userMessage,
    limit: "8",
    basic_info: "true", // lightweight payload is sufficient for LLM context
  });

  const response = await fetch(
    `${KINESTEX_BASE_URL}/exercises/search?${params}`,
    { headers: { Authorization: `Bearer ${jwtToken}` } }
  );
  if (!response.ok) {
    throw new Error(`Exercise search failed: ${await response.text()}`);
  }

  const data = await response.json();
  return data.exercises as Array<{
    id: string;
    title: string;
    description: string;
    thumbnail_url: string;
    video_url: string;
  }>;
}

// --- Step 2: Ask the LLM to build a recommendation from the results ---
async function getWorkoutRecommendation(
  userMessage: string,
  exercises: Awaited<ReturnType<typeof searchExercises>>,
  claudeApiKey: string
): Promise<string> {
  const exerciseContext = exercises
    .map((ex) => `- ${ex.title}: ${ex.description}`)
    .join("\n");

  const response = await fetch(CLAUDE_API_URL, {
    method: "POST",
    headers: {
      "x-api-key": claudeApiKey,
      "anthropic-version": "2023-06-01",
      "content-type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-haiku-4-5-20251001",
      max_tokens: 400,
      system: `You are a friendly fitness coach.
The user will describe their fitness goal or constraint.
Recommend a short workout using only the exercises provided.
Be concise — 3-5 exercises, 2-3 sentences of explanation.`,
      messages: [
        {
          role: "user",
          content: `User request: "${userMessage}"

Available exercises:
${exerciseContext}

Please recommend a workout using these exercises.`,
        },
      ],
    }),
  });

  const result = await response.json();
  return result.content[0].text;
}

// --- Step 3: Combine into a single chatbot turn ---
async function chatbotTurn(userMessage: string, jwtToken: string, claudeApiKey: string) {
  const exercises = await searchExercises(userMessage, jwtToken);
  const recommendation = await getWorkoutRecommendation(userMessage, exercises, claudeApiKey);
  return {
    recommendation,
    exercises, // includes thumbnail_url and video_url for your chat UI
  };
}

// --- Usage ---
// const result = await chatbotTurn(
//   "I have bad knees and want a gentle 10-minute workout",
//   "your_kinestex_jwt",
//   "your_claude_api_key"
// );
```

---
Source: https://www.kinestex.com/docs/guides/guide-trainer-chatbot · Index: https://www.kinestex.com/llms.txt
