Build WhatsApp Order Management with OpenClaw & Groq
Build a WhatsApp order management system using OpenClaw as a webhook-driven backend. Complete tutorial with SKILL.md, Supabase RLS, Groq integration, and p
Originally published:
What You'll Learn
This tutorial guides you through building DukanBot, a production-grade WhatsApp order management system for small retailers using OpenClaw as a triggered messaging engine. You'll learn how to architect OpenClaw as a backend microservice (not a chatbot), design culturally-specific AI prompts in SKILL.md, integrate webhook-driven messaging flows, secure multi-tenant data with Supabase Row Level Security, and deploy a real-world SaaS application for 12 million Indian kirana stores.
Prerequisites
- Node.js 18+ and npm installed locally
- OpenClaw installed globally (
npm install -g openclaw@latest) - Supabase account (free tier sufficient) with PostgreSQL database initialized
- WhatsApp Business Account with API credentials and webhook URL capability
- Groq API key (free tier; request at console.groq.com)
- Git and basic command-line familiarity
- Basic understanding of: REST APIs, webhook patterns, authentication tokens, database schema design
- Frontend framework knowledge (React/Vue/Svelte — the dashboard uses your preferred stack)
Step 1: Understanding the Architecture — Why OpenClaw as a Microservice?
Most OpenClaw deployments treat it as a conversational chatbot: user talks → AI responds → conversation continues. DukanBot inverts this pattern entirely. The store owner's dashboard is the UI; OpenClaw runs silently as a triggered execution layer in the background.
The flow: Dashboard → Webhook POST → OpenClaw → Groq LLaMA 3.3 70B → WhatsApp → Customer. This separation of concerns means:
- Dashboard developers don't touch OpenClaw or WhatsApp API complexity
- OpenClaw skill authors don't need Supabase schema knowledge
- Each layer scales independently
- Testing is isolated (mock the webhook, test the skill independently)
Why Groq specifically? Latency. When a store owner clicks "Send Confirmation," they expect instant visual feedback. Groq's LPU hardware returns ~180ms responses. Claude Haiku averages 800ms; GPT-4o-mini ~600ms. For a 2-3 sentence WhatsApp message, the speed difference is the difference between "feels instant" and "feels broken."
Step 2: Setting Up OpenClaw Locally
Install OpenClaw globally and initialize DukanBot's configuration:
npm install -g openclaw@latest
openclaw --version # Verify installation
mkdir dukanbot-openclaw && cd dukanbot-openclaw
openclaw init
The init command prompts you to:
- Select model: Choose Groq LLaMA 3.3 70B
- Configure Groq API key: Paste your key from console.groq.com
- Set webhook port: Default is 18789 (note this; you'll POST to localhost:18789/webhook)
- Download/create SKILL.md: This is where DukanBot's behavior lives
OpenClaw generates an openclaw.json file with your configuration. Examine it:
Critical: Keep this file out of version control. Add to .gitignore: openclaw.json, .env.local, any file containing API keys.
Start the OpenClaw daemon:
openclaw start
You should see: OpenClaw webhook server listening on localhost:18789. Leave this running in a separate terminal.
Step 3: Writing the SKILL.md — The Behavioral Engine
SKILL.md is where OpenClaw learns what DukanBot should do. This is pure Markdown, not code. The model reads it and uses it to guide message generation.
Create SKILL.md in your OpenClaw directory with this structure:
---
name: dukanbot
description: Kirana store WhatsApp assistant. Sends order confirmations and payment reminders. Handles customer replies automatically. Powered by Groq LLaMA 3.3 70B Versatile.
---
# DukanBot — Kirana Store WhatsApp AI
You are DukanBot, a WhatsApp assistant for Indian kirana stores.
You run on Groq's ultra-fast LLaMA 3.3 70B model via OpenClaw.
## When webhook type is "confirmation":
Send WhatsApp to the "to" number using the "message" field.
Add a warm closing: "Aapka business humara garv hai 🙏"
Log: CONFIRMATION sent to [customer_name] for [store_name]
## When webhook type is "reminder":
Send WhatsApp to the "to" number using the "message" field.
Keep tone polite but clear — small business relationships matter.
Add: "Koi problem ho toh batayein — hum help karenge 🙏"
Log: REMINDER sent to [customer_name] for [store_name]
## When customers reply on WhatsApp:
- "paid" / "done" / "ho gaya" → "Shukriya! Payment received ✅🙏"
- "when" / "kab" / "ready" → "Aapka order prepare ho raha hai. Notification milegi jaldi!"
- "cancel" / "nahi chahiye" → Forward to store owner: "⚠️ Customer wants to cancel. Please respond."
- anything else → Summarize and forward to store owner
## Tone Rules:
- Always Hinglish (mix Hindi and English naturally)
- Never rude, never rushed — ye relationship business hai
- Use 🙏 for greetings and thanks
- Keep messages under 3 sentences
- Always include store name
## What you are:
- Framework: OpenClaw (open-source personal AI)
- Model: Groq LLaMA 3.3 70B Versatile (~200ms response)
- Channel: WhatsApp
- Purpose: Automate the boring parts so store owners focus on people
Why Hinglish? Indian kirana store customers communicate in Hinglish, not formal English. "Aapka business humara garv hai" (Your business is our pride) generates measurably warmer customer responses than "Thank you for your patronage." Specificity to a real culture isn't localization overhead — it's product quality.
The instructions must be clear enough that a language model can execute them reliably. Test by manually sending webhook payloads (Step 4) and observe the responses. Refine the SKILL.md language if outputs don't match your intent.
Step 4: Building the Webhook Integration
Your dashboard (frontend) needs to POST JSON to OpenClaw's webhook endpoint. Here's what the payload structure looks like:
For order confirmation:
POST http://localhost:18789/webhook
Content-Type: application/json
For payment reminder:
POST http://localhost:18789/webhook
Content-Type: application/json
From your dashboard backend (Node.js example):
async function sendConfirmation(orderId, customerPhone, storeName) {
const payload = {
type: "confirmation",
to: customerPhone,
message: `Order ${orderId} from ${storeName} is confirmed 🙏`,
customer_name: customerPhone.slice(-10),
store_name: storeName,
order_id: orderId
};
const response = await fetch("http://localhost:18789/webhook", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
return response.json(); // Returns { status: "sent", logged: true }
}
Test this immediately: Use curl or Postman to send a test webhook. Verify that OpenClaw receives it, logs it, and (if WhatsApp is configured) sends the message. Watch the OpenClaw daemon logs for errors.
Step 5: Securing Multi-Tenant Data with Supabase RLS
If DukanBot serves multiple store owners, Row Level Security (RLS) is not optional. Without it, every store owner sees every other store's orders.
Create two tables in Supabase:
orders table:
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id),
store_name TEXT NOT NULL,
customer_phone TEXT NOT NULL,
order_details JSONB,
amount_due INT,
status TEXT CHECK (status IN ('pending', 'confirmed', 'paid', 'cancelled')),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can see their own orders"
ON orders FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can update their own orders"
ON orders FOR UPDATE
USING (auth.uid() = user_id);
Why this matters: The policy USING (auth.uid() = user_id) ensures that when Supabase queries orders, it automatically filters to only rows where the authenticated user's ID matches the user_id column. No additional WHERE clause needed in your application code. At the database level, one user literally cannot query another user's data.
Test this:
- Sign in as store owner A, insert an order
- Sign in as store owner B, query orders — should return zero rows (not A's order)
- Sign in as A again, query orders — should see A's order
Step 6: Building the Dashboard Frontend
Your dashboard needs:
- Orders list: Fetch from Supabase (RLS automatically filters)
- Send Confirmation button: POST to OpenClaw webhook, show success/error toast
- Send Reminder button: POST to OpenClaw webhook with reminder template
- OpenClaw connection status: Check if webhook URL is reachable (health endpoint)
- Settings tab: Webhook URL configuration, Groq API key validation
Health check endpoint: Add to your dashboard backend:
app.get("/api/openclaw-status", async (req, res) => {
try {
const response = await fetch("http://localhost:18789/health");
if (response.ok) {
return res.json({ status: "connected", latency: response.headers.get("x-response-time") });
}
} catch (err) {
return res.json({ status: "disconnected", error: err.message });
}
});
In your frontend, call this every 10 seconds to show a live connection indicator (green dot = connected, red dot = disconnected). If disconnected, show a banner: "OpenClaw not connected. Go to Settings to configure webhook URL."
Step 7: Testing the Complete Flow
Scenario: Store owner Sharma sends an order confirmation
- Sharma opens DukanBot dashboard, sees a pending order from customer Rahul
- Clicks "Send Confirmation" button
- Dashboard POSTs to
localhost:18789/webhookwith order JSON - OpenClaw receives webhook, queries SKILL.md instructions
- Groq LLaMA 3.3 70B generates a Hinglish message with warm tone
- OpenClaw sends WhatsApp via Twilio to +919876543210
- Rahul receives: "Hello Rahul! Your order DKN-023 from Sharma Kirana worth ₹340 is confirmed 🙏 Aapka business humara garv hai 🙏"
- Dashboard shows green toast: "✓ Sent via OpenClaw"
- Order status updates to "confirmed" in Supabase
Testing checklist:
- ☐ OpenClaw daemon is running
- ☐ Groq API key is valid (test in OpenClaw dashboard)
- ☐ WhatsApp Business Account is connected to Twilio
- ☐ Supabase RLS policies are active
- ☐ Dashboard backend can POST to localhost:18789/webhook
- ☐ Customer phone numbers are in E.164 format (+91 prefix for India)
- ☐ Messages under 3 sentences (WhatsApp best practice)
Troubleshooting
Webhook receives 404 or connection refused
OpenClaw daemon isn't running. Return to the terminal where you ran openclaw start. You should see listening on localhost:18789. If not, check that port 18789 is not blocked by a firewall.
Groq API returns "rate limit exceeded"
You've exceeded the free tier quota (typically 30 requests/minute). This won't happen in development unless you're load testing. In production, upgrade to Groq's paid tier or implement request queuing on your dashboard backend.
WhatsApp message not delivered
Check in order:
- Is the phone number in E.164 format? (+919876543210, not 9876543210)
- Is the Twilio account provisioned for the customer's country?
- Did the customer opt-in to receive WhatsApp messages from your Twilio number?
- Check Twilio logs for bounce/rejection reasons
Supabase queries return empty, but data exists
RLS is blocking the query. Verify:
- User is authenticated (check
auth.uid()is not NULL) - The
user_idcolumn in the row matches the authenticated user's ID - RLS policy uses correct syntax:
USING (auth.uid() = user_id)
Debug by temporarily disabling RLS (danger: all data visible), inserting a test order, re-enabling RLS, then checking if the order is readable.
Dashboard shows "OpenClaw disconnected" even though daemon is running
Check that your dashboard backend can actually reach localhost:18789. If your dashboard and OpenClaw are on different machines (not localhost), update the webhook URL in settings. Verify firewall rules allow traffic on port 18789.
Best Practices
SKILL.md Maintenance
Keep SKILL.md single-purpose and under 500 lines. If it grows, you're adding too much behavior. Split into separate skills (e.g., dukanbot-orders.md, dukanbot-payments.md) and route webhooks accordingly.
Webhook Payload Design
Include all context the model needs: store name, customer name, order ID, amount. Never make the model infer context from missing data. This reduces hallucinations and makes logs auditable.
Error Handling
Every webhook should be idempotent. If OpenClaw receives the same payload twice (network retry), it should produce the same output without duplicate messages. Include an idempotency_key UUID in your payload and log it alongside the message.
Response Latency Monitoring
Log the time between webhook POST and message delivery. Track p50, p95, p99 latencies weekly. If Groq responses degrade, switch to a fallback model (Claude Haiku) or queue the request for async processing.
Tone Testing
Before shipping tone changes, test with real store owners. "Aapka business humara garv hai" resonated with Indian retailers. "Thank you for your patronage" didn't. Culture-specific language is a feature, not localization overhead.
Scaling Considerations
For 12 million stores:
- Don't run OpenClaw on the same server as your dashboard. Use a separate inference service.
- Implement request queuing (Redis + Bull) for reminders sent at scale ("Send All" button).
- Cache SKILL.md compilation if using multiple OpenClaw instances.
- Use Supabase connection pooling (PgBouncer) to prevent database exhaustion.
Next Steps
Phase 1 (Week 1): Get a single store owner using DukanBot locally. Gather feedback on message tone and button placement.
Phase 2 (Week 2-3): Deploy to production (Vercel for dashboard, Railway/Render for OpenClaw backend, Supabase managed DB). Test with 5-10 real store owners.
Phase 3 (Week 4+): Add features based on real usage: inventory sync, payment link generation, customer analytics, bulk reminder scheduling.
Join the openclaw-community to share what you build and learn from other builders.
Summary
DukanBot demonstrates that OpenClaw's true power isn't in replacing chatbots — it's in automating backend workflows. By treating OpenClaw as a webhook-driven microservice (not a conversational UI), you unlock architectural flexibility, testability, and the ability to serve millions of users with minimal infrastructure.
Key takeaways:
- Architecture matters: Dashboard as UI, OpenClaw as execution layer, Supabase as data layer — clear separation enables independent scaling.
- SKILL.md is powerful: Write clear, specific instructions in Markdown. The model reliably executes them without code.
- Latency is UX: Groq's 180ms response times feel instant. This matters for mobile users and store owners on slow connections.
- Security is not optional: Multi-tenant apps must implement RLS from day one, not as an afterthought.
- Cultural specificity is a feature: Hinglish tone and ₹ formatting aren't localization — they're product quality for your actual users.
Original source: Dev.to article by Simran Shaikh (April 24, 2026). Demo: dukan-bot.netlify.app | GitHub: github.com/SimranShaikh/dukanbot
Original Source
https://dev.to/simranshaikh20_50/dukanbot-i-flipped-openclaw-inside-out-to-run-whatsapp-for-12-million-kirana-stores-3956
Last updated: