Back to works Case Study
SaaS PlatformCustomer SupportCRM Integration

NemaChat

All-in-One Customer Support & Sales CRM for SMEs

Visit Live Website
NemaChat Interface Showcase

01 — Overview

Overview

a. Concept & Multi-Platform Ecosystem

NemaChat is an omnichannel customer support and sales platform designed specifically for Indonesian small-to-medium enterprises (SMEs) drowning in customer chats. We built NemaChat as a unified ecosystem of four decoupled repositories, enabling business agents to handle queries from WhatsApp, Instagram DM, Telegram, and Email inside a single fast screen while keeping track of deal pipelines.

The ecosystem consists of: (1) `nema-chat`, the core PHP Laravel 12 backend serving as the centralized business CRM, client portal, and multi-tenant agent database; (2) `nema-chat-wa-lite-gateway`, a high-throughput WhatsApp engine written in Go using the whatsmeow socket library; (3) `nema-chat-web`, the client-facing multilingual landing page website built with Astro v5 and Tailwind CSS; and (4) `nemachat-mobile`, a Flutter-based mobile companion app currently prepared as a work-in-progress (WIP) build.

NemaChat mobile application conversation list view

b. High-Level Architecture Flow

We designed a decoupled, event-driven microservice-like architecture to handle high volumes of chats without degrading user interface performance. When a customer sends a message on WhatsApp, it is captured by the Go WhatsApp gateway client. The gateway downloads any attached media, uploads it to AWS S3, and immediately triggers an HTTP POST webhook to the Laravel core backend.

The Laravel backend receives the webhook, parses the JID or sender details, creates or updates the Conversation and Message records in PostgreSQL, and pushes the event to Soketi (a self-hosted WebSocket server). The Inertia.js React dashboard and the Flutter mobile client receive these real-time notifications via standard WebSocket subscriptions, updating the chat UI dynamically. Outgoing agent responses are sent via Laravel controllers which relay HTTP POST API requests to the Go gateway, initiating socket delivery to WhatsApp servers.

NemaChat core engine inbound message routing and processing workflow
Timeline Dec 2024 – Jun 2026
Role Full-Stack Development & Architecture
Technologies
Laravel 12Inertia.jsReactGo 1.25whatsmeowPostgreSQLRedisSoketiFlutterRiverpod
Tools
FigmaVS CodeGitHubPostman
Responsibilities
  • Designed & built the multi-platform system architecture
  • Coded the Go WhatsApp API gateway & events listener
  • Developed the Laravel CRM & Filament admin portal
  • Implemented Inertia.js React unified chat workspace
  • Architected the unreleased Flutter mobile app layout
  • Wrote multilingual Astro landing page & geo-redirects

02 — Problems & Challenges

Problems & Challenges

a. Gateway Session Handling

Maintaining hundreds of concurrent, persistent WhatsApp connections without official API keys is highly unstable. In-memory session tokens can drop randomly, and the gateway must keep sessions alive across process restarts to avoid forcing merchants to re-scan QR codes constantly.

We solved this in the Go gateway (`nema-chat-wa-lite-gateway`) by implementing a database-backed device store using whatsmeow's SQLStore container mapped to our PostgreSQL database. We built a dynamic Instance Manager using `sync.RWMutex` to manage in-memory `ClientWrapper` maps. When the server boots, the `RestoreAll()` function queries all connected instances and concurrently restores their connections. If pairing is required, we set up a context-controlled QR channel generation routine that outputs base64 PNG data URLs using `go-qrcode` and updates the instance status to `qr_required`, automatically cleaning up the connection if the scan limits are exceeded (capped at 5 refreshes).

Logical structure of the Go gateway connection state and read status synchronization handler

b. Real-time Message Queuing

Inbound WhatsApp webhooks containing heavy binary attachments (such as PDFs, audio notes, or images) can bottleneck the API gateway and the core backend, leading to dropped messages and slow sync rates.

We built a resilient, non-blocking media pipeline. When an event is received, `handleMessage` checks for media content. If media exists, the gateway streams the binary payload directly from WhatsApp's CDN, detects the content type using MIME type mapping, and uploads the bytes to AWS S3. The gateway then swaps the binary data for a public S3 CDN URL in the webhook metadata before executing a fire-and-forget HTTP POST to the Laravel backend. To prevent contact name decay, the gateway stores contact profiles in the database and asynchronously queries WhatsApp for updated profile pictures, keeping profile photo refreshes out of the main message flow.

Web interface conversation view displaying real-time incoming messages and media preview elements

c. Web & Mobile State Sync

Keeping the unified inbox synchronized across the React web dashboard and the Flutter mobile client in real time is critical. An agent marking a conversation as read, assigning it to a division (e.g., Support or Sales), or sending a reply must update all other agents' screens instantly.

We integrated Laravel Octane with Soketi Redis Pub/Sub to serve as the real-time event dispatcher. On the web client, we built a custom React state context that listens to WebSocket channels using Pusher-JS. On the mobile client, we configured `dart_pusher_channels` to listen to Soketi WebSocket streams, updating local Riverpod providers and list models dynamically. We implemented exponential connection retry logic inside the Flutter app to maintain stability over unreliable cellular connections.

Mobile application user interface showing channel filter and conversation state filtering mechanisms

03 — Project Goals

Project Goals

a. Omnichannel Communication

Our primary goal was to consolidate separate messaging pipelines—WhatsApp, Instagram DM, Telegram, and Email—into a single, unified inbox interface. We designed the dashboard layout to keep average message processing latency under 150ms.

By establishing a structured, unified message payload format across all channels, we made it simple to add new messaging providers without altering the front-end React interface or the state sync models.

b. Seamless User Experience

SME owners need a frictionless experience to train new agents quickly. We targeted a setup time of under 15 minutes, which required a simple, intuitive user interface.

We designed automatic division routing (directing conversations to divisions like Sales or Support based on keyword prompts) and structured billing panels inside Laravel Filament, providing merchants with absolute transparency regarding their usage limits, billing packages, and invoices.

04 — Design Process

Design Process

a. Core API & Database Modeling

We modeled the database schema to support multi-tenancy from day one. An `Organization` owns multiple `OrganizationPlatform` connections, `Users` with specific division roles, `Contacts` unified across channels (with platform-specific JIDs in `ContactPlatform`), and `Conversations` that track active assignments, divisions, and tag listings.

We leveraged GORM in Go and Eloquent in Laravel to write performant database queries, ensuring proper foreign key cascade rules to prevent orphan sessions and transaction records when a merchant deletes an integration.

CRM interface and visual sales pipeline flow illustrating lead stages and database organization mapping

b. Web Dashboard UI/UX

We designed the multi-column React chat layout to maximize productivity. The left panel shows the searchable active chat queues; the middle displays the conversation thread with rich text and media players; and the right panel loads the integrated CRM context, showing customer details, transaction history, and custom lead pipeline card fields.

We built the client interface with React, Tailwind, and custom SCSS components, styling interactive transitions for chat assignment changes, lead pipeline stage changes, and CSAT survey generation.

Web dashboard campaign management interface for broadcast messages config

c. Mobile App Blueprint

We mapped out a Flutter app layout optimized for mobile screens. The mobile interface uses a bottom navigation bar pointing to Chats, Contacts, Pipelines, and Settings. We designed the unified chat screen to support rapid text input, voice note playback, and image viewing.

We constructed Riverpod providers to decouple the business logic from visual widgets, preparing state structures to sync seamlessly with the Laravel database.

CRM visual layout showing lead card pipelines on the client-facing UI design

05 — Usability Testing

Usability Testing

a. Message Throughput Testing

We executed load testing on the Go gateway to evaluate message delivery under high traffic. Using concurrency tests, we validated that the gateway handles up to 5,000 inbound messages per minute without losing socket packets or dropping webhooks.

We monitored memory usage in whatsmeow and optimized connection pooling, ensuring SQL connections release back to the pool promptly.

b. Error & Disconnect Handling

We simulated network drops in the Go gateway to verify connection recovery behavior. The gateway intercepts disconnect events via whatsmeow handlers and triggers a re-connection loop with a 3-second base delay and up to 5 seconds of random jitter, preventing server overloading.

We set up structured logs using Zap (`go.uber.org/zap`) to record API failures, webhook delivery errors, and token mismatches, providing real-time visibility into engine health.

06 — Prototyping & Handoff

Prototyping & Handoff

a. API Gateway Integration

We created the REST API specification using Swagger (`github.com/swaggo/swag`), which is served directly by the Go gateway at `/api-docs`. Every endpoint requires API key authentication via the `x-api-key` header.

This schema definition makes it easy for other systems to communicate with the gateway, standardizing JSON payloads and error codes (such as `401 Unauthorized` and `404 Not Found`).

b. Unreleased Mobile App Preparation

Since the Flutter mobile application is not yet released, we focused on building a solid code architecture for future compilation. We configured deep links via GoRouter, integrated Firebase Cloud Messaging for background notifications, and set up Sentry for error tracking.

We structured the Flutter build scripts (`pubspec.yaml`, gradle configurations, and iOS info.plist fields) to support instant package builds as soon as the client authorizes the release of the mobile application.

07 — Key Takeaways

Key Takeaways

a. Lessons in Microservice-like Architecture

Decoupling the WhatsApp connection engine from the main PHP CRM application was a critical architectural decision. Using Go to run the whatsmeow socket instances keeps the system lightweight and ensures that high-volume message delivery does not exhaust the web application's resource pool.

b. Managing Multi-repo Projects

Engineering a multi-repo project (Laravel, Go, Astro, React, and Flutter) highlighted the importance of maintaining strict API contracts. Documenting schemas and automating integration checkpoints saved weeks of debugging and ensured that updates in one repository did not break downstream consumer applications.