WhatsApp Gateway
A Node/TypeScript service that owns a single WhatsApp session, stores every conversation in SQLite and hands scoped access to other projects through a REST API — plus a runner that turns a chat into a working assistant.
- 88k+
- messages stored
- 7
- services sharing one session
- 1
- WhatsApp connection to maintain
- Node.js
- TypeScript
- Baileys
- Express 5
- better-sqlite3
- Docker
- Python
- systemd
The problem
Every project that wanted to send a WhatsApp message was about to grow its own client, its own session and its own way of leaking into chats it had no business reading. One of those is maintainable; five is not.
My role
Sole author — gateway, REST API, permission model, dashboard and the bot runner.
What I built
- 01
A single Baileys session in Docker, with history sync, media download and identity resolution, writing conversations, messages and contacts to SQLite.
- 02
A REST API built for consumers that come and go: cursor-based tailing on a monotonic sequence so a service can disappear for a day and resume exactly where it stopped.
- 03
An agent model instead of one shared secret — every service gets its own hashed token plus per-chat grants for read, send and media, so a store bot cannot read family chats.
- 04
A dashboard for the human side: analytics, chat browsing, and a switch per agent that revokes access instantly.
- 05
A shared Python runner that attaches an assistant to a chat — it answers bursts as one turn, keeps its own memory in git, schedules reminders and jobs, transcribes voice notes, and strips secrets from anything it says.
Outcome
Five assistants and two notification-only services run on it today: a store bot that answers customers, two family group assistants with scoped IoT control, and my own assistant.
A new bot is now an agent, a token and a markdown file — no new integration code.
Nothing but the dashboard is exposed publicly; the public route refuses bearer tokens outright so a leaked token is useless from outside.