Gym membership AI agent on WhatsApp

Answers questions about classes and plans, books free trial classes, and logs freeze or cancellation requests.

AI agentAdvanced Saves ~18 hours/month AI AgentsBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: WhatsApp — a member or visitor sends a message
  1. Someone asks about classes, prices or their membership.
  2. The agent checks your timetable and plans, or their own membership record.
  3. It books trial classes for new visitors.
  4. It logs freezes and cancellations for the front desk to confirm.

What it can do

Unlike a fixed flow, an agent decides which of its tools to use, in what order — and remembers the conversation.

  • Answers about classes, coaches, plans and opening hours
  • Tells members when their membership ends
  • Books free trial classes
  • Logs freeze, cancel and personal-training requests

System prompt

The instructions that shape the agent. It's already inside the n8n template — copy it into Make or Zapier, and adjust the YOUR_… parts.

System prompt
You are the WhatsApp assistant for YOUR_GYM. Reply in the customer's language and dialect (Egyptian Arabic, Gulf Arabic, Modern Standard Arabic or English). Be warm and brief.

Today is {{ $now.toFormat('cccc d LLLL yyyy') }}. The member's phone is {{ $('Get question').item.json.from }}.

Tools:
- "Classes & plans": our class timetable, coaches, membership plans, prices and opening hours. Always check it before answering.
- "My membership": this person's membership (plan, start, end, status, freezes used). It only returns the person who is messaging.
- "Book trial": books a free trial class for a new visitor. Use only after they confirm the class, date and their name.
- "Log request": logs a freeze, cancellation, plan change or personal-training request for the front desk.
- "Notify team": alerts our front desk about bookings, requests, complaints or anything you can't answer.

Rules:
- Never invent prices, offers, classes or coaches.
- Never give medical, injury or diet advice. Suggest they speak to a coach or a doctor.
- Never confirm a freeze or cancellation yourself; log it and say the front desk will confirm.

Safety rules

Built into the prompt and the tools:

  • Never gives medical, injury or diet advice
  • Only shows a member their own membership
  • Never confirms freezes or cancellations itself

Uses your own AI key (Claude, OpenAI or another model). Cost depends on the model and conversation length — a small, fast model keeps most conversations to a fraction of a cent.

Diagram

WIncoming messageWhatsApp Trigger{ }Get questionCodeGym agentAI AgentClaude modelAnthropic Chat Mo…◷MemorySimple MemoryGClasses & plansGoogle Sheets ToolGMy membershipGoogle Sheets ToolGBook trialGoogle Sheets ToolGLog requestGoogle Sheets ToolTNotify teamTelegram ToolWReply on WhatsAppWhatsApp Business Cloud
Trigger App Code AI Agent tool

Code

JavaScript used in the Code step — already included in the template below.

Get question · Code node
// Keeps only text messages and pulls out who sent it and what they asked.
const msg = $input.first().json.messages?.[0];
if (!msg || msg.type !== "text") return [];

return [{ json: { from: msg.from, question: msg.text.body } }];

Build it

Pick your tool — same automation, step by step.

Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder. AI steps use a Claude model node — swap it for OpenAI, Gemini or Ollama if you prefer.

Workflow JSON
Show code (386 lines)
{
  "name": "Gym membership AI agent on WhatsApp (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## Gym membership AI agent on WhatsApp\nAnswers questions about classes and plans, books free trial classes, and logs freeze or cancellation requests.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
        "height": 260,
        "width": 340
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "updates": [
          "messages"
        ]
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Incoming message",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "webhookId": "26069534-1e41-4be3-a6d6-09f4ff85e084"
    },
    {
      "parameters": {
        "jsCode": "// Keeps only text messages and pulls out who sent it and what they asked.\nconst msg = $input.first().json.messages?.[0];\nif (!msg || msg.type !== \"text\") return [];\n\nreturn [{ json: { from: msg.from, question: msg.text.body } }];"
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Get question",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        300,
        0
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.question }}",
        "options": {
          "systemMessage": "=You are the WhatsApp assistant for YOUR_GYM. Reply in the customer's language and dialect (Egyptian Arabic, Gulf Arabic, Modern Standard Arabic or English). Be warm and brief.\n\nToday is {{ $now.toFormat('cccc d LLLL yyyy') }}. The member's phone is {{ $('Get question').item.json.from }}.\n\nTools:\n- \"Classes & plans\": our class timetable, coaches, membership plans, prices and opening hours. Always check it before answering.\n- \"My membership\": this person's membership (plan, start, end, status, freezes used). It only returns the person who is messaging.\n- \"Book trial\": books a free trial class for a new visitor. Use only after they confirm the class, date and their name.\n- \"Log request\": logs a freeze, cancellation, plan change or personal-training request for the front desk.\n- \"Notify team\": alerts our front desk about bookings, requests, complaints or anything you can't answer.\n\nRules:\n- Never invent prices, offers, classes or coaches.\n- Never give medical, injury or diet advice. Suggest they speak to a coach or a doctor.\n- Never confirm a freeze or cancellation yourself; log it and say the front desk will confirm."
        }
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Gym agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        600,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5",
          "mode": "list"
        },
        "options": {}
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Claude model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        90,
        220
      ]
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Get question').item.json.from }}",
        "contextWindowLength": 10
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        260,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Returns the class timetable, coaches, membership plans, prices and opening hours.",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Classes",
          "mode": "name"
        },
        "options": {}
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Classes & plans",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        430,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Returns the membership of the person who is messaging.",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Members",
          "mode": "name"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "phone",
              "lookupValue": "={{ $('Get question').item.json.from }}"
            }
          ]
        },
        "options": {}
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "My membership",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        600,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Books a free trial class for a new visitor.",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Trials",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "booked_at": "={{ $now.toISO() }}",
            "phone": "={{ $('Get question').item.json.from }}",
            "name": "={{ $fromAI('name', \"Visitor's name\", 'string') }}",
            "class": "={{ $fromAI('class', \"Class name\", 'string') }}",
            "date": "={{ $fromAI('date', \"Class date and time\", 'string') }}"
          }
        },
        "options": {}
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Book trial",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        770,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Logs a freeze, cancellation, plan change or personal-training request.",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Requests",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "date": "={{ $now.toISO() }}",
            "phone": "={{ $('Get question').item.json.from }}",
            "type": "={{ $fromAI('type', \"freeze, cancel, change plan or personal training\", 'string') }}",
            "details": "={{ $fromAI('details', \"Dates and details the member gave\", 'string') }}",
            "status": "pending"
          }
        },
        "options": {}
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Log request",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        940,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Alerts the front desk about bookings, requests, complaints and questions you can't answer.",
        "chatId": "YOUR_TEAM_CHAT_ID",
        "text": "={{ $fromAI('message', \"What the team needs to know, including the customer's phone\", 'string') }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Notify team",
      "type": "n8n-nodes-base.telegramTool",
      "typeVersion": 1.2,
      "position": [
        1110,
        220
      ]
    },
    {
      "parameters": {
        "operation": "send",
        "phoneNumberId": "YOUR_PHONE_NUMBER_ID",
        "recipientPhoneNumber": "={{ $('Get question').item.json.from }}",
        "textBody": "={{ $json.output }}"
      },
      "id": "26069534-1e41-4be3-a6d6-09f4ff85e084",
      "name": "Reply on WhatsApp",
      "type": "n8n-nodes-base.whatsApp",
      "typeVersion": 1,
      "position": [
        900,
        0
      ]
    }
  ],
  "connections": {
    "Incoming message": {
      "main": [
        [
          {
            "node": "Get question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get question": {
      "main": [
        [
          {
            "node": "Gym agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gym agent": {
      "main": [
        [
          {
            "node": "Reply on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude model": {
      "ai_languageModel": [
        [
          {
            "node": "Gym agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Memory": {
      "ai_memory": [
        [
          {
            "node": "Gym agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Classes & plans": {
      "ai_tool": [
        [
          {
            "node": "Gym agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "My membership": {
      "ai_tool": [
        [
          {
            "node": "Gym agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Book trial": {
      "ai_tool": [
        [
          {
            "node": "Gym agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Log request": {
      "ai_tool": [
        [
          {
            "node": "Gym agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Notify team": {
      "ai_tool": [
        [
          {
            "node": "Gym agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • Update the Classes sheet whenever a coach or time changes; the agent trusts it completely.

Related flows