Install
openclaw skills install zeptoOrder groceries from Zepto in seconds. Just say what you need, get a payment link on WhatsApp, pay on your phone, done. Remembers your usual items. Works across India where Zepto delivers.
openclaw skills install zeptoOrder groceries from Zepto in 30 seconds. From chat to checkout.
Tell your AI what you need. It shops, generates a payment link, sends it to WhatsApp. You pay on your phone. Groceries arrive in 10 minutes.
Quick orders:
"Order milk and bread from Zepto"
"Add vegetables - tomatoes, onions, potatoes"
"Get me Amul butter and cheese"
Your usuals:
"Add my usual milk" → AI picks the brand you always order
"Order the usual groceries" → AI suggests your frequent items
Full shopping list:
"Add milk, bread, eggs, coriander, ginger, and tea bags"
→ AI adds everything, shows total: ₹X
→ Sends payment link to WhatsApp
→ You pay, groceries arrive
What this skill does:
~/.openclaw/skills/zepto/order-history.json (local file, not shared)What this skill does NOT do:
Data Storage:
~/.openclaw/skills/zepto/order-history.json (local only, helps with "usuals" feature)User Control:
ALWAYS follow this order when building an order:
# Before adding ANY items, ALWAYS check cart state
node zepto-agent.js get-cart
Why: Cart may have items from previous sessions. Adding duplicates is wasteful.
# This handles everything: clears unwanted, checks duplicates, adds missing
node zepto-agent.js smart-shop "milk, bread, eggs"
What it does:
{ added: [], skipped: [], failed: [] }When you see in snapshot:
"Decrease quantity 1 Increase quantity" → Item is IN CART
button "Remove" [ref=eXX] → Item is IN CART
DO NOT click "ADD" when you see these signals!
Your order history is tracked in: {SKILL_DIR}/order-history.json
(Where {SKILL_DIR} is your skill directory, typically ~/.openclaw/skills/zepto/)
Smart Selection Logic:
order-history.json for that categoryWhen to run: User says "update my zepto history" or "refresh order history"
Process:
order-history.jsonImplementation:
# Step 1: Navigate to account page
browser navigate url=https://www.zepto.com/account profile=openclaw
# Step 2: Extract order URLs
browser act profile=openclaw request='{"fn":"() => { const orders = []; document.querySelectorAll(\"a[href*=\\\"/order/\\\"]\").forEach(link => { if (link.href.includes(\"isArchived=false\") && link.textContent.includes(\"delivered\")) { orders.push(link.href); } }); return [...new Set(orders)]; }", "kind":"evaluate"}'
# Returns array of order URLs
# Step 3: For each order URL:
browser navigate url={order_url} profile=openclaw
# Step 4: Extract items from order page
browser act profile=openclaw request='{"fn":"() => { const items = []; document.querySelectorAll(\"*\").forEach(el => { const text = el.textContent; if (text.match(/\\d+\\s*unit/i)) { const parent = el.closest(\"div\"); if (parent) { const lines = parent.textContent.split(\"\\n\").map(l => l.trim()).filter(l => l && l.length > 5 && l.length < 100); if (lines[0]) { const qtyMatch = text.match(/(\\d+)\\s*unit/i); items.push({ name: lines[0], quantity: qtyMatch ? parseInt(qtyMatch[1]) : 1 }); } } } }); const uniqueItems = {}; items.forEach(item => { if (!uniqueItems[item.name]) uniqueItems[item.name] = item; }); return Object.values(uniqueItems); }", "kind":"evaluate"}'
# Returns array of {name, quantity}
# Step 5: Aggregate all items into frequency map
# Build JSON structure with counts
# Step 6: Write to file
write path={SKILL_DIR}/order-history.json content={json_data}
Automated scraper advantages:
Example:
User: "Update my Zepto order history"
Response:
"🔍 Scanning your Zepto orders...
📦 Found 6 delivered orders
🔄 Extracting items...
✅ Updated! Found:
- Coriander: 4 orders
- Milk: 3 orders
- Bread: 2 orders
- Potato: 2 orders
+ 15 other items
Your usuals are ready!"
Smart Selection Logic (Using History):
Example:
User: "Add milk"
[Check order-history.json]
→ "Amul Taaza Toned Fresh Milk | Pouch (500ml)" ordered 3x
Response:
"🥛 Adding your usual milk!
Amul Taaza Toned Fresh Milk (500ml) - ₹29
📊 You've ordered this 3 times
✅ Added to cart"
If only ordered once or never:
User: "Add milk"
[Check order-history.json]
→ "Amul Taaza" ordered 1x only
Response:
"🥛 Found some milk options:
1. Amul Taaza Toned (500ml) - ₹29 ⭐ 4.8 (100k) - You've ordered this once
2. Amul Gold (1L) - ₹68 ⭐ 4.9 (80k) - Most popular
3. Mother Dairy (500ml) - ₹30 ⭐ 4.7 (60k)
Which one? (or tell me a number)"
Update order history: After each successful order, update the JSON file with new items.
Check if already logged in:
browser open url=https://www.zepto.com profile=openclaw
browser snapshot --interactive profile=openclaw
# Look for "login" button vs "profile" link
If NOT logged in, start auth flow:
Ask user: "What's your phone number for Zepto? (10 digits)"
# Click login button
browser act profile=openclaw request='{"kind":"click","ref":"{login_button_ref}"}'
# Type phone number
browser act profile=openclaw request='{"kind":"type","ref":"{phone_input_ref}","text":"{phone}"}'
# Click Continue
browser act profile=openclaw request='{"kind":"click","ref":"{continue_button_ref}"}'
Ask user: "I've sent the OTP to {phone}. What's the OTP you received?"
browser snapshot --interactive profile=openclaw # Get OTP input refs
browser act profile=openclaw request='{"kind":"type","ref":"{otp_input_ref}","text":"{otp}"}'
# OTP auto-submits after 6 digits
Result: User is now logged in! Session persists across browser restarts.
🚨 CRITICAL: ALWAYS CHECK ADDRESS BEFORE PROCEEDING WITH ANY SHOPPING!
Default behavior:
On homepage, address is visible in the header:
browser snapshot --interactive profile=openclaw
# Look for button with heading level=3 containing the address
# Example ref: e16 with text like "Home - [Address Details]..."
# Delivery time shown nearby (e.g., "10 minutes")
ALWAYS ask user to confirm before shopping:
📍 I see your delivery address is set to:
{Address Name} - {Full Address}
⏱️ Delivery in ~{X} minutes
Is this correct? Should I proceed with this address?
Use the zepto-agent.js select-address command:
node zepto-agent.js select-address "Home"
node zepto-agent.js select-address "sanskar" # Fuzzy matching works!
node zepto-agent.js select-address "kundu blr"
How it works:
Example:
# Current address: "Kundu Blr"
node zepto-agent.js select-address "sanskar"
# Output:
# ℹ️ Opening Zepto...
# ✅ Zepto opened
# ℹ️ 📍 Selecting address: "sanskar"
# ℹ️ Current: Kundu Blr
# ✅ Clicked: Sanskar BlrA-301, A, BLOCK-B...
# 🎉 Address changed to: Sanskar blr
When user says "change address to X" or "deliver to X":
# Just call the command with their address name/query
node zepto-agent.js select-address "{user_query}"
No manual modal navigation needed! The script handles:
Manual Selection (Fallback): If the programmatic method fails or address isn't found:
# Click the address button (ref e16 or similar)
browser act profile=openclaw request='{"kind":"click","ref":"e16"}'
# This opens address selection modal with all saved addresses
Select address using JavaScript:
# Replace {USER_ADDRESS_NAME} with the actual address name user selected
browser act profile=openclaw request='{"fn":"() => { const input = document.querySelector('input[placeholder*=\"address\"]'); if (!input) return { error: 'Modal not found' }; let modal = input; for (let i = 0; i < 15; i++) { if (!modal.parentElement) break; modal = modal.parentElement; if (window.getComputedStyle(modal).position === 'fixed') break; } const divs = Array.from(modal.querySelectorAll('div')); const match = divs.find(d => d.textContent && d.textContent.trim().startsWith('{USER_ADDRESS_NAME}')); if (!match) return { error: 'Address not found' }; let p = match; for (let i = 0; i < 10; i++) { if (!p) break; const s = window.getComputedStyle(p); if (p.onclick || p.getAttribute('onClick') || s.cursor === 'pointer') { p.scrollIntoView({ block: 'center' }); setTimeout(() => {}, 300); p.click(); return { clicked: true, text: match.textContent.substring(0, 100) }; } p = p.parentElement; } return { error: 'No clickable parent' }; }()","kind":"evaluate"}'
After address confirmed by user:
✅ Delivery address confirmed: {address_name}
📍 {full_address}
⏱️ ETA: {eta} mins
Ready to shop! What would you like to add to cart?
⚠️ Address is CRITICAL - never skip this step!
When user asks to "explore", "show me", "what's good", "find something", or "discover":
Common Discovery Patterns:
Browse Categories:
# Navigate to category pages
browser navigate url=https://www.zepto.com profile=openclaw
browser snapshot --interactive profile=openclaw
# Categories available on homepage:
# - Fruits & Vegetables
# - Dairy, Bread & Eggs
# - Munchies (snacks)
# - Cold Drinks & Juices
# - Breakfast & Sauces
# - Atta, Rice, Oil & Dals
# - Cleaning Essentials
# - Bath & Body
# - Makeup & Beauty
Filter & Sort:
# Example: Browse "Munchies" category
browser navigate url=https://www.zepto.com/pn/munchies profile=openclaw
browser snapshot --interactive profile=openclaw
# Take screenshot to show user the options
browser screenshot profile=openclaw
Discovery Response Format:
🔍 Found some great options in {category}:
1. **{Product Name}** - ₹{price} ({discount}% OFF)
⭐ {rating} ({review_count} reviews)
📦 {size/quantity}
2. **{Product Name}** - ₹{price}
⭐ {rating} ({review_count} reviews)
3. **{Product Name}** - ₹{price} ({discount}% OFF)
⭐ {rating} ({review_count} reviews)
Want me to add any of these? Just tell me the number(s)!
Smart Filtering Tips:
Interactive Discovery: After showing options, user can:
MANDATORY PRE-FLIGHT CHECK: Before adding ANY items:
Multi-Item Shopping Flow: When user gives a list (e.g., "add milk, butter, bread"):
CRITICAL: Never batch-add without verification! Page refs change after each add.
Item Selection Logic:
When UNCLEAR about variant:
🥛 Found multiple milk options:
1. Amul Taaza (500ml) - ₹29 ⭐ 4.8 (100k)
2. Amul Gold (1L) - ₹68 ⭐ 4.9 (80k)
3. Mother Dairy (500ml) - ₹30 ⭐ 4.7 (60k)
Which one? (or tell me a number)
Search Process:
browser navigate url=https://www.zepto.com/search?query={item} profile=openclaw
browser snapshot --interactive profile=openclaw
Rule: Pick product with highest review count (unless order history says otherwise).
Format: {rating} ({count}) where k=thousand, M=million.
Example: "4.8 (694.4k)" = 694,400 reviews = most popular.
browser act profile=openclaw request='{"kind":"click","ref":"{ADD_button_ref}"}'
browser navigate url=https://www.zepto.com/?cart=open profile=openclaw
browser snapshot profile=openclaw # Get cart summary
Cart Summary Format:
🛒 Added to cart:
1. Item 1 - ₹XX
2. Item 2 - ₹YY
3. Item 3 - ₹ZZ
💰 Total: ₹{total}
Ready to checkout? (say "yes" or "checkout" or "lessgo")
CRITICAL - Quantity Mapping: When user provides a shopping list with quantities (e.g., "3x jeera, 2x saffola oats"):
Example mapping:
{
"jeera": 3,
"saffola_oats": 2,
"milk": 1
}
Before removing duplicates or adjusting quantities:
If item not found or out of stock:
❌ {item} is currently unavailable.
🔍 Suggestions:
- {similar_item_1}
- {similar_item_2}
What would you like instead?
Don't auto-add alternatives - wait for user's next item or choice.
After all items added to cart and user confirms checkout:
# Open cart modal
browser act profile=openclaw request='{"kind":"click","ref":"{cart_button_ref}"}'
# Example ref from homepage: e44
# Wait for cart to open, take snapshot
browser snapshot --interactive profile=openclaw
# Click "Click to Pay ₹{amount}" button
browser act profile=openclaw request='{"kind":"click","ref":"{click_to_pay_button_ref}"}'
# Example ref: e3579
# Wait 2 seconds for navigation to complete
browser act profile=openclaw request='{"fn":"async () => { await new Promise(r => setTimeout(r, 2000)); return window.location.href; }","kind":"evaluate"}'
URL Format:
https://payments.juspay.in/payment-page/signature/zeptomarketplace-{order_id}
Example:
https://payments.juspay.in/payment-page/signature/zeptomarketplace-{ORDER_ID_EXAMPLE}
message action=send channel=whatsapp target={user_phone} message="🛒 *Your Zepto order is ready!*
*Cart Summary ({item_count} items):*
1. {item1} - ₹{price1}
2. {item2} - ₹{price2}
3. {item3} - ₹{price3}
*💰 Total: ₹{total}*
📍 Delivering to: {address_name} - {address}
⏱️ ETA: {eta} minutes
*🔗 Click here to pay:*
{juspay_payment_link}
⚠️ *IMPORTANT: After payment, message me \"DONE\" to confirm your order!*
(Don't rely on the payment page - just tell me when you've paid and I'll verify it) 🚀"
After user says "done" or "paid":
Step 1: Navigate to Zepto homepage to check order status
browser navigate url=https://www.zepto.com profile=openclaw
browser snapshot --interactive profile=openclaw
Step 2: Look for order confirmation Check for text like:
Step 3: Auto-clear cart (Post-Payment Behavior)
🚨 CRITICAL: After payment, cart items persist because Zepto hasn't synced yet!
Automatically clear cart without asking (user expects cart to be empty after payment):
# Open cart
browser act profile=openclaw request='{"kind":"click","ref":"{cart_button_ref}"}'
browser snapshot --interactive profile=openclaw
# Click Remove button for each item
browser act profile=openclaw request='{"kind":"click","ref":"{remove_button_ref_1}"}'
browser act profile=openclaw request='{"kind":"click","ref":"{remove_button_ref_2}"}'
browser act profile=openclaw request='{"kind":"click","ref":"{remove_button_ref_3}"}'
# ... repeat for all items
Step 4: Confirm to user
If order confirmed:
✅ *Payment confirmed!*
🚚 Your order is on the way! Arriving in ~{X} mins.
Order details:
- {item_count} items, ₹{total}
- Delivery to: {address}
✅ Cart cleared ({item_count} items removed from previous order)
🛒 Ready for your next order! 🐺
If order NOT showing yet:
⏳ Payment processed, but order confirmation is still loading on Zepto's end.
Let me check again in 30 seconds...
Then set up a background check to try again.
Step 1: Navigate back to Zepto homepage
browser navigate url=https://www.zepto.com profile=openclaw
Step 2: Check order status on homepage
browser snapshot --interactive profile=openclaw
# Look for "Your order is on the way" or order tracking
Step 3: Open cart and check items
browser act profile=openclaw request='{"kind":"click","ref":"{cart_button_ref}"}'
browser snapshot --interactive profile=openclaw
🚨 CRITICAL: Cart items may still be there because Zepto hasn't synced order confirmation yet!
Step 4: Ask user about clearing cart
✅ Payment confirmed! Your order is on the way.
⚠️ I can see {X} items still in the cart (from the previous order that just went through).
Should I:
1. Clear the cart (recommended for fresh start)
2. Keep the items (if you want to reorder them)
*Default: I'll clear the cart unless you say "keep it"*
Step 5: Clear cart if user approves (or by default)
# For each item in cart, click Remove button
browser act profile=openclaw request='{"kind":"click","ref":"{remove_button_ref_1}"}'
browser act profile=openclaw request='{"kind":"click","ref":"{remove_button_ref_2}"}'
# ... repeat for all items
# Or use JavaScript to clear all at once:
browser act profile=openclaw request='{"fn":"() => { const removeButtons = document.querySelectorAll(\"button\"); let count = 0; for (let btn of removeButtons) { if (btn.textContent.trim() === \"Remove\") { btn.click(); count++; } } return `Removed ${count} items`; }","kind":"evaluate"}'
Confirmation message:
✅ Cart cleared! ({X} items removed)
🛒 Ready for your next order!
Your current order ({item_count} items, ₹{total}) will arrive in ~{eta} mins.
If user says "keep it":
✅ Got it! Keeping {X} items in cart.
🛒 Ready to add more items or proceed with these?
**If delivery address becomes unserviceable:**
⚠️ Your delivery address is currently unserviceable. Should I order it to a different address?
(I can show you all your saved addresses)
---
## 🎯 Complete Order Flow Summary
### Before Starting ANY New Order (Normal Flow - No Recent Payment):
**1. Check Address (ALWAYS)**
📍 Current address: {address} Is this correct?
**2. Check Cart (if items exist)**
```bash
# Open cart
browser act profile=openclaw request='{"kind":"click","ref":"{cart_button_ref}"}'
browser snapshot --interactive profile=openclaw
If items in cart from NORMAL browsing (not post-payment):
⚠️ I see {X} items in your cart:
1. {item1} - ₹{price1}
2. {item2} - ₹{price2}
Should I:
1. Clear the cart
2. Keep these items
What would you like?
Wait for user response before proceeding.
This is DIFFERENT from normal flow - auto-clear expected!
1. Navigate to zepto.com and check order status
browser navigate url=https://www.zepto.com profile=openclaw
browser snapshot --interactive profile=openclaw
2. Look for "Your order is on the way" or "Arriving in X mins"
3. Open cart and AUTO-CLEAR without asking
# Open cart
browser act profile=openclaw request='{"kind":"click","ref":"{cart_button_ref}"}'
# Remove all items (they're from the order that just went through)
browser act profile=openclaw request='{"kind":"click","ref":"{remove_ref_1}"}'
browser act profile=openclaw request='{"kind":"click","ref":"{remove_ref_2}"}'
browser act profile=openclaw request='{"kind":"click","ref":"{remove_ref_3}"}'
4. Confirm to user
✅ Payment confirmed! Your order is on the way! Arriving in ~{X} mins.
✅ Cart cleared ({item_count} items removed from previous order)
🛒 Ready for your next order!
Why auto-clear in post-payment?
✅ Cart cleared!
✅ Address confirmed: {address}
What would you like to order? 🛒
Key Difference:
✅ DO:
❌ DON'T:
Phone number invalid:
"Phone number should be 10 digits. Please try again."
OTP verification failed:
"OTP verification failed. Let me resend the OTP.
Check your phone for the new code."
Location not serviceable:
"⚠️ Your location is currently not serviceable by Zepto.
Store might be temporarily closed or location outside delivery zone.
Want to try a different address?"
Item not found:
"Couldn't find {item} on Zepto. Try a different search term?"
After successful authentication:
To check if authenticated:
browser navigate url=https://www.zepto.com profile=openclaw
browser snapshot --interactive profile=openclaw
# If "profile" link exists → logged in
# If "login" button exists → need to auth