How I built an open-source WhatsApp claims bot for insurers in the DRC
How I built an open-source WhatsApp claims bot for insurers in the DRC In Kinshasa, nobody fills in an online claim form. If your car is hit by a minibus on Boulevard du 30 Juin, you take photos and send a WhatsApp mes
How I built an open-source WhatsApp claims bot for insurers in the DRC
In Kinshasa, nobody fills in an online claim form. If your car is hit by a minibus on Boulevard du 30 Juin, you take photos and send a WhatsApp message to whoever you know at your insurer. That message lands on a personal phone, the photos get lost in the chat, and someone types the claim into a system days later, if at all.
RapidOS is my attempt to fix that. It's a WhatsApp assistant plus a dashboard. The insurer connects its own WhatsApp Business number and its own AI key, the assistant collects the claim in the customer's language, and the claims team gets a complete, structured file with a PDF. It's open source under AGPL-3.0, and it runs with one docker compose up.
This post covers how it works and what was harder than I expected.
The architecture
There are four services:
- Go API (Gin, GORM): receives Meta's webhook, runs the conversation with the company's AI provider, and sends replies through the WhatsApp Cloud API.
- Next.js dashboard (Prisma): claims, customers, conversations, users and settings. It also generates claim PDFs.
- Postgres for everything persistent. Redis for rate limiting.
- A one-shot migrate container that runs
prisma migrate deploybefore the others start.
One installation serves several companies. Each one has its own WhatsApp number, AI provider, coverage types, team and roles (super admin, admin, moderator, agent, viewer). WhatsApp tokens and AI keys are stored encrypted and shown masked in the UI.
Bring your own WhatsApp number and AI key
I didn't want RapidOS to be a middleman. Each company pastes its WhatsApp Cloud API credentials and registers the webhook in Meta itself. The webhook path is fixed (/api/v1/whatsapp/webhook), and the API matches Meta's GET challenge against each company's saved verify token, so a token change takes effect immediately. POSTs are checked against X-Hub-Signature-256, an HMAC-SHA256 of the body using the company's app secret.
For the AI, there are two client implementations behind one interface: Gemini, and an OpenAI-compatible client that covers OpenAI, DeepSeek, Qwen, and anything self-hosted like Ollama or vLLM. A company that won't send customer data to a cloud model can point RapidOS at http://host.docker.internal:11434/v1 and keep it all on its own hardware. One practical catch: Ollama unloads idle models after five minutes, so the next customer waits 10β30 seconds for a cold load. Setting OLLAMA_KEEP_ALIVE fixes it.
Webhooks: ack fast, process in order
Meta wants a quick 200, and it retries deliveries. Customers also send bursts: three photos and a sentence within two seconds. So the handler acknowledges immediately and hands the message to a dispatcher. Messages from the same conversation (company + sender) run one after another, in arrival order. Different conversations run in parallel. Recently seen message ids are skipped, and the database dedupe stays the source of truth. Without per-conversation ordering, two messages processed at the same time could both update the same claim draft.
Getting a claim out of a chat without inventing anything
This is the part that took the longest. The flow:
- The company defines coverage types (auto, home, healthβ¦) with required fields and documents. Signup seeds sensible defaults.
- Each turn, the model gets an extraction tool built from those fields and fills what it can.
- Grounding: every extracted value must be supported by what the customer actually wrote. A policy number, plate or date that isn't in the customer's messages is dropped before anything is shown or saved. Each field kind (ID, phone, date, number, free text) has its own matcher.
- The partial claim (the "draft") is carried in message metadata, so the next turn continues from it. The bot asks only for what's still missing.
- When the draft is complete, the bot reads it back and the customer replies YES, NO (to change something) or CANCEL.
- The claim is created with a number, the photos sent during the conversation are attached, a PDF is generated, and the team sees it in the dashboard.
Why the grounding step: LLMs love to "helpfully" complete a policy number. In insurance, a made-up policy number is worse than a missing one. I also had to handle real-world mess: approximate dates ("last Tuesday, I think"), and customers correcting themselves halfway through ("actually it was my wife's car, a 2014 Honda Fit"). In that case the vehicle fields are replaced together rather than mixed.
Fifteen languages, and being honest about them
The dashboard and the assistant support 15 languages: English, French, Portuguese, Spanish, Arabic (right-to-left), Swahili, Lingala, Hausa, Yoruba, Amharic, Hindi, Bengali, Vietnamese, Indonesian and Filipino. By default the assistant replies in whatever language the customer writes in, not the company's.
Detection is a small deterministic heuristic rather than another model call: non-Latin scripts decide immediately, and Latin-script languages are scored with per-language stopwords ("mbote", "nalingi" and "motuka" for Lingala, "habari" and "ajali" for Swahili). When it isn't sure ("ok", an emoji, a number), it returns nothing and keeps the current language.
To be honest: English and French are reviewed. The other 13 are machine-translated and marked "community review needed" in the language picker. PDFs are fully translated in English, French, Spanish, Portuguese and Swahili; other languages get an English PDF for now. If you're a native speaker, reviewing a locale is two JSON files and a checker script.
Humans stay in charge
Every conversation is visible in the dashboard. Staff can pause the bot on a conversation and reply themselves. While it's paused, automatic WhatsApp status updates for that customer are held back, so the customer doesn't get a bot message in the middle of a human conversation. When staff change a claim's status, the customer is told on WhatsApp, with the updated PDF.
Each WhatsApp number is one customer record, and returning customers are recognised, so one person can have several claims over time.
Why open source, and why AGPL
Insurers in emerging markets often need customer data to stay in-country, and many can't justify an enterprise contract to try something new. Open source fixes both: run it yourself, free. AGPL-3.0 means anyone offering a modified version as a service has to share their changes. I fund the project with a hosted version and help with setup, because WhatsApp Business onboarding is painful.
Beyond claims
Underneath, RapidOS is a WhatsApp support desk with structured intake. Banks, telecoms, utilities and clinics in emerging markets have the same "messages on a personal phone" problem. Claims are just where I started.
Try it
git clone https://github.com/raphmwanza/RapidOS-open-source.git rapidos
cd rapidos && cp .env.example .env
docker compose up -d --build
# open http://localhost:3000/signup
- Repo: https://github.com/raphmwanza/RapidOS-open-source
- Docs and hosted version: https://rapidos243.com
If you've built on the WhatsApp Business API or worked in support in emerging markets, I'd really like to hear what I got wrong.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.