A complete, production-ready WhatsApp customer support bot built with Express.js and meta-cloud-api. Features conversation flows, ticket management, queue processing, and comprehensive observability.
- Conversation State Machine: Guided multi-step conversation flows for ticket creation
- Ticket Management: Full CRUD operations with status tracking and categorization
- Queue Processing: Background job processing with BullMQ for notifications and ticket handling
- Session Management: Redis-based session storage with automatic cleanup
- Database Integration: PostgreSQL with Prisma ORM for data persistence
- Security: Helmet, CORS, rate limiting, and input validation
- Observability: Structured logging with Winston, health checks, and metrics
- Type Safety: Full TypeScript with strict mode and comprehensive type definitions
- Testing: Unit, integration, and E2E tests with Vitest
- Docker Support: Multi-stage Dockerfile and docker-compose for easy deployment
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ WhatsApp │────────>│ Express.js │────────>│ PostgreSQL │
│ Business │ │ API │ │ Database │
│ API │<────────│ (Webhook) │<────────│ (Prisma) │
└─────────────┘ └──────────────┘ └─────────────┘
│
│
v
┌──────────┐
│ Redis │
│ Sessions │
│ Cache │
└──────────┘
│
│
v
┌──────────┐
│ BullMQ │
│ Queues │
└──────────┘
│
v
┌──────────┐
│ Workers │
│ - Notifications │
│ - Tickets │
└──────────┘
The bot guides users through a structured conversation to create support tickets:
1. User sends "Hi"
→ Bot sends welcome message with "New Issue" button
2. User clicks "New Issue"
→ Bot asks: "What's your name?"
→ State: COLLECTING_NAME
3. User replies: "John Doe"
→ Bot asks: "Please describe your issue"
→ State: COLLECTING_ISSUE
4. User replies: "Payment failed"
→ Bot shows category list (Technical Support, Billing, etc.)
→ State: SELECTING_CATEGORY
5. User selects: "Billing & Payment"
→ Bot shows summary with Confirm/Cancel buttons
→ State: CONFIRMING_TICKET
6. User clicks "Confirm"
→ Bot creates ticket and sends ticket number
→ State: COMPLETED
→ Ticket stored in database
→ Notification sent to support team
stateDiagram-v2
[*] --> IDLE
IDLE --> COLLECTING_NAME: New Issue
COLLECTING_NAME --> COLLECTING_ISSUE: Name provided
COLLECTING_ISSUE --> SELECTING_CATEGORY: Issue described
SELECTING_CATEGORY --> CONFIRMING_TICKET: Category selected
CONFIRMING_TICKET --> COMPLETED: Confirmed
CONFIRMING_TICKET --> CANCELLED: Cancelled
COMPLETED --> IDLE: Reset
CANCELLED --> IDLE: Reset
express-production/
├── prisma/
│ ├── schema.prisma # Database schema
│ └── seed.ts # Sample data
├── src/
│ ├── config/ # Configuration
│ │ ├── index.ts # Environment validation
│ │ ├── logger.ts # Winston logger
│ │ ├── database.ts # Prisma client
│ │ └── redis.ts # Redis client
│ ├── middleware/ # Express middleware
│ │ ├── errorHandler.ts # Global error handling
│ │ ├── rateLimiter.ts # Rate limiting
│ │ ├── requestLogger.ts # HTTP logging
│ │ ├── validator.ts # Zod validation
│ │ └── security.ts # Helmet & CORS
│ ├── services/ # Business logic
│ │ ├── whatsapp/
│ │ │ ├── client.ts # WhatsApp client singleton
│ │ │ ├── sender.ts # Message sending with retry
│ │ │ └── templates.ts # Message templates
│ │ ├── conversation/
│ │ │ ├── stateMachine.ts # State transitions
│ │ │ ├── sessionStore.ts # Redis sessions
│ │ │ └── flows.ts # Conversation flows
│ │ ├── tickets/
│ │ │ ├── ticketService.ts # Ticket CRUD
│ │ │ └── categories.ts # Category metadata
│ │ └── queue/
│ │ ├── queueManager.ts # BullMQ setup
│ │ └── workers/
│ │ ├── notificationWorker.ts
│ │ └── ticketWorker.ts
│ ├── handlers/ # Message & webhook handlers
│ │ ├── messages/
│ │ │ ├── text.ts # Text message handler
│ │ │ ├── interactive.ts # Button/list handler
│ │ │ ├── media.ts # Media handlers
│ │ │ └── index.ts
│ │ └── webhooks/
│ │ ├── status.ts # Status updates
│ │ ├── flows.ts # Flow webhooks
│ │ └── index.ts
│ ├── routes/ # Express routes
│ │ ├── webhook.ts # WhatsApp webhook
│ │ ├── health.ts # Health checks
│ │ ├── api/
│ │ │ └── tickets.ts # Ticket REST API
│ │ └── index.ts
│ ├── utils/ # Utilities
│ │ ├── responses.ts # Response builders
│ │ ├── validation.ts # Validators
│ │ ├── formatters.ts # Formatters
│ │ └── errors.ts # Custom errors
│ ├── types/ # Type definitions
│ │ ├── index.ts
│ │ ├── conversation.ts
│ │ └── ticket.ts
│ ├── app.ts # Express app factory
│ ├── server.ts # HTTP server
│ └── workers/
│ └── index.ts # Worker process
├── tests/ # Tests
│ ├── setup.ts # Test setup
│ ├── fixtures/ # Test data
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ └── e2e/ # E2E tests
├── Dockerfile # Multi-stage build
├── docker-compose.yml # Full stack setup
├── .env.example # Environment template
├── .gitignore
├── tsconfig.json # TypeScript config
├── vitest.config.ts # Vitest config
├── package.json
└── README.md
- Node.js >= 18
- pnpm >= 10
- PostgreSQL >= 14
- Redis >= 7
- WhatsApp Business Account with Cloud API access
cd examples/express-production
pnpm installCopy .env.example to .env and configure:
cp .env.example .envRequired environment variables:
# WhatsApp Cloud API
WHATSAPP_ACCESS_TOKEN=your_access_token
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
WHATSAPP_WEBHOOK_VERIFICATION_TOKEN=your_webhook_token
# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/whatsapp_bot
# Redis
REDIS_URL=redis://localhost:6379
# Optional: Support team notifications
SUPPORT_TEAM_PHONE=14155552671# Generate Prisma client
pnpm prisma:generate
# Run migrations
pnpm prisma:migrate
# Seed sample data (optional)
pnpm prisma:seed
# View database in Prisma Studio
pnpm prisma:studio# Start API server
pnpm dev
# Start worker process (in another terminal)
pnpm worker
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Type checking
pnpm typecheck
# Linting
pnpm lint# Start all services (PostgreSQL, Redis, App, Worker)
pnpm docker:up
# Stop all services
pnpm docker:down
# View logs
docker compose logs -f app
docker compose logs -f worker# Build production image
pnpm docker:build
# Deploy to your container platform
# (Kubernetes, ECS, Cloud Run, etc.)GET /webhook- Webhook verificationPOST /webhook- Receive WhatsApp events
GET /health- Overall health statusGET /health/ready- Readiness probe (K8s)GET /health/live- Liveness probe (K8s)
GET /api/tickets- List tickets (with filtering)GET /api/tickets/:id- Get ticket by IDGET /api/tickets/number/:ticketNumber- Get ticket by numberGET /api/tickets/user/:userId- Get user's ticketsGET /api/tickets/stats- Get ticket statisticsPATCH /api/tickets/:id/status- Update ticket statusPATCH /api/tickets/:id/assign- Assign ticketPATCH /api/tickets/:id/priority- Update priorityPATCH /api/tickets/:id/tags- Add tagsDELETE /api/tickets/:id- Delete ticket
All configuration is managed through environment variables validated with Zod.
# Server
PORT=3000
NODE_ENV=production
# Session
SESSION_TIMEOUT=1800 # 30 minutes
SESSION_CLEANUP_INTERVAL=600000 # 10 minutes
# Queue
QUEUE_ATTEMPTS=3
QUEUE_BACKOFF_DELAY=5000
# Rate Limiting
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100
# Logging
LOG_LEVEL=info # error, warn, info, debug
LOG_FORMAT=json # json, simple
# Security
CORS_ORIGIN=*
HELMET_ENABLED=true
# Features
ENABLE_ANALYTICS=true
ENABLE_AUTO_REPLIES=true
ENABLE_TICKET_REMINDERS=true# Run all tests
pnpm test
# Run specific test file
pnpm vitest run src/api/messages/__test__/unit.test.ts
# Run with coverage
pnpm test --coverage
# Run E2E tests
pnpm test:e2e- Unit Tests: Test individual functions and classes
- Integration Tests: Test handlers and routes with mocked dependencies
- E2E Tests: Test complete conversation flows
Structured JSON logging with Winston:
logger.info('Ticket created', {
ticketNumber: 'T-10001',
userId: '14155552671',
category: 'TECHNICAL_SUPPORT'
});/health- Checks database, Redis, WhatsApp API, and queues/health/ready- Kubernetes readiness probe/health/live- Kubernetes liveness probe
Monitor these metrics:
- Message processing rate
- Ticket creation rate
- Queue depth and processing time
- Session count and TTL
- Error rates by type
- Environment Variables: Never commit
.envfiles - Rate Limiting: Configured for webhook and API endpoints
- Input Validation: All inputs validated with Zod
- SQL Injection: Protected by Prisma ORM
- CORS: Configurable allowed origins
- Helmet: Security headers enabled
- Error Handling: No sensitive info in error responses
- Set
NODE_ENV=production - Configure production database URL
- Set Redis URL with authentication
- Configure WhatsApp webhook URL
- Set webhook verification token
- Configure CORS allowed origins
- Set support team phone number
- Enable production logging (
LOG_FORMAT=json) - Configure health check endpoints
- Set up monitoring and alerting
- Configure backup strategy
- Set resource limits (CPU, memory)
- Enable SSL/TLS
- Configure rate limits
- Test graceful shutdown
- Set up log aggregation
Database connection failed
# Check DATABASE_URL is correct
# Verify PostgreSQL is running
docker compose logs postgresRedis connection failed
# Check REDIS_URL is correct
# Verify Redis is running
docker compose logs redisWebhook not receiving messages
# Verify webhook URL is publicly accessible
# Check WHATSAPP_WEBHOOK_VERIFICATION_TOKEN matches
# View webhook logs
docker compose logs app | grep webhookQueue jobs not processing
# Check worker is running
docker compose logs worker
# Verify Redis connectionThis is an example project. For issues or improvements to meta-cloud-api itself, please visit the main repository.
MIT License - See LICENSE file in the root repository.
For questions about this example:
- Review the code comments
- Check the main meta-cloud-api documentation
- Open an issue in the main repository
- meta-cloud-api Documentation
- WhatsApp Business Platform API
- Prisma Documentation
- BullMQ Documentation
- Express.js Documentation
Built with meta-cloud-api