Building a WhatsApp Sales Bot for Real-Time Deal Notifications with OpenClaw [2026]
Your CRM sends email notifications. You check email twice a day. A hot lead came in at 9am. You saw it at 3pm. They already booked with a competitor.
The notification problem is a channel problem.
Sales leaders check WhatsApp 50+ times per day. They check email 2-3 times. If you want real-time awareness of your pipeline, put alerts where you actually look.
OpenClaw makes this trivially easy. In this guide, I'll show you how to build a WhatsApp bot that:
- Alerts you when high-value leads come in
- Notifies you of deal stage changes
- Sends daily pipeline summaries
- Lets you query your CRM conversationally
All running 24/7. All delivered to the app you already have open.

Why WhatsApp for Sales Notifications
The Engagement Numbers
| Channel | Avg. Time to See | Open Rate | Response Rate |
|---|---|---|---|
| 6-24 hours | 21% | 2% | |
| Slack | 30-60 min | 65% | 15% |
| < 5 min | 98% | 45% | |
| SMS | 3-10 min | 90% | 25% |
WhatsApp wins because:
- It's always open - Most professionals keep it running
- Notifications are prominent - You see them immediately
- It's conversational - You can reply/query naturally
- It's personal - Feels more important than work tools
What Should Come Through WhatsApp
Not everything. Be selective:
High Priority (Immediate WhatsApp):
- New leads over $50K estimated value
- Deal stage changes (especially late stage)
- At-risk deals (no activity 7+ days)
- Competitor mentions detected
- Key account activity (target list)
- Urgent meeting requests
Medium Priority (Daily Summary):
- Pipeline additions
- Forecast changes
- Activity metrics
- Team performance
Low Priority (Keep in CRM/Email):
- Routine activity logging
- System notifications
- Bulk updates
Setting Up OpenClaw for WhatsApp
OpenClaw connects to WhatsApp through its built-in WhatsApp channel. Setup takes about 10 minutes.
Step 1: Configure OpenClaw
# openclaw.yaml
channels:
whatsapp:
enabled: true
# Your personal WhatsApp gets connected via QR code scan
agents:
deal-alerts:
model: claude-sonnet-4-20250514
prompt: |
You are a sales intelligence assistant. Monitor the CRM for important events
and send timely, actionable notifications to the sales team via WhatsApp.
Be concise. Every message should be scannable in 5 seconds.
Include: What happened, why it matters, what to do next.
tools:
- hubspot # or salesforce
- web_search
- memory
Step 2: Link WhatsApp
When you start OpenClaw, it will prompt you to scan a QR code with WhatsApp:
openclaw gateway start
# Scan QR code with WhatsApp when prompted
Once linked, OpenClaw can send and receive WhatsApp messages.
Step 3: Build the Alert System
# deal_alerts.py
import os
from datetime import datetime, timedelta
from hubspot import HubSpot
class DealAlertSystem:
def __init__(self):
self.hubspot = HubSpot(access_token=os.environ['HUBSPOT_TOKEN'])
self.alert_thresholds = {
"high_value_deal": 50000,
"stale_deal_days": 7,
"hot_lead_score": 80
}
def check_new_high_value_deals(self) -> list:
"""Find deals created in last hour over threshold."""
one_hour_ago = datetime.now() - timedelta(hours=1)
deals = self.hubspot.crm.deals.search(
filter_groups=[{
"filters": [
{
"propertyName": "createdate",
"operator": "GTE",
"value": int(one_hour_ago.timestamp() * 1000)
},
{
"propertyName": "amount",
"operator": "GTE",
"value": self.alert_thresholds["high_value_deal"]
}
]
}]
)
return [self.format_deal_alert(d) for d in deals.results]
def check_stage_changes(self) -> list:
"""Find deals that changed stage in last hour."""
# Query deal history for stage changes
# (Implementation depends on your CRM's activity tracking)
pass
def check_stale_deals(self) -> list:
"""Find open deals with no activity in X days."""
stale_date = datetime.now() - timedelta(
days=self.alert_thresholds["stale_deal_days"]
)
deals = self.hubspot.crm.deals.search(
filter_groups=[{
"filters": [
{
"propertyName": "dealstage",
"operator": "NEQ",
"value": "closedwon"
},
{
"propertyName": "dealstage",
"operator": "NEQ",
"value": "closedlost"
},
{
"propertyName": "notes_last_updated",
"operator": "LT",
"value": int(stale_date.timestamp() * 1000)
}
]
}]
)
return [self.format_stale_alert(d) for d in deals.results]
def format_deal_alert(self, deal) -> str:
"""Format a deal into a scannable WhatsApp message."""
amount = f"${deal.properties.get('amount', 0):,.0f}"
company = deal.properties.get('dealname', 'Unknown')
stage = deal.properties.get('dealstage', 'Unknown')
owner = deal.properties.get('hubspot_owner_id', 'Unassigned')
return f"""
🔥 *NEW HIGH-VALUE DEAL*
💰 {amount}
🏢 {company}
📍 Stage: {stage}
👤 Owner: {owner}
→ Check HubSpot: [link]
"""
def format_stale_alert(self, deal) -> str:
"""Format a stale deal warning."""
company = deal.properties.get('dealname', 'Unknown')
days_stale = self.calculate_days_stale(deal)
amount = f"${deal.properties.get('amount', 0):,.0f}"
return f"""
⚠️ *DEAL GOING COLD*
🏢 {company}
💰 {amount}
📅 No activity: {days_stale} days
Action needed: Log an activity or update stage.
"""

Scheduling Alerts with OpenClaw Cron
OpenClaw has built-in cron support. Here's how to schedule different alert types:
# openclaw.yaml - Cron jobs for deal alerts
cron:
jobs:
# Check for hot leads every 15 minutes
- name: "hot-lead-check"
schedule: "*/15 * * * *"
payload:
kind: systemEvent
text: |
Check CRM for new high-value leads (score > 80 or amount > $50K).
If found, send WhatsApp alert with lead details and recommended action.
sessionTarget: main
# Check for stale deals twice daily
- name: "stale-deal-check"
schedule: "0 9,15 * * *" # 9am and 3pm
payload:
kind: systemEvent
text: |
Check for open deals with no activity in 7+ days.
Send WhatsApp summary if any found, grouped by owner.
sessionTarget: main
# Daily pipeline summary
- name: "daily-pipeline-summary"
schedule: "0 8 * * 1-5" # 8am weekdays
payload:
kind: systemEvent
text: |
Generate daily pipeline summary:
- Total pipeline value
- New deals (24h)
- Stage changes
- Deals closing this week
- At-risk deals
Send formatted summary to WhatsApp.
sessionTarget: main
# Weekly forecast update
- name: "weekly-forecast"
schedule: "0 9 * * 1" # Monday 9am
payload:
kind: systemEvent
text: |
Generate weekly forecast report:
- Commit vs. best case
- Week-over-week change
- Key deals to watch
- Recommended focus areas
Send to WhatsApp with action items.
sessionTarget: main
Conversational CRM Queries
The real power is bidirectional. Ask questions, get answers:
Example Conversations
You: "What's my pipeline looking like this quarter?"
Bot:
📊 Q1 Pipeline Summary
Total: $2.4M (68 deals)
├── Qualified: $890K (32 deals)
├── Demo: $720K (18 deals)
├── Proposal: $540K (12 deals)
└── Negotiation: $250K (6 deals)
🎯 vs. Target: $2.4M / $3M (80%)
📈 WoW Change: +$180K (+8%)
Top 3 Deals:
1. Acme Corp - $180K - Proposal
2. TechStart Inc - $120K - Demo
3. Global Systems - $95K - Negotiation
You: "Any updates on the Acme deal?"
Bot:
🏢 Acme Corp | $180K | Proposal Stage
Last Activity: 2 days ago
- Sarah sent revised proposal
- Waiting on procurement review
Next Steps:
- Follow-up scheduled for Thursday
- Need to connect with CFO (budget holder)
⚠️ Risk: Competitor (Warmly) mentioned in last call
You: "Who should I call today?"
Bot:
📞 Priority Calls for Today
1. 🔥 Global Systems - $95K
Negotiation stage, contract review
Contact: Mike Chen (VP Sales)
2. ⚠️ DataFlow Inc - $65K
No activity 5 days, was hot
Contact: Lisa Park (CRO)
3. 📅 TechStart Inc - $120K
Demo follow-up due
Contact: James Wilson (CEO)
Shall I prep call briefs for any of these?
Implementation
# conversational_crm.py
from anthropic import Anthropic
class ConversationalCRM:
def __init__(self):
self.client = Anthropic()
self.hubspot = HubSpot(access_token=os.environ['HUBSPOT_TOKEN'])
def process_query(self, user_message: str) -> str:
"""Process natural language CRM queries."""
# First, understand the intent
intent = self.classify_intent(user_message)
# Fetch relevant data based on intent
if intent['type'] == 'pipeline_summary':
data = self.get_pipeline_data()
elif intent['type'] == 'deal_detail':
data = self.get_deal_detail(intent['deal_name'])
elif intent['type'] == 'priority_tasks':
data = self.get_priority_tasks()
elif intent['type'] == 'forecast':
data = self.get_forecast_data()
else:
data = self.general_crm_query(user_message)
# Format response for WhatsApp
return self.format_whatsapp_response(data, intent)
def classify_intent(self, message: str) -> dict:
"""Use Claude to understand what the user wants."""
response = self.client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=200,
messages=[{
"role": "user",
"content": f"""
Classify this CRM query intent:
"{message}"
Return JSON:
{{
"type": "pipeline_summary|deal_detail|priority_tasks|forecast|activity_log|general",
"deal_name": "if specific deal mentioned",
"time_period": "if time mentioned",
"filters": ["any filters mentioned"]
}}
"""
}]
)
return json.loads(response.content[0].text)
def format_whatsapp_response(self, data: dict, intent: dict) -> str:
"""Format CRM data for WhatsApp (concise, scannable)."""
response = self.client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=500,
messages=[{
"role": "user",
"content": f"""
Format this CRM data for WhatsApp. Keep it:
- Scannable in 5 seconds
- Using emojis for visual hierarchy
- Under 300 words
- Action-oriented
Data: {json.dumps(data)}
User Intent: {json.dumps(intent)}
"""
}]
)
return response.content[0].text
Security Considerations
What to Protect
- Deal values: Don't send exact amounts to shared groups
- Contact info: Keep personal details in CRM, not messages
- Competitive intel: Sensitive mentions stay private
- Access control: Only authorized users get alerts
Implementation
# security_filter.py
class AlertSecurityFilter:
def __init__(self):
self.private_fields = ['contact_phone', 'contact_email', 'notes']
self.sensitive_keywords = ['competitor', 'pricing', 'discount']
def filter_for_channel(self, alert: dict, channel_type: str) -> dict:
"""Filter alert content based on channel sensitivity."""
if channel_type == 'group':
# Remove specific amounts
alert['amount'] = self.round_amount(alert.get('amount', 0))
# Remove private fields
for field in self.private_fields:
alert.pop(field, None)
# Flag if contains sensitive info
if self.contains_sensitive(alert):
return self.create_private_redirect(alert)
return alert
def round_amount(self, amount: float) -> str:
"""Round amounts for public channels."""
if amount >= 100000:
return f"${int(amount/100000)}00K+"
elif amount >= 10000:
return f"${int(amount/10000)}0K+"
else:
return "< $10K"
def contains_sensitive(self, alert: dict) -> bool:
"""Check if alert contains sensitive content."""
text = json.dumps(alert).lower()
return any(kw in text for kw in self.sensitive_keywords)
def create_private_redirect(self, alert: dict) -> dict:
"""Create a redacted alert that points to private channel."""
return {
"type": "private_redirect",
"message": f"🔒 Sensitive update on {alert.get('deal_name', 'a deal')}. Check DM for details."
}
Results You Can Expect
Teams using WhatsApp-based deal alerts report:
- Response time to hot leads: Down from 4 hours to 8 minutes
- Stale deal intervention: Up 3x (catches problems faster)
- Pipeline accuracy: Up 40% (reps update more when it's easy)
- Manager awareness: "I know what's happening without asking"
The ROI isn't complicated: faster response = more deals closed.
Getting Started
Prerequisites
- OpenClaw installed and running
- HubSpot or Salesforce API access
- WhatsApp on your phone
Quick Start
- Add WhatsApp to OpenClaw config:
channels:
whatsapp:
enabled: true
- Scan QR code when prompted:
openclaw gateway start
- Add your first cron alert:
cron:
jobs:
- name: "hot-lead-alert"
schedule: "*/15 * * * *"
payload:
kind: systemEvent
text: "Check for new leads over $50K. Alert me on WhatsApp if found."
sessionTarget: main
-
Test it: Create a test deal in your CRM over the threshold. Wait 15 minutes. Get the alert.
-
Iterate: Add more alerts, tune thresholds, build conversational queries.
Want this without building it yourself? MarketBetter includes real-time deal alerts + AI-powered playbooks →
