Automates WhatsApp conversations for local businesses — handling replies, booking inquiries, lead capture, and intelligent follow-ups without requiring 24/7 human attention. Designed for gyms, restaurants, salons, clinics, and service-based businesses in South Africa.
RequirementVersion/DetailOpenClawv2.4+Node.jsv18+WhatsApp Business API (Meta)Approved business accountMeta Developer AppWith WhatsApp product configuredPython 3v3.10+ (for NLP pipeline)wacli CLIInstalled on hostngrok or static IPFor webhook callback
bash
cp -r streams/01_ClawHub_Skills/01_WhatsApp_Business_AI_Assistant/* ~/.openclaw/skills/
bash
cd ~/.openclaw/skills/whatsapp-business-ai
npm install
pip install -r requirements.txt
Create ~/.openclaw/skills/whatsapp-business-ai/.env:
env
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
WHATSAPP_ACCESS_TOKEN=your_permanent_token
WHATSAPP_VERIFY_TOKEN=your_webhook_verify_token
WHATSAPP_BUSINESS_ACCOUNT_ID=your_business_account_id
BUSINESS_HOURS_TIMEZONE=Africa/Johannesburg
DEFAULT_LANGUAGE=en
NLP_MODEL_PATH=./models/za_intent_classifier
LEAD_DB_PATH=./data/leads.db
BOOKING_CALENDAR_ID=your_google_calendar_id
bash
# Verify wacli is available
which wacli || brew install wacli
wacli --configure
bash
# Start webhook receiver (uses ngrok for dev)
./scripts/start-webhook.sh
# Register webhook with Meta
curl -X POST "https://graph.facebook.com/v18.0/$WHATSAPP_PHONE_NUMBER_ID/subscriptions" \
-H "Authorization: Bearer $WHATSAPP_ACCESS_TOKEN" \
-d "object=whatsapp_business_account&callback_url=https://your-ngrok-url.ngrok.io/webhook&verify_token=$WHATSAPP_VERIFY_TOKEN"
bash
# Production mode
openclaw skill run whatsapp-business-ai
# Development / dry-run
openclaw skill run whatsapp-business-ai --dry-run
The assistant runs as a persistent OpenClaw process that:
text
Incoming Message → Intent Classifier → Response Generator → Send Reply
↓
Booking Detected?
├── Yes → Check Calendar → Confirm Slot → Add Booking
└── No → Lead? → Log Lead → Schedule Follow-up
CommandDescription/statusShow assistant health, queue depth, message count today/leadsExport leads as CSV or JSON/analyticsLast 24h: sent/replied/followed-up counts/blacklist <phone>Block a number/broadcastSend bulk message to all leads (ask first)/pauseStop accepting new messages (resume with /resume)
Auto-reply templates are in ./templates/. Customise per business type:
text
templates/
├── auto_replies/
│ ├── booking_gym.yaml
│ ├── booking_restaurant.yaml
│ ├── booking_salon.yaml
│ ├── greeting_day.yaml
│ ├── greeting_night.yaml
│ ├── hours_inquiry.yaml
│ ├── pricing_inquiry.yaml
│ ├── complaint_acknowledgement.yaml
│ └── out_of_hours.yaml
├── follow_ups/
│ ├── no_reply_24h.yaml
│ ├── abandoned_booking.yaml
│ ├── post_service_feedback.yaml
│ └── promotion_blast.yaml
└── intents.yaml
Example: booking_gym.yaml
yaml
name: gym_booking
trigger:
intents: [booking, tour_inquiry, membership_inquiry]
confidence_threshold: 0.75
response:
template: |
Hi {{customer_name}}! 👋
Thanks for reaching out to {{business_name}}.
I can help you book:
🏋️ A free trial session
📋 A membership consultation
🎯 A personal training intro
What time works for you? Our hours are:
Mon–Fri: 5:00 AM – 9:00 PM
Sat: 6:00 AM – 6:00 PM
Sun: 7:00 AM – 2:00 PM
Just tell me your preferred day and time!
buttons:
- "Book Trial"
- "See Pricing"
- "Ask a Human"
Edit ./config/prompts.yaml:
yaml
classifier_prompt: |
Classify the incoming WhatsApp message into exactly one intent.
Intents: [booking, pricing_inquiry, hours, complaint, lead, general, spam]
Context: {business_type} in {location}
Message: {text}
Respond with only the intent name and confidence score.
response_prompt: |
You are {assistant_name}, the friendly AI assistant for {business_name}.
Location: {location}
Business type: {business_type}
Business hours: {hours}
Customer message: {text}
Detected intent: {intent}
Reply in a warm, professional tone. Be concise. If it's a booking,
ask for time and contact info. Never give away pricing you're unsure of.
Use South African English.
"Hey Marvis, can you check on the gym assistant? How many unqualified leads are waiting?" "Set up a new WhatsApp assistant for 'Jozi Cuts Barbershop' in Randburg." "What's the reply rate on the restaurant assistant this week?" "Add a 'Student Discount' promotion template to the gym assistant's broadcast queue."
text
whatsapp-business-ai/
├── SKILL.md
├── README.md
├── config/
│ ├── business.yaml # Business profile (name, hours, location, type)
│ ├── prompts.yaml # LLM prompts for classification & reply
│ └── webhooks.yaml # Webhook routing rules
├── templates/
│ ├── auto_replies/ # Pre-written reply templates
│ ├── follow_ups/ # Scheduled follow-up templates
│ └── intents.yaml # Intent definitions & training data
├── scripts/
│ ├── start-webhook.sh # Start webhook listener with ngrok
│ ├── register-webhook.sh # Register with Meta APIs
│ └── export-leads.py # Export leads to CSV
├── workflows/
│ ├── booking.yaml # Booking: detect → confirm → create
│ ├── lead.yaml # Lead: capture → enrich → follow-up
│ └── complaint.yaml # Complaint: acknowledge → escalate → resolve
├── data/
│ ├── leads.db # SQLite lead database
│ └── conversation_logs/ # Raw conversation transcripts (rotated)
├── requirements.txt
└── package.json
SymptomLikely CauseFixAssistant not replyingWebhook not registeredRun register-webhook.shReplies go to wrong chatWrong phone_number_id in envCheck .env values"Message not allowed"Sender hasn't opted inSend template message firstLow confidence on intentTrained on wrong business typeUpdate config/business.yamlCalendar sync failsMissing Google Calendar scopeRe-auth with https://www.googleapis.com/auth/calendar scopeLeads not savingSQLite path not writablechmod 755 ./data/ngrok URL changedFree ngrok rotates on restartUse paid plan or static IP