
LLM-powered agents for lead qualification, CRM sync, and process orchestration.
Sales teams lose good leads to slow, inconsistent manual follow-up. Enriching a lead, scoring it against an ideal customer profile, and following up across WhatsApp and email — consistently, on time, for every lead — doesn't scale past a handful of reps. Luma needed to automate that loop without turning into a black box: every score needs a reason attached, every message needs to respect consent and channel restrictions, and a human rep needs to stay in control of what actually gets sent.
We're building Luma as an orchestration layer sitting between lead sources and CRM output, structured around an explicit lead state machine — new, enriching, enriched, qualifying, qualified or disqualified, outreach, contacted, responded, handoff, archived — rather than ad-hoc scripts glued together. Every AI task, reply suggestions and conversation summarization today, runs in a separate Python microservice behind a provider-agnostic LLM client, so the current OpenAI-only implementation can add another provider later without touching a single agent's code. The same swap-the-provider-without-touching-business-logic pattern covers WhatsApp too: a BSP (business solution provider) client factory currently backs onto Gallabox, ready for other providers behind the same interface. Enrichment, AI scoring, outreach and classification all run asynchronously through BullMQ and Redis, so the request/response cycle for a lead update or an inbound WhatsApp message is never blocked waiting on an LLM call or a third-party API.
Rep console
React inbox and CRM UI where reps see leads, conversations, AI-suggested replies and team performance.
Core API
Modular-monolith Fastify service owning leads, CRM sync, inbox, auth and team management, with a lead state machine as its backbone.
AI service
Separate Python microservice handling reply suggestions and conversation summarization behind a provider-agnostic LLM client — deliberately extracted out of the core API.
Data
Multi-tenant relational storage for leads, conversations, CRM sync state and AI usage/cost logs.
Async infrastructure
BullMQ queues over Redis run enrichment, AI scoring, outreach sends and inbound-message classification off the request path.
Realtime
A WebSocket gateway pushes live inbox and lead updates to connected reps, using Redis pub/sub to fan out across multiple Node instances without sticky sessions.
A single inbound WhatsApp message triggers several asynchronous steps before a rep ever sees a suggested reply — none of it blocks the webhook response.
Gallabox posts an inbound WhatsApp message to a webhook, which is acknowledged immediately and handed off to a worker.
The core API loads the lead's profile, recent message history and any existing conversation summary.
The AI service trims the message history to fit a hard input-token budget — always keeping at least the two most recent messages — before calling the model.
GPT-4o (or GPT-4o-mini) returns reply drafts as structured JSON, each scored by confidence, via the provider-agnostic LLM client.
Suggestions are pushed to the rep's inbox in real time over the WebSocket gateway, ready to approve, edit or send.
Problem 1
AI providers change fast — pricing, quality and even availability shift — and locking every agent directly to one SDK would mean rewriting all of them just to switch.
Solution
Agents only ever call a small LlmClient interface through a factory function; today only the OpenAI implementation exists, but adding Gemini or Anthropic later is a single new file plus one factory branch — no agent code changes.
Impact: The AI service can adopt a new model or provider without touching the suggest or summarize logic that depends on it.
Problem 2
The same swappable-provider problem exists on the WhatsApp side — Gallabox today, but a future tenant or region might need a different BSP.
Solution
A BspClient interface with a factory function currently returns a Gallabox implementation; any other provider slots in behind the same send-message and webhook contract.
Impact: Onboarding a second WhatsApp provider won't touch the inbox or outreach modules that use it.
Problem 3
LLM calls are billed per token, and without discipline a growing conversation history quietly balloons the cost of every single suggestion or summary.
Solution
Both the suggest and summarize agents enforce a hard input-token budget, trimming the oldest messages first — while always keeping at least two recent ones — and capping conversation summaries to an approximate length, checked before every call, not after.
Impact: Cost stays predictable per call regardless of how long a conversation has been running.
Problem 4
AI costs need to be visible to the business, not buried — and a wrong number here is worse than a missing one.
Solution
Token usage per call is logged, and USD cost is computed on the fly from a per-provider, per-model pricing table rather than stored at call time; if a model isn't in the pricing table the system reports it as unpriced instead of guessing.
Impact: Prices can be corrected retroactively across all historical usage, and the dashboard never silently shows a wrong cost.
Lead ingestion, WhatsApp inbound/outbound via Gallabox, AI reply suggestions, conversation summarization and Zoho CRM sync are all built and integrated.
Both the LLM client and the WhatsApp BSP client are built behind provider-agnostic interfaces, validated with one real implementation each.
Every AI call is token-budgeted, logged and priced, rather than bolted on after costs became a problem.
Foundation (Phase 0) is complete and the core loop (Phase 1) is substantially built; the engagement is currently on hold before reaching a sellable MVP.
Building something similar?
Let's Talk