The Inbox Is a Mirror: Why I Kept Chatwoot Over ChatbotX
ChatbotX is an open-source, AI-first chat platform built on a stack close to mine. I checked whether it should replace Chatwoot as the human inbox behind Loma's WhatsApp agent. It shouldn't, and the reason is architectural, not a feature checklist.
Loma sells fresh produce boxes in the Dominican Republic, and most of its sales happen in WhatsApp chats run by an AI agent. When the agent gets stuck, or a customer needs a person, someone on the team takes over from a shared inbox. That inbox is a self-hosted Chatwoot.
This week I came across ChatbotX, an open-source "agentic chat marketing platform". It is MIT licensed, self-hostable, and built on Node, Next.js, Postgres and Redis, with AI agents, a flow builder, broadcasts and a shared inbox. That is close to my own stack, and it was built for AI from day one, while Chatwoot started life as a support desk. So I spent an afternoon checking whether it should replace Chatwoot.
The short answer is no. The longer answer is more useful, because it applies to anyone who runs their own AI agent behind a human inbox.
What the inbox actually does
The design choice that matters is that Chatwoot is not where the bot lives. Our API receives every message from Meta, runs the agent, sends the reply and owns the conversation state. Chatwoot is a mirror with a steering wheel:
- Every customer message and every bot reply is copied into Chatwoot, fire-and-forget. If Chatwoot is down, customers still get answers.
- When an operator replies, Chatwoot calls our webhook, and our API sends the message to WhatsApp and stores it in the history the agent reads. When the bot takes the chat back, it knows what the human said.
- The inbox's own gestures are the controls. Typing a reply takes the chat off the bot and assigns it to whoever typed. A private note does the same. A label or a macro moves it between "AI" and "needs a human". Resolve hands it back to the AI.
- The status column doubles as the queue: Pending means the bot has the chat, Open means a person must act.
- The contact card shows what an operator needs at a glance: the ad the customer came from, the state of their order, and the delivery status of each message.
None of that is exotic. What took months is everything around it.
The part that took months
The first version of the handoff took a day. Making it correct took about a hundred commits, many of them fixes for something that went wrong with a real customer on the other end.
- Our own writes echo back. When the API labels a conversation, Chatwoot sends a webhook about that label change, which can read as an operator asking for the opposite. We ended up with a guard that ignores our own echoes for thirty seconds, and a breaker that hands a conversation to a human once it flips owner more than three times in two minutes.
- Events you don't subscribe to are dropped silently. A Chatwoot webhook only delivers the events it opted into. For a while the assignment event wasn't one of them, so claiming a thread never reached our API, and nothing anywhere said so.
- Round-robin doesn't know the bot is a participant. Our bot was a regular user account, and in five minutes round-robin moved a large batch of bot-owned threads onto my name. Chatwoot 4.17 made "a bot owns this thread" a first-class state that round-robin skips, and moving to it fixed that for good.
- Resolve doesn't mean what the button says. Our operators press Resolve to mean "give it back to the AI", not "this customer is done". So resolving hands the chat back and moves it straight to Pending. Before that, conversations vanished from every default view in the middle of a sale.
Each of these lessons now lives in the integration and is pinned by tests: about ten thousand lines of code and nine thousand lines of specs. That is the real asset, and all of it is specific to how Chatwoot behaves.
What ChatbotX is
ChatbotX is serious work, and it moves fast. As of October 2026:
- The community edition is MIT licensed. RBAC and audit logs sit under a commercial license.
- The first public release shipped in February 2026, and version 1.12 in early October, with a new minor release most weeks.
- It connects WhatsApp, Instagram, Messenger, Threads, TikTok, Telegram, Zalo, email, webchat and a generic API channel.
- It has a visual flow builder, broadcasts, drip sequences, comment and story automations, click-to-WhatsApp ad funnels, AI agents on the model of your choice, an MCP server and a CLI.
- Self-hosting is free; cloud plans run from $29 to $499 a month.
Its model is ManyChat's: the platform connects to the channels, runs the flows and the AI agents, and keeps the contacts. It is built to be the brain.
Where it doesn't fit
That is exactly the problem. Loma's rule is that every channel goes through one API, and that API is the single source of truth. The agent runs there, with an evaluation gate for prompt changes and a trace of every turn. If ChatbotX owned the WhatsApp connection and the agent, I would have two brains, and the one I would switch off is the one with the evals.
So the only way to use it would be the way I use Chatwoot: as a mirror. ChatbotX has the right building block for that, an API channel added in August 2026. You push inbound messages in with an idempotent id, and replies come back to a callback URL. But when I went through its docs for the events my handoff code depends on, the gaps were in exactly the wrong places:
- The callback that delivers an operator's reply doesn't document a signature. Any webhook that can make our API message a customer has to be verifiable.
- Turning the bot off or on and assigning a conversation exist as API calls I can make. I couldn't find an event that tells me an operator did them in the inbox, and that event is the whole handoff.
- Notes and tags live on the contact, not on the conversation.
- The external webhooks are aimed at Make and n8n: a free-form event name, no documented payload and no documented signing.
- There's no mobile app for operators yet.
Any of these may be solved by the time you read this. Today, adopting ChatbotX would mean rebuilding the handoff on its least mature surface while ignoring most of what it does well, and paying for it by rewriting the code that holds every lesson above. Hosting wouldn't get smaller either: self-hosted ChatbotX needs Postgres, Redis, object storage, a worker and a realtime server, roughly what the Chatwoot box needs today.
The lesson
If your agent is the product, the inbox is a mirror. Choose the inbox for how faithfully it reports what people do in it, not for how smart it is.
Comparing chat tools feature by feature gets this backwards. ChatbotX has more AI features than Chatwoot, and those are exactly the features I don't need, because the AI lives somewhere else. What I need from an inbox is boring: a faithful copy of every message, a signed event for every operator gesture, a first-class way to say "the bot owns this thread", and native mobile apps so an operator can answer from a phone.
I'll look again when ChatbotX documents signed conversation events on its API channel, covering operator messages with their sender, assignment, the bot turning off and on, and notes. Or when Chatwoot itself becomes the bottleneck. Until then, the boring inbox stays.