Return to projects
Project deep dive· Functional prototype·

WhatsApp Commerce Bot

Stateful conversational shopping through Meta APIs

A Node.js commerce bot that combines WhatsApp interactive messages, Wit.ai intent detection, Firestore-backed carts and orders, checkout state, tracking, and human-agent handoff.

Node.js
Express
Firebase
Meta
Node.jsExpressFirebaseMeta

Overview

The WhatsApp Commerce Bot is a full-stack conversational prototype for a fictional gourmet store called The Local Pantry. It turns a WhatsApp chat into a lightweight shopping interface with product discovery, a persistent cart, checkout, order creation, order tracking, and escalation to a human agent.

The application uses Express as the webhook server, the Meta WhatsApp Business Cloud API for messages and commerce interactions, Firestore for customer and order state, and Wit.ai for natural-language intent and entity recognition.

The project is deliberately more than a menu bot. A user can ask for a product category in natural language, add catalog items from native WhatsApp product messages, leave the conversation, return to the same cart, complete a multi-step checkout, and later retrieve the saved order by ID.

Architecture

The repository separates transport, conversation routing, persistence, and external AI calls into small modules.

Meta webhook
    |
Express route and conversation controller
    |--------> Wit.ai intent detection
    |--------> Firestore users, cart, and orders
    +--------> WhatsApp message service

app.js configures Slack-style modularity for Express: environment loading, JSON parsing, the webhook route, and server startup. The webhook controller coordinates messages, while service modules own WhatsApp Graph API payloads and Wit.ai requests. The Firestore module owns user, cart, and order operations.

Stateful conversation flow

WhatsApp webhook events are stateless HTTP requests, but checkout is not a one-message operation. The bot stores a state on each Firestore user document.

Implemented states include:

  • default for menus and intent routing;
  • awaiting_name during checkout;
  • awaiting_address before order creation;
  • awaiting_order_id during tracking;
  • awaiting_agent while automation is paused.
default
  -> checkout selected
awaiting_name
  -> name captured
awaiting_address
  -> address + cart become order
default

The controller checks exact commands first for predictable and fast menu behavior. Other text is sent to Wit.ai. A recognized browse_category intent or product-category entity routes directly to coffee, bread, or cheese handling. Unrecognized language falls back to a menu-oriented recovery message.

Native WhatsApp interaction patterns

The bot uses platform-native messages instead of forcing every interaction through free text:

  • list messages for the main navigation;
  • reply buttons for category selection and checkout decisions;
  • multi-product messages connected to a Meta Commerce catalog;
  • order-message events when a user selects catalog products;
  • approved utility templates for formal order confirmation.

This creates an app-like flow while staying inside WhatsApp. It also reduces ambiguous parsing for high-value actions such as checkout.

Persistent carts and order tracking

Each WhatsApp ID maps to a Firestore user. Cart items live in a subcollection under that user, which means cart state survives separate webhook deliveries and later conversations.

When the user supplies a name and delivery address, the bot reads the cart, creates a top-level order document, sends text and template confirmations, clears the cart in a Firestore batch, stores the address, and returns the user to the default state.

Order tracking normalizes the supplied ID, reads the matching document, and reports its current status, item count, and delivery destination.

users/{whatsappId}
  state
  name
  address
  cart/{sku}

orders/{orderId}
  customer
  items
  shipping address
  status
  created timestamp

Human handoff

Selecting “Talk to an Agent” moves the user into awaiting_agent. While that state is active, bot handling for interactive orders is paused. An administrative !resume command returns the conversation to automated handling.

This is a simple prototype of a crucial production concept: automation must have a deliberate escape route. A support agent should inherit both the conversation and the context already collected rather than forcing the customer to start again.

Current limitations

The prototype demonstrates the complete shape of the interaction, but several production controls remain necessary:

  • webhook signature verification and idempotent event processing;
  • durable event receipt before acknowledging Meta;
  • transactional or collision-safe order-number generation;
  • stronger authentication around the resume command;
  • validated catalog quantities, pricing, and inventory;
  • retries and delivery-status reconciliation for outbound messages;
  • a real agent inbox and role model;
  • Firestore security rules and managed credential loading;
  • automated tests for conversation transitions.

Some menu branches are explicitly marked as coming soon, including specials and order history. The current catalog demonstration is intentionally small.

What I learned

This project taught me to treat conversational software as a state machine connected to external systems, not as a list of keyword replies.

The important decisions were persisting user state between webhook requests, separating message payload construction from routing, using native interactive messages for deterministic actions, and preserving a human handoff path. It also exposed the reliability work required to move from a convincing demo to a production messaging system: signatures, idempotency, durable queues, retries, and operator controls.

Explore the project