WhatsApp appointment booking AI agent

Customers book appointments on WhatsApp in their own dialect — the agent checks your calendar, books the slot and tells your team.

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

Works with n8n · Make · Zapier

How it works

Trigger: WhatsApp — a customer sends a message
  1. A customer asks to book on WhatsApp.
  2. The agent checks your calendar and offers 2–3 free slots within opening hours.
  3. It repeats the day, time and service, then books the appointment.
  4. It alerts your team on Telegram about the new booking.

What it can do

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

  • Books in Egyptian, Gulf or Standard Arabic — or English
  • Offers only real free slots from your calendar
  • Answers questions about your services and prices
  • Saves the customer's phone in the booking, so the WhatsApp reminder flow works automatically
  • Passes cancellations and changes to your team

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 booking assistant for YOUR_BUSINESS (a clinic, salon or studio).

Today is {{ $now.toFormat('cccc d LLLL yyyy, HH:mm') }}. Our timezone is YOUR_TIMEZONE. Opening hours: YOUR_OPENING_HOURS. Each appointment is YOUR_DURATION minutes. Services and prices: YOUR_SERVICES.

Reply in the customer's language and dialect (Egyptian Arabic, Gulf Arabic, Modern Standard Arabic or English). Be warm and brief.

Tools:
- "Check free slots": lists booked appointments in a time range. Use it to find free slots within opening hours, then offer 2–3 options.
- "Book appointment": books a confirmed slot. Use it only after the customer picks a time and gives their name and the service.
- "Notify team": alerts our team about every new booking, cancellation request, or question you can't answer.

Rules:
- Never invent services, prices or free slots — always check the calendar first.
- Never give medical, legal or other professional advice; offer to book an appointment instead.
- Repeat the day, time and service back to the customer before booking.
- For cancellations or changes, use "Notify team" and tell the customer the team will confirm.

Safety rules

Built into the prompt and the tools:

  • Never invents services, prices or free slots
  • Never gives medical or professional advice
  • Confirms day, time and service before booking

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 questionCodeBooking agentAI AgentClaude modelAnthropic Chat Mo…◷MemorySimple MemoryCCheck free slo…Google Calendar T…CBook appointme…Google Calendar T…TNotify 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 (284 lines)
{
  "name": "WhatsApp appointment booking AI agent (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## WhatsApp appointment booking AI agent\nCustomers book appointments on WhatsApp in their own dialect — the agent checks your calendar, books the slot and tells your team.\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": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "updates": [
          "messages"
        ]
      },
      "id": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Incoming message",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "webhookId": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1"
    },
    {
      "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": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "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 booking assistant for YOUR_BUSINESS (a clinic, salon or studio).\n\nToday is {{ $now.toFormat('cccc d LLLL yyyy, HH:mm') }}. Our timezone is YOUR_TIMEZONE. Opening hours: YOUR_OPENING_HOURS. Each appointment is YOUR_DURATION minutes. Services and prices: YOUR_SERVICES.\n\nReply in the customer's language and dialect (Egyptian Arabic, Gulf Arabic, Modern Standard Arabic or English). Be warm and brief.\n\nTools:\n- \"Check free slots\": lists booked appointments in a time range. Use it to find free slots within opening hours, then offer 2–3 options.\n- \"Book appointment\": books a confirmed slot. Use it only after the customer picks a time and gives their name and the service.\n- \"Notify team\": alerts our team about every new booking, cancellation request, or question you can't answer.\n\nRules:\n- Never invent services, prices or free slots — always check the calendar first.\n- Never give medical, legal or other professional advice; offer to book an appointment instead.\n- Repeat the day, time and service back to the customer before booking.\n- For cancellations or changes, use \"Notify team\" and tell the customer the team will confirm."
        }
      },
      "id": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Booking 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": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Claude model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        260,
        220
      ]
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Get question').item.json.from }}",
        "contextWindowLength": 10
      },
      "id": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        430,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Lists booked appointments between two dates, so you can find free slots within opening hours.",
        "operation": "getAll",
        "calendar": {
          "__rl": true,
          "value": "primary",
          "mode": "id"
        },
        "returnAll": true,
        "timeMin": "={{ $fromAI('start', \"Start of the time range, ISO 8601\", 'string') }}",
        "timeMax": "={{ $fromAI('end', \"End of the time range, ISO 8601\", 'string') }}",
        "options": {
          "singleEvents": true,
          "orderBy": "startTime"
        }
      },
      "id": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Check free slots",
      "type": "n8n-nodes-base.googleCalendarTool",
      "typeVersion": 1.3,
      "position": [
        600,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Books a confirmed appointment in the calendar.",
        "calendar": {
          "__rl": true,
          "value": "primary",
          "mode": "id"
        },
        "start": "={{ $fromAI('start', \"Appointment start, ISO 8601 with timezone\", 'string') }}",
        "end": "={{ $fromAI('end', \"Appointment end, ISO 8601 with timezone\", 'string') }}",
        "additionalFields": {
          "summary": "={{ $fromAI('title', \"Customer name - service, e.g. Sara - Haircut\", 'string') }}",
          "description": "=Phone: +{{ $('Get question').item.json.from }}\nBooked via WhatsApp agent"
        }
      },
      "id": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Book appointment",
      "type": "n8n-nodes-base.googleCalendarTool",
      "typeVersion": 1.3,
      "position": [
        770,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Sends a message to our team's Telegram group: new bookings, cancellation requests, or 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": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "name": "Notify team",
      "type": "n8n-nodes-base.telegramTool",
      "typeVersion": 1.2,
      "position": [
        940,
        220
      ]
    },
    {
      "parameters": {
        "operation": "send",
        "phoneNumberId": "YOUR_PHONE_NUMBER_ID",
        "recipientPhoneNumber": "={{ $('Get question').item.json.from }}",
        "textBody": "={{ $json.output }}"
      },
      "id": "3f36c28d-dcaa-4fe7-afb0-dde2cffcb7d1",
      "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": "Booking agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Booking agent": {
      "main": [
        [
          {
            "node": "Reply on WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude model": {
      "ai_languageModel": [
        [
          {
            "node": "Booking agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Memory": {
      "ai_memory": [
        [
          {
            "node": "Booking agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Check free slots": {
      "ai_tool": [
        [
          {
            "node": "Booking agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Book appointment": {
      "ai_tool": [
        [
          {
            "node": "Booking agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Notify team": {
      "ai_tool": [
        [
          {
            "node": "Booking agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • Pair it with the "WhatsApp reminders for tomorrow's appointments" flow to cut no-shows.

Related flows