{
  "name": "Coach fitness and nutrition on Telegram with Claude, Google Sheets and Calendar",
  "nodes": [
    {
      "id": "f518cb88-e89c-4617-aa02-9de84528dce6",
      "name": "Receive Telegram message",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        0,
        112
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "e58bcd0d-e1ba-4942-811d-1d7d4f57acef",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-coach-language",
              "name": "coach_language",
              "type": "string",
              "value": "French"
            },
            {
              "id": "cfg-transcription-language",
              "name": "transcription_language",
              "type": "string",
              "value": "fr"
            },
            {
              "id": "cfg-date-format",
              "name": "date_format",
              "type": "string",
              "value": "dd/MM/yyyy"
            },
            {
              "id": "cfg-memory-context-length",
              "name": "memory_context_length",
              "type": "number",
              "value": 15
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "273fac60-9983-4452-a6d4-44245a2abc16",
      "name": "Check for voice message",
      "type": "n8n-nodes-base.if",
      "position": [
        448,
        112
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "or",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.message.voice.file_unique_id }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "23122dc8-063a-4093-95ca-add86fb13b0b",
      "name": "Download voice message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        672,
        64
      ],
      "parameters": {
        "fileId": "={{ $('Receive Telegram message').item.json.message.voice.file_id }}",
        "resource": "file",
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "61fb8fec-d6fb-4e86-a1e3-57b6810ab789",
      "name": "Transcribe voice with OpenAI",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        896,
        64
      ],
      "parameters": {
        "options": {
          "language": "={{ $('Configuration').item.json.transcription_language }}"
        },
        "resource": "audio",
        "operation": "transcribe"
      },
      "typeVersion": 2.3
    },
    {
      "id": "4e8acdeb-ac74-4261-862d-e3fe0c9ef484",
      "name": "Normalize user message",
      "type": "n8n-nodes-base.set",
      "position": [
        1232,
        64
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "nm-user-message",
              "name": "user_message",
              "type": "string",
              "value": "={{ $('Receive Telegram message').item.json.message.text || $json.text }}"
            },
            {
              "id": "nm-chat-id",
              "name": "chat_id",
              "type": "string",
              "value": "={{ $('Receive Telegram message').item.json.message.chat.id }}"
            },
            {
              "id": "nm-current-date",
              "name": "current_date",
              "type": "string",
              "value": "={{ $now.toFormat($('Configuration').item.json.date_format) }}"
            },
            {
              "id": "nm-weekday",
              "name": "weekday",
              "type": "string",
              "value": "={{ $now.setLocale('fr').toFormat('cccc') }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ed969d6a-a07e-4724-9eae-3815169385b4",
      "name": "AI fitness coach agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1776,
        64
      ],
      "parameters": {
        "text": "={{ $json.user_message }}",
        "options": {
          "maxIterations": 40,
          "systemMessage": "=# RÔLE\nTu es un coach sportif personnel et expert en nutrition. Tu accompagnes l'utilisateur vers ses objectifs de forme avec un suivi rigoureux, personnalisé et bienveillant. Tu t'exprimes TOUJOURS en {{ $('Configuration').item.json.coach_language === 'French' ? 'français' : $('Configuration').item.json.coach_language }}, sur un ton motivant, professionnel et exigeant.\n\n# POSTURE : ÉTAT D'ESPRIT DE DÉVELOPPEMENT\nTu adoptes en permanence une mentalité de développement (growth mindset). Cette posture PRIME sur le ton motivant : tu restes exigeant et honnête, jamais flatteur.\n- Quand l'utilisateur rate une séance ou stagne, ne le rassure pas à vide. Extrais la leçon concrète et donne la prochaine action précise. Un échec est une donnée, pas un verdict.\n- Reformule tout « je n'y arrive pas » en « pas ENCORE », et montre le chemin.\n- Félicite le PROCESSUS : effort, régularité, méthode. Ne félicite JAMAIS un talent supposé.\n- Quand l'utilisateur se dévalorise, consulte Read workout history pour lui rappeler des preuves concrètes de sa progression, puis recentre-le sur la prochaine action.\n\n# TES OUTILS\n- Read user profile : lire le profil (poids, objectif, niveau, blessures).\n- Update user profile : modifier une donnée du profil (usage strict, voir règle de sécurité).\n- Log workout session : enregistrer une séance réalisée.\n- Read workout history : consulter l'historique des séances.\n- Log meal in nutrition journal : enregistrer un repas consommé.\n- Read today meals : lire les repas du jour pour un bilan.\n- Save weekly nutrition plan : créer/mettre à jour le plan de repas et la liste de courses.\n- Save weekly training plan : créer/mettre à jour le programme de la semaine.\n- Read weekly training plan : consulter le programme de la semaine.\n- Create workout event in Calendar : planifier une séance dans l'agenda.\n\n# CONNAISSANCE DE L'UTILISATEUR\n- Avant tout conseil personnalisé, consulte Read user profile.\n- Tiens TOUJOURS compte des blessures : adapte les exercices pour ne jamais les aggraver.\n\n# ENREGISTREMENT DES DONNÉES\n- Séance terminée rapportée (exercice, charge, répétitions) : utilise Log workout session.\n- REPAS, RÈGLE DE QUANTITÉ : AVANT d'estimer les macros, vérifie que la quantité est exploitable (grammes ou portions concrètes). Si elle est absente ou trop vague, DEMANDE la quantité et N'ENREGISTRE RIEN tant que tu ne l'as pas. Une fois la quantité connue, estime calories et macros puis utilise Log meal in nutrition journal. Dans ta confirmation, indique la quantité retenue comme base de calcul.\n- Confirme tout enregistrement de manière brève et valorisante (processus, pas talent).\n\n# PLANS DE LA SEMAINE\n- Plan de repas demandé : rédige-le et enregistre-le avec Save weekly nutrition plan.\n- Programme d'entraînement demandé : rédige-le et enregistre-le avec Save weekly training plan.\n- Ces plans REMPLACENT le précédent. Après enregistrement, confirme simplement.\n\n# MODIFICATION DU PROFIL (RÈGLE DE SÉCURITÉ STRICTE)\n- N'utilise Update user profile QUE si l'utilisateur demande EXPLICITEMENT de modifier une donnée.\n- Ne modifie QUE le champ demandé. JAMAIS de ta propre initiative.\n\n# MISE EN FORME DES PLANS\n- Texte simple (jamais de HTML ni de Markdown), très aéré : une ligne vide entre sections et jours, titres en MAJUSCULES avec un emoji, un élément par ligne commençant par « • », liste de courses groupée par catégorie.\n\n# MESSAGES VOCAUX\n- Les messages peuvent venir de transcriptions audio : ignore les fautes propres à l'oral, concentre-toi sur l'intention.\n\n# STYLE DE RÉPONSE\n- Réponses claires, aérées, adaptées à Telegram. Termine souvent par une question ouverte.\n\nDate actuelle : {{ $json.current_date }}\nJour de la semaine : {{ $json.weekday }}"
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "70e316f0-c14d-4b1c-a7b5-ad63906b1476",
      "name": "Claude chat model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        1168,
        512
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-6",
          "cachedResultName": "Claude Sonnet 4.6"
        },
        "options": {}
      },
      "typeVersion": 1.5
    },
    {
      "id": "846a6d26-e35a-4c5f-b622-e268b8e1d147",
      "name": "Session memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        1328,
        512
      ],
      "parameters": {
        "sessionKey": "={{ $('Normalize user message').item.json.chat_id }}",
        "sessionIdType": "customKey",
        "contextWindowLength": "={{ $('Configuration').item.json.memory_context_length }}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "581afb6c-7ab5-4a3c-9540-c40b06c734c0",
      "name": "Read user profile",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        1488,
        512
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Normalize user message').item.json.chat_id }}",
              "lookupColumn": "id_telegram"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "profil"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile pour récupérer le profil complet du sportif (poids actuel, objectif, niveau, blessures) à partir de son ID Telegram (chat_id). À consulter avant tout conseil personnalisé."
      },
      "typeVersion": 4.7
    },
    {
      "id": "28b4719b-f505-42e9-acc7-a822332b3797",
      "name": "Update user profile",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        1648,
        512
      ],
      "parameters": {
        "columns": {
          "value": {
            "objectif": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('objectif', 'Nouvel objectif du sportif, uniquement si demandé explicitement') }}",
            "id_telegram": "={{ $('Normalize user message').item.json.chat_id }}",
            "poids_actuel": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('poids_actuel', 'Nouveau poids actuel du sportif, uniquement si demandé explicitement') }}"
          },
          "schema": [
            {
              "id": "id_telegram",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "id_telegram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "poids_actuel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "poids_actuel",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "objectif",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "objectif",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id_telegram"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "profil"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile UNIQUEMENT pour modifier une donnée précise du profil quand l'utilisateur le demande EXPLICITEMENT (ex: change mon poids à 78 kg, mon objectif est maintenant l'endurance). Ne modifier QUE le champ demandé. NE JAMAIS utiliser cet outil de ta propre initiative ni pour compléter des champs manquants."
      },
      "typeVersion": 4.7
    },
    {
      "id": "9b98a973-8916-4eb2-bce0-e9530d41d9cc",
      "name": "Log workout session",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        1824,
        512
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $('Normalize user message').item.json.current_date }}",
            "exercice": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('exercice', 'Nom des exercices réalisés pendant la séance') }}",
            "id_telegram": "={{ $('Normalize user message').item.json.chat_id }}",
            "performance": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('performance', 'Détail des performances: charges, séries, répétitions, durée') }}"
          },
          "schema": [
            {
              "id": "id_telegram",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "id_telegram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "exercice",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "exercice",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "performance",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "performance",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "historique"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile pour enregistrer une séance terminée dans l'historique. À utiliser uniquement quand l'utilisateur annonce une performance réalisée (exercice, charge, répétitions). Ne pas utiliser pour une simple question."
      },
      "typeVersion": 4.7
    },
    {
      "id": "6b325f7e-e0df-429e-abab-99a6ee6f3d6d",
      "name": "Read workout history",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2000,
        512
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Normalize user message').item.json.chat_id }}",
              "lookupColumn": "id_telegram"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "historique"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile pour CONSULTER l'historique des séances passées afin d'analyser la progression (comparer les performances, voir la dernière séance, le volume d'entraînement). NE PAS utiliser pour enregistrer une nouvelle séance, c'est le rôle de Log workout session."
      },
      "typeVersion": 4.7
    },
    {
      "id": "86e4869e-e35c-4ed2-9f27-9cc679dbcace",
      "name": "Log meal in nutrition journal",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2160,
        512
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $('Normalize user message').item.json.current_date }}",
            "repas": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('repas', 'Description du repas consommé avec la quantité retenue') }}",
            "lipides": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('lipides', 'Estimation des lipides en grammes') }}",
            "calories": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('calories', 'Estimation des calories du repas') }}",
            "glucides": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('glucides', 'Estimation des glucides en grammes') }}",
            "proteines": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('proteines', 'Estimation des protéines en grammes') }}",
            "id_telegram": "={{ $('Normalize user message').item.json.chat_id }}"
          },
          "schema": [
            {
              "id": "id_telegram",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "id_telegram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "repas",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "repas",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "calories",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "calories",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "proteines",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "proteines",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "lipides",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lipides",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "glucides",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "glucides",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "nutrition"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile pour enregistrer un repas dans le journal nutrition. À utiliser uniquement quand l'utilisateur annonce ce qu'il a mangé ou bu, avec une quantité exploitable. Estimer calories et macros à partir de sa description. Ne pas utiliser pour une question sur l'entraînement."
      },
      "typeVersion": 4.7
    },
    {
      "id": "365eec2a-676b-4410-b8a5-6747112f6a3d",
      "name": "Read today meals",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2336,
        512
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Normalize user message').item.json.chat_id }}",
              "lookupColumn": "id_telegram"
            },
            {
              "lookupValue": "={{ $('Normalize user message').item.json.current_date }}",
              "lookupColumn": "date"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "nutrition"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile pour lire les repas du jour enregistrés dans le journal nutrition. À appeler quand l'utilisateur demande un bilan ou un total de ce qu'il a mangé (calories, protéines, lipides, glucides). Additionner les lignes pour calculer le total de la journée."
      },
      "typeVersion": 4.7
    },
    {
      "id": "b10ef81e-b209-4408-98ce-1833fb5e21db",
      "name": "Save weekly nutrition plan",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2480,
        512
      ],
      "parameters": {
        "columns": {
          "value": {
            "date_maj": "={{ $('Normalize user message').item.json.current_date }}",
            "plan_repas": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('plan_repas', 'Plan de repas de la semaine en texte clair et structuré') }}",
            "id_telegram": "={{ $('Normalize user message').item.json.chat_id }}",
            "liste_courses": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('liste_courses', 'Liste de courses regroupée par catégorie en texte clair') }}"
          },
          "schema": [
            {
              "id": "id_telegram",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "id_telegram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date_maj",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date_maj",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "plan_repas",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "plan_repas",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "liste_courses",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "liste_courses",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id_telegram"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "plan_nutrition"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile pour créer ou mettre à jour le plan nutritionnel de la semaine et la liste de courses. À utiliser quand l'utilisateur demande un nouveau plan ou une modification. Le nouveau plan REMPLACE le précédent. NE PAS utiliser pour enregistrer un repas mangé, c'est le rôle de Log meal in nutrition journal."
      },
      "typeVersion": 4.7
    },
    {
      "id": "d4f50ca9-5754-4c10-a77c-ef866ea5f797",
      "name": "Save weekly training plan",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2640,
        512
      ],
      "parameters": {
        "columns": {
          "value": {
            "date_maj": "={{ $('Normalize user message').item.json.current_date }}",
            "id_telegram": "={{ $('Normalize user message').item.json.chat_id }}",
            "programme_texte": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('programme_texte', 'Programme de la semaine en texte clair: jours, exercices, séries, répétitions, repos') }}"
          },
          "schema": [
            {
              "id": "id_telegram",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "id_telegram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date_maj",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date_maj",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "programme_texte",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "programme_texte",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id_telegram"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "plan_sport"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Utile pour créer ou mettre à jour le programme d'entraînement de la semaine. À utiliser quand l'utilisateur demande un nouveau programme sportif ou une modification. Le nouveau programme REMPLACE le précédent. NE PAS utiliser pour enregistrer une séance réalisée."
      },
      "typeVersion": 4.7
    },
    {
      "id": "da484017-74cc-41c3-a630-b9a93d62b17c",
      "name": "Read weekly training plan",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        2816,
        512
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Normalize user message').item.json.chat_id }}",
              "lookupColumn": "id_telegram"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "plan_sport"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "AI fitness coach data"
        },
        "descriptionType": "manual",
        "toolDescription": "Permet de lire le programme d'entraînement de la semaine de l'utilisateur. À utiliser quand il demande son programme, ce qu'il doit faire aujourd'hui ou un jour précis. NE PAS utiliser pour modifier le programme, c'est le rôle de Save weekly training plan."
      },
      "typeVersion": 4.7
    },
    {
      "id": "10afcbb4-3e18-492c-9eb2-c66e7a758e44",
      "name": "Create workout event in Calendar",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        2976,
        512
      ],
      "parameters": {
        "end": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('end_time', 'Date et heure de fin au format ISO 8601 AAAA-MM-JJTHH:MM:SS') }}",
        "start": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('start_time', 'Date et heure de début au format ISO 8601 AAAA-MM-JJTHH:MM:SS, exemple 2026-07-15T18:00:00') }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "descriptionType": "manual",
        "toolDescription": "Crée un événement dans l'agenda de l'utilisateur pour programmer une séance de sport. À utiliser quand il demande de planifier une séance. Calculer la date à partir de la date du jour fournie dans le contexte.",
        "additionalFields": {
          "summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('titre', 'Titre de la séance, exemple: Séance jambes') }}"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "b823d32a-9159-484b-bd07-01a71f443d41",
      "name": "Send coach reply on Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2768,
        64
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "chatId": "={{ $('Normalize user message').item.json.chat_id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1b74b581-204c-4d6c-82ee-d225903def0c",
      "name": "Sticky Note b72ab057",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -688,
        -96
      ],
      "parameters": {
        "width": 560,
        "height": 1956,
        "content": "# Coach fitness and nutrition on Telegram with Claude, Google Sheets and Calendar\n\n# 📥  [Open full documentation on Notion](https://automatisation.notion.site/Coach-fitness-and-nutrition-on-Telegram-with-Claude-Google-Sheets-and-Calendar-Course-support-En-3993d6550fd9810c9641ffc68da1d2aa)\n\n## How it works\n1. The workflow triggers when a Telegram message arrives (text or voice note).\n2. Voice notes are downloaded from Telegram and transcribed with OpenAI.\n3. The message, chat ID and current date are normalized into common fields.\n4. An AI coach agent (Anthropic Claude) with session memory answers in French and decides when to call its tools.\n5. Google Sheets tools read and write the user profile, workout history, nutrition journal and weekly plans.\n6. A Google Calendar tool schedules workout sessions on request.\n7. The coach reply is sent back to the user on Telegram.\n\n## Setup\n1. Connect your Telegram bot credential on the trigger, download and reply nodes.\n2. Connect Anthropic (chat model) and OpenAI (voice transcription) credentials.\n3. Connect Google Sheets OAuth2 and Google Calendar OAuth2 credentials.\n4. Create one Google Spreadsheet with 5 tabs: profil (id_telegram, poids_actuel, objectif), historique (id_telegram, date, exercice, performance), nutrition (id_telegram, date, repas, calories, proteines, lipides, glucides), plan_nutrition (id_telegram, date_maj, plan_repas, liste_courses), plan_sport (id_telegram, date_maj, programme_texte).\n5. Select this spreadsheet in every Google Sheets tool node and pick your calendar in the Calendar tool.\n6. Adjust the variables in the Configuration node (language, date format, memory length).\n\n## Requirements\n- A Telegram bot token (create one with @BotFather).\n- An Anthropic API key for the coach model.\n- An OpenAI API key for audio transcription.\n- A Google account with Sheets and Calendar access.\n\n## Customization\n- Edit the system prompt of the AI fitness coach agent to change the coaching style.\n- Change the language, date format or memory window in the Configuration node.\n- Swap the Anthropic model for another chat model, or add more Google Sheets tools (measurements, sleep, hydration).\n- Add a text-to-speech step to reply with voice notes.\n\n## Need help customizing?\nContact me for consulting and support : [Linkedin](https://www.linkedin.com/in/doctor-firass/)\n\n# MY NEW YOUTUBE CHANNEL\n👉 [Subscribe to my new YouTube channel](https://www.youtube.com/@DrFiras_AI). Here I'll share videos and Shorts with practical tutorials and FREE templates for n8n.\n\n[![The AI Doctor](https://www.dr-firas.com/the-ai-doctor.png)](https://www.youtube.com/@DrFiras_AI)"
      },
      "typeVersion": 1
    },
    {
      "id": "83cde8f1-8366-43b8-8013-3935015ceae9",
      "name": "Sticky Note 33437496",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 400,
        "content": "## Receive and route messages\nReceives Telegram messages and checks whether the user sent a voice note or a text message. The Configuration node centralizes every user-adjustable variable: set your language, date format and memory length here."
      },
      "typeVersion": 1
    },
    {
      "id": "45cd71c5-61fc-485a-89b9-523906f9926a",
      "name": "Sticky Note ba9c6b3b",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 816,
        "height": 400,
        "content": "## Transcribe voice notes\nDownloads the Telegram voice note and transcribes it with OpenAI, then structures the transcript or the raw text into common fields (user_message, chat_id, current_date, weekday)."
      },
      "typeVersion": 1
    },
    {
      "id": "ee9be27c-c1f8-47c2-9bd2-f088714742bb",
      "name": "Sticky Note d7a975cd",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1664,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 976,
        "height": 400,
        "content": "## Run the AI coach agent\nThe central Claude agent with session memory interprets the request, applies the coaching rules and calls the right tool: profile, workout history, nutrition journal, weekly plans or calendar."
      },
      "typeVersion": 1
    },
    {
      "id": "4493e4c1-e8b3-4b6c-b661-f4809050078e",
      "name": "Sticky Note e7250f27",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        672
      ],
      "parameters": {
        "color": 7,
        "width": 1984,
        "content": "## Manage fitness records in Google Sheets\nTools used by the agent to read and write the profile, log workouts and meals, compute daily nutrition totals and store the weekly training and nutrition plans. The Calendar tool schedules workout sessions."
      },
      "typeVersion": 1
    },
    {
      "id": "6206bd29-6b78-45c6-b239-145cbc506952",
      "name": "Sticky Note f78a52ac",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2656,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 400,
        "content": "## Send the reply\nSends the coach answer back to the user in the same Telegram chat."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Configuration": {
      "main": [
        [
          {
            "node": "Check for voice message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Session memory": {
      "ai_memory": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Read today meals": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Claude chat model": {
      "ai_languageModel": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Read user profile": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Log workout session": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Update user profile": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Read workout history": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "AI fitness coach agent": {
      "main": [
        [
          {
            "node": "Send coach reply on Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download voice message": {
      "main": [
        [
          {
            "node": "Transcribe voice with OpenAI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize user message": {
      "main": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for voice message": {
      "main": [
        [
          {
            "node": "Download voice message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Normalize user message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Telegram message": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read weekly training plan": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Save weekly training plan": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Save weekly nutrition plan": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe voice with OpenAI": {
      "main": [
        [
          {
            "node": "Normalize user message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log meal in nutrition journal": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Create workout event in Calendar": {
      "ai_tool": [
        [
          {
            "node": "AI fitness coach agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false
}
