Skip to content
All projects
Proprietary

GronerZap

The WhatsApp contact center I built into the CRM to replace a third-party vendor — serverless on AWS, four WhatsApp providers, hundreds of companies.

Role
Creator and lead developer — front end, back end and infrastructure
Period
Dec 2025 – Present
Context
Groner · built solo, 4 contributors later
Stack
WhatsApp Cloud API Lambda API Gateway SQS DynamoDB S3 EC2 Vue.js TypeScript Node.js PWA Tailwind CSS C# Uazapi

This work is proprietary to Groner. It is described at architecture level, without source code or client data.

At a glance

  • Replaced a vendor that cost over R$30k a month
  • Hundreds of companies, thousands of users
  • 4 WhatsApp providers, one per number
  • First version live in 2 months

Context

Groner is the CRM built for Brazil’s solar energy industry, and in Brazil, selling happens on WhatsApp. Until GronerZap, the CRM’s WhatsApp channel ran on a third-party vendor that cost more than R$30,000 a month — a bill that grew with every new customer.

The problem

  • A cost that scaled the wrong way. Every new customer made the vendor bill bigger.
  • Stability out of our hands. Connection drops and recurring bugs we couldn’t fix ourselves.
  • No control. We only talked to the vendor through webhooks: we couldn’t change its front end or its back end, and it didn’t evolve with our customers’ needs.

My role

I built GronerZap end to end: front end, back end, infrastructure, the CRM integration and the PWA. The first version, on Uazapi, went live in two months. Later I added Meta’s official WhatsApp API, Z-API and a self-hosted Evolution Go. Four other developers joined over time, with features such as bulk-messaging campaigns, and fixes.

Architecture

GronerZap architecture Each WhatsApp number runs on one of four providers — Meta’s official Cloud API, Uazapi, Z-API or a self-hosted Evolution Go on EC2 — and its webhooks go to intake Lambdas on AWS, which normalize the events and queue them in SQS. Worker Lambdas store messages and media in DynamoDB and S3, forward them to the CRM’s C# API, which links them to leads and decides who can see them, and push them over a WebSocket API to the GronerZap PWA, which runs inside the CRM or as an installed app. Replies go from the PWA to an HTTP API and out through the provider adapter of each number. WhatsApp providers GronerZap · serverless on AWS Groner CRM Meta Cloud API official API Uazapi unofficial API Z-API unofficial API Evolution Go self-hosted · EC2 Webhooks intake Lambdas SQS + DLQs FIFO for history DynamoDB · S3 messages · media Worker Lambdas store · fan out HTTP API provider adapters WebSocket API live events CRM API (C#) leads · access · CSAT GronerZap PWA Vue · iframe or app webhooks forward live send via adapter
Each number runs on one of four providers: webhooks come in through a queue, live updates go out over WebSocket, and replies leave through that number’s adapter.
  • Front end — a Vue 3 + TypeScript PWA. Inside the CRM it runs in an iframe, receiving the session encrypted and talking to the CRM through postMessage; outside, it installs as an app on phones and desktops, with offline caching and web push.
  • Back end — fully serverless on AWS, defined with SAM: HTTP and WebSocket APIs on API Gateway, about 150 Lambda functions, SQS queues with dead-letter queues, DynamoDB and S3.
  • Inbound — each provider’s webhook is normalized and queued. A worker resolves the WhatsApp number, stores the message and its media without duplicates and, in parallel, forwards it to the CRM and pushes it to the agents’ screens over WebSocket.
  • CRM integration — the C# monolith stays the source of truth: it links conversations to leads and deals, decides what each user can see, and runs the CSAT surveys, service queues and reports.
  • Self-hosted provider — Evolution Go runs on our own EC2 instance, defined in CDK, with Docker, snapshots, a database watchdog and alarms.

Key decisions

  • One interface, four providers. Every provider implements the same client interface, with a capability matrix for what each one supports, and each WhatsApp number picks its provider. Adding Z-API and Evolution Go meant writing adapters, not changing the inbox.
  • Meta’s official API as an option. Unofficial providers are cheaper, but numbers can get restricted, so customers who want zero risk use the official API — the system enforces Meta’s 24-hour window and suggests a template outside it. On the unofficial providers, an anti-ban guard respects opt-outs, caps bursts and daily volume, and stops sending when a number gets restricted.
  • Built to be replayed. Every queue has a dead-letter queue, inbound and outbound messages are idempotent, and a failed forward to the CRM is recorded on the message so it can be replayed. X-Ray tracing and log-based alarms watch the whole flow.

The hardest part

Moving hundreds of companies — thousands of people who spend their workday on WhatsApp — to a new system without breaking their routine. Stability had to match the old tool from day one while the interface, performance and reliability got better, so each company moved over behind its own beta flag.

Migrating also meant importing each company’s history, and one import fanned out into 358 parallel runs and saturated the CRM’s database. History imports now run in order through a FIFO queue, with concurrency capped at five.

The code was half the job. The other half was earning the trust of people used to the old way.

Results

  • More than R$30,000 a month in vendor costs eliminated.
  • WhatsApp became a profitable add-on: customers pay per connected number, and its infrastructure costs a small fraction of that.
  • Hundreds of companies and thousands of users on GronerZap.