Build notes
Before we asked anyone to pay us
There is a moment in every software relationship when the customer stops evaluating and starts trusting. It is not the demo. It is not the trial. It is the moment they pay, and then wait to see whether what they bought is what arrives. We had built that entire moment and never once watched it happen. So before putting anyone's money through it, we opened it up.
We found five things wrong. One of them is worth the whole article.
The customer who never complains
A jeweller signs up for Growth. She pays for three WhatsApp lines and fifteen thousand contacts, because that is what the page said and that is what her shop needs: one line for the store, one for the workshop, one for the owner.
She goes to add the second line. The software says no.
She does not open a support ticket. She assumes she has misunderstood the plan, or that the feature is coming, or that this is simply what these tools are like. She works around it. Six weeks later, when someone asks her about Klaros, she says it is fine, a bit limited.
That was a real defect in our code. Every paid plan key resolved to the entry tier's capabilities, so a customer paying ₹17,988 a year for three lines and fifteen thousand contacts would have been provisioned one line and two and a half thousand. Silently. No error, no warning, nothing to complain about.
We have a name for this, and we usually apply it to somebody else. Digital distance is the gap between what a person believes you know about them and what your software actually knows. A customer messages a shop that has served her for three years and gets treated like a stranger. She feels the distance. She does not mention it. She just comes back a little less often.
The jeweller above is that same gap, with us on the wrong side of it. She believed she had bought three lines. Our software silently gave her one. No error, no argument, nothing to escalate. Just a quiet difference between what she was promised and what arrived, sitting there permanently, doing its work.
The other four
The rest of the audit found four more, all live, none of which had ever been noticed for the same reason: no money had ever moved through the system that contained them.
| What was wrong | What it would have done to a customer |
|---|---|
| The payment webhook verified signatures with its arguments in the wrong order. | Every payment confirmation rejected. You pay, and nothing happens. You email us, and we tell you it takes a moment. |
| Subscription status mapping treated anything unfamiliar as "active". | A cancelled account keeps working. Comfortable, until the day it is not, and nobody can say when that day is. |
| A missing plan configuration quietly fell back to a different plan. | You choose the ₹699 tier and get billed on a ₹17,988 one. Nothing in the flow tells you. |
| Two provider plans charged less than the price on our own pricing page. | Nothing, to you. It was our money, not yours. Which is precisely why it could have gone on for years. |
That last one deserves a moment. Two plans in our payment provider were charging ₹7,188 and ₹999 while the product advertised ₹17,988 and ₹2,499. An error in the customer's favour is the kind nobody reports, which makes it the kind that survives longest. It also told us something uncomfortable: we had no way to know what we charge. The page renders one number from our catalog. The provider charges another from its own records. Nothing had ever compared the two, because in most software stacks nothing ever does.
What we did about it
The fixes are two-line changes and not very interesting. Two things came out of this that are.
The first is that every plan now declares the capabilities it grants, and a test fails if a plan resolves to anything else. What you were quoted and what you are given come from the same definition. There is one Growth, not three.
The second is a small tool that reconciles what the payment provider charges against what we publish. It runs in seconds and refuses to pass quietly:
$ node tools/verify-billing-plans.mjs
Razorpay account: LIVE
✓ klaros_founding (Founding Partner Launch Tier)
charges ₹8,388 yearly. Matches the catalog.
✓ klaros_annual (Growth Cloud (Annual))
charges ₹17,988 yearly. Matches the catalog.
✓ klaros_monthly (Growth Cloud (Monthly))
charges ₹2,499 monthly. Matches the catalog.
All 3 plans charge what the catalog advertises.
It is fifteen lines of logic. It would have caught two of these five on the first day they existed. We built it because a business should not have to take our word for what we charge, and neither should we.
The same audit turned up a smaller one worth naming, because anyone building WhatsApp payments in India
will meet it. A payment request inside a chat is Meta's order_details interactive message,
and its payment_type: upi path is India-only and INR-only. We had a product priced in
dollars that would have been assembled into that message quite happily and then rejected by Meta with
something unhelpful. It now refuses at our end with a sentence a human can act on: WhatsApp payment
requests support INR only, send a payment link instead. A good error message is also a way of closing
distance. It is the difference between a customer who knows what to do next and one who is left
guessing at somebody else's system.
The forks
Two of these decisions had a version that would have looked better. We took the other path, and it is worth saying why plainly.
We chose: fail the sale.
Refuse to sell, or sell something close enough?
When a plan has no payment configuration, the old code borrowed another plan's. That is the accommodating choice: the checkout always works. It is also how someone ends up on a subscription they did not choose. A missing configuration now stops the sale and names exactly what is absent. Losing a sale is recoverable. Taking money for the wrong thing is not.
We chose: publish the real number.
Show the flattering count, or the true one?
Our pricing page said "4 of 10 slots left" on the founding tier. Nothing counted anything. It was a number someone typed. We built the counter, and the honest answer today is that all ten are open, because nobody has bought one yet. A company whose entire argument is that its claims are checkable does not get to invent scarcity. The number on that page will move when someone actually buys.
What we have not fixed
This would be a dishonest article if it ended at the tidy part.
Contact and line limits are computed correctly on every request and enforced almost nowhere. The tier ladder is, at runtime, still closer to a description than a rule. We know exactly where that gap is and it is next. Add-on capacity has the same shape: correct, stored, and not yet consumed by the code that would make it matter.
And the honest state of the business: everything here is verified by tests and by reconciliation against the live payment provider, and none of it has yet been verified by a stranger's money. Both launch tiers show every slot remaining, because every slot remains. When that changes, this page will say so.
What this is actually for
Klaros exists to close the distance between a business and the people it serves. Every structural decision follows from that. You connect your own WhatsApp Business Account. Meta bills you directly at Meta's rate, with us nowhere in that payment path. Your history exports whole, including on the day you leave. Buy the self-hosted licence and you hold a signed key for software that runs on your own infrastructure and keeps running whether or not we do.
We cannot credibly sell that while quietly running a gap of our own. A vendor's interests and its customer's come apart most easily in the room where the money moves, and almost everything that goes wrong in there is invisible from the outside. So we audited it before anyone's money was in it, and we built the check that fails loudly next time.
What we want a business to get from this is boring, and we think boring is the point. You should never need to audit us. You should not have to wonder whether the plan gives what the page said, whether the bill will look like the quote, whether leaving means a support ticket. The absence of surprise is what nearness with a vendor actually feels like, and it is built the same way nearness with a customer is built: by refusing to let small unspoken gaps accumulate.
Ask our WhatsApp number what it costs
Not a form. Message the line and type pricing. You will get our live catalog as a WhatsApp list, with the real number of slots left, and a payment link on whatever you tap. Buy the self-hosted licence and your signed key arrives in the same thread, for software we cannot reach into. The whole path is the product, demonstrating itself before you own it.
+91 97893 77634 · you message first, so nothing reaches you without your say-so.
Written 31 July 2026. We append when the facts change. Related: the belief this comes from, proving consent, what it costs, all build notes.
