How it started
The call started with a sentence I won't forget. "I have an analyst spending the first three hours of every morning copy pasting invoice totals into WhatsApp." She wasn't complaining about the analyst. She was complaining about herself, because she'd let the process turn into that.
This is a B2B services firm running its entire client portfolio out of Odoo. The product is good. The clients are loyal. The cash flow, on paper, should be smooth. But every month a meaningful chunk of revenue was sitting in the "past due" column, and the only thing keeping it from getting worse was one human being doing the work of a small machine.
What they wanted was specific. Don't replace Odoo. Don't build a payment portal. Don't add another login. Just close the loop between "the invoice is overdue" and "the client got a polite, well timed nudge." And do it on WhatsApp, because that's the channel their clients actually answer.
Why the custom WhatsApp API
We didn't go with the official Business API. We built our own thin layer in front of WhatsApp on top of a self hosted bridge, designed to do exactly what this client needed and nothing more. Three reasons.
First, the messages they send are conversational, not transactional. A real human voice. Pre approved templates would have flattened that into something cold.
Second, the volume and shape of the traffic were a poor fit for template fees and approval cycles. We needed to ship a new tone or a new offer in an afternoon, not next week.
Third, integration. The custom API speaks the language of the rest of the system. It knows about invoices, about client segments, about handoffs to a human. Every message that goes out is logged against an invoice in Odoo, in plain language, the same way a sales rep would log a phone call.
What we built
The system has four moving parts. They sound boring on their own and they were designed that way on purpose.
A small Node service pulls invoice state from Odoo through its XML RPC API on a tight cron, mirrors what matters into a local Postgres, and writes events back to Odoo as internal notes on each invoice. Odoo stays the source of truth for the numbers, our system is the source of truth for the conversation.
A reminder engine sits on top of that. Per invoice, per client segment, a small state machine moves from pending to nudge one to nudge two to escalation. Each step picks the right channel, the right template and the right human handoff if needed.
The custom WhatsApp API takes those handoffs and sends. It's not a generic broadcast tool. It's tuned to look and feel like a person following up, because that's what works.
n8n is the connective tissue between all of this. Templates, cadences and routing rules live there so finance can tweak the tone or add a new step without waiting for code.
The week we went live
We rolled out in stages. Smallest client segment first, then the mid tier, then the big accounts. For the first two weeks the analyst still approved every batch by hand. By the third week the approvals had quietly turned into spot checks. By the fourth she was barely looking.
The metric I care about most isn't the 41% drop in DSO, even though that's the one the CFO talks about. It's that the analyst told me, about a month after launch, that she finally had time to call a client back about a real problem they'd been ignoring for weeks. The system gave her back her judgment.
What we didn't do
We didn't replace Odoo. We didn't build a payment portal. We didn't integrate with the banks. We didn't ship a single WhatsApp template that the team hadn't read out loud first. Collections messages misfire badly when the tone is off, and tone is exactly the kind of thing that doesn't show up in a Jira ticket.
The handoff
Three months in, finance owns the cadences and the templates. The analyst who used to do the manual chase now spends her time on payment plans and disputes, the work that actually needs a person. I'm on a small retainer that covers the Odoo sync layer and the WhatsApp service. Everything else is theirs.