
AI Concierge Across a Regional Hotel Group — 370 Leads, 92-96.8% Self-Service, Second Property Live in Half the Time
An AI concierge live across Facebook Messenger, LINE, and the brand website for a regional hotel group — 370 cumulative booking leads with no down week, 92-96.8% of conversations handled without a human, and a second property live in 7 days against the first one's 14.
All Case Studies
Auto Financial OpsAutomated Expense Entry into PEAK — 12 of 12 Bill Types Read Correctly, 45 of 48 Real Documents Passed
AI ChatbotOne Owner, 50 Chats a Day — a LINE AI Bot That Took 90% of Them Across 3,260 SKUs
Auto Financial OpsSix Legal Entities, One Message, Every Morning at 07:00 — Multi-Property Hospitality Group
Auto Financial Ops873 Students Off a 900-Parent Group Chat — and 293 Families Found Sitting Under a Silent Do-Not-Chase Flag
Auto Financial OpsA Clinic Group Found Its Own Revenue Report Overstated by 135% — 21.4 Million Against a Real 9.1 Million
AI ChatbotLINE AI File Memory — 30-Second Searches Cut to Under One Second, Delivered 23 Days Early

A Clinic Group Found Its Own Revenue Report Overstated by 135% — 21.4 Million Against a Real 9.1 Million
Full Case Study
The Client
A specialist medical and aesthetic clinic with 2 branches and a third opening, taking payment through several channels at once — cash, payment gateways, cards through banking apps, and e-wallets.
The difficulty is that every channel reports differently. Some do not report daily. Some are up to a week behind. And no two of them share a cut-off day.
So the accounts team could not reconcile receipt by receipt. They reconciled in weekly lumps instead — a state the owner described in her own words as:
"Risky, and possibly not trustworthy."
The Challenge: 956 Lines, but Only 591 Real Receipts
Before quoting, 18 days of the clinic's real export were requested — and actually read, line by line, rather than skimmed.
The report comes out per line. Everyone totals it as though it were per receipt. The file held 956 lines representing 591 actual receipts, because one customer buying several items produces several lines. 192 receipts occupied more than one line — the longest ran to seven — and the payment amount is reprinted identically on every line of the same receipt.
Anyone who drags a SUM down that column gets an immediately inflated number:
| Channel | Straight column total | Real amount | Inflation |
|---|---|---|---|
| Gateway A | 14,689,198 | 5,332,002 | +175% |
| Gateway B | 664,500 | 244,500 | +172% |
| Banking app | 2,371,110 | 1,203,320 | +97% |
| Gateway C | 1,687,485 | 900,625 | +87% |
| Store credit | 185,500 | 109,500 | +69% |
| Cash | 1,844,038 | 1,331,584 | +38% |
| Total | 21,445,332 | 9,125,030 | +135% |
The channel labels cannot be trusted either. The channel holding the most money — 5.3 million baht — was tagged "bank transfer". The report that provider actually sends is a credit card report, with merchant fees and card types spelled out. Meanwhile the clinic system's own credit-card fee column was zero across all 738 receipts.
The client spotted this herself, sitting and going through the file together, and annotated her own copy: "check the channel mapping — this one is a card."
The lesson became a design rule: never build any rule that trusts a channel label.
There is no shared key between the two systems. This was the single biggest technical obstacle:
| The clinic system | The payment provider's report |
|---|---|
| Key = a payment document code | Key = a transaction number |
No field joins those two together. Matching has to lean on date plus amount, which cannot be perfectly accurate by its nature.
Data that looks like money and is not. The same review turned up entries that skew the total the moment they are counted:
| Item | Count | Why it must be excluded |
|---|---|---|
| Store credit | 174 receipts (109,500) | Never reaches the bank account |
| Unpaid balance | 19 receipts | Has not been paid |
| Using a pre-purchased course | 51 entries, value 0 | No money moves |
| Deposits | 162 receipts | Settle against a later balance, so they must be carried forward |
What it costs to leave alone. A revenue figure inflated by 135% does not stay in a file. It is the number used to decide on a third branch, to assess how the team is performing, and to talk to the bank. The longer it is used, the harder it is to unwind.
The Price Went Up and Then Down, and Why the Reduction Was Offered
The client arrived with a clear problem and set the priority herself in one sentence: "receipts first."
Why build a system rather than hire another person:
- •She wanted AI on the mechanical work and people left for the exceptions — her words: "put a process in with AI at certain points, and let the team fix the points AI cannot do."
- •She was more worried about coverage than price — "I am afraid it will not cover everything. Is there anything touching the bank side that still needs doing?"
- •She wanted somebody who would advise back, not just take orders — "if anything is not covered, I would like you to recommend it too."
Three Things Found in the File That Made the Scope Smaller, Not Bigger
Reading the real data did not produce a bigger sale. It shrank the job in three places, and all three were reported:
One — there are 5 channels to handle, not 10 to 20. The client was worried she had 10 to 20 payment channels, which would make the job large and expensive. In the real data, most of those columns are zero from top to bottom. Only 5 channels carry actual money. She was told the quoted price already covered every channel in real use, and that it would not rise with the number of columns.
Two — connecting to the accounting software was cheaper than quoted. One tab in her own file was an import template for the accounting package, already filled in with 1,245 rows. That meant no system integration was needed at all — just producing a correctly formatted file. That line of the quotation was reduced.
Three — the accuracy figure was given before the sale, not after. Because there is no shared key between the two systems, the match rate was measured against her real sample and stated plainly up front: roughly 75-90%, with the remaining 10-25% needing a person to check. And it was also stated that the sample used to measure it was only about 30 rows, so it could not yet be treated as a final number.
The Price Moved Four Times, and the Client Moved It
| Round | Who moved it |
|---|---|
| Opening | The client's own budget — below the published minimum, so it could not be accepted |
| Adjusted | Scope reduced to fit the published minimum |
| Expanded | The client asked for the full scope — five channels, every branch |
| Settled | The client came back and asked to start with a smaller first phase |
What her messages actually said: she was not rejecting the full-scope price. She was afraid of a total with no ceiling.
So the right answer was not a discount. It was a ceiling. A cap for all three phases was written into the quotation rather than mentioned loosely in chat — so if phase 2 turns out harder than a 30-row sample suggested, that is a scope conversation, not a price rise.
Her reply: "Then let us do this together."
The Solution: Count the Money at the Receipt, Not at the Line
One idea holds it up: collapse the data at the receipt number, then have the system prove itself every day by adding the per-channel amounts back up and comparing them to that receipt's total.
If they stop matching on any given day, something upstream has changed — and the system reports it itself, rather than waiting for a person to notice.
The Plan, in Seven Working Days
| Step | The work | Evidence it produced |
|---|---|---|
| 1 | Analyse 18 days of real data for duplicated structure | The 956 to 591 table, plus per-channel figures |
| 2 | Write readers for both file formats — on-screen report and automated feed | Both formats read correctly |
| 3 | Three-layer reconciler, channel classifier, exclusion rules | Per-channel totals must equal receipt totals |
| 4 | Five report views | Seven tabs in a shared sheet |
| 5 | Daily summary into the team chat | Summary card plus a fallback message |
| 6 | Test against two consecutive days of real files | 109 of 109 cases passed |
| 7 | Read the results back from the destination | Every row read back and checked |
The Technical Decisions
| Problem | The decision | Why |
|---|---|---|
| Payment amount repeated on every line of a receipt | Collapse at the receipt number, not the line | Confirmed against all 192 multi-line receipts, with no exceptions |
| Need to know the moment a total goes wrong | Add every channel amount back against the receipt total, every receipt, every day | If it does not match, something changed — the system says so itself |
| Channel labels cannot be trusted | Build no rule that relies on a label, and report the mismatches separately | It is a problem at source, not something to fix quietly inside our system |
| No shared key between the two systems | Match on date plus amount, and accept that a portion needs a human check | State the expected rate before the sale, not after |
| Fees already deducted from the amount received | Match on the gross amount, and use the fee as a second check | Both sides carry a fee figure, so they can confirm each other |
| E-wallet entries are deposits already settled | Separate them at the reporting layer, not the checking layer | The source system counts e-wallet in its totals, so deducting at the wrong layer fails every receipt that used a deposit |
| Entries that look like money and are not | Four exclusion rules — store credit, unpaid balance, course usage, deposits | Without them the totals are wrong from day one |
| Some gateway reports arrive a week late | Phase 1 covers the receipts side only, with no gateway matching yet | Exactly matching the client's own instruction: "receipts first" |
What Was Delivered
- •A reader for both report formats
- •A line-to-receipt collapser with duplicate detection
- •A per-channel versus receipt-total check, on every receipt
- •A channel classifier plus four exclusion rules
- •Five report views across seven tabs — daily, clean, exceptions, channel-label mismatches, and a monthly archive
- •A daily summary into the team chat
- •A 109-case test suite
The Results
| Before | After |
|---|---|
| Column total from the report = 21,445,332 baht | Real amount = 9,125,030 baht over 18 days |
| 956 lines counted as 956 transactions | 591 receipts, collapsed correctly |
| Believed there were 10 to 20 payment channels | 5 real ones — the rest are zero from top to bottom |
| Card money recorded as a transfer, with nobody aware | 738 receipts identified, reported separately |
| Reconciled in weekly lumps | Reconcilable receipt by receipt, with an exception queue for a person |
1. The revenue figure being used to make decisions was overstated by 135%
The clinic had been totalling straight from its own system's report. Analysis of 18 days of real data found the number inflated by 135% — 21.4 million against a real 9.1 million.
That is the same number used to assess performance, to plan a third branch, and to talk to the bank.
2. Verified by checking every qualifying receipt, not a sample
Collapsing lines into receipts is only safe if the payment amount really is repeated identically on every line.
So all 192 receipts with more than one line were checked, and 0 exceptions were found.
The new figure is not an approximation. It comes from a rule proven against the entire set of real data.
3. The clinic's software vendor shipped a new feature within 3 days
At the start of the project the vendor had no scheduled export and would not give a timeline.
After the issue was escalated with evidence that the channel data was incomplete, the vendor released gateway-level fields within 3 days.
The clinic gets permanently better data at no extra cost — and so does every other customer of that vendor.
The Story That Almost Became the Headline, and Why It Was Cut
The client asked whether any receipts had a payment amount that did not match the value of the items.
The first pass found 14 receipts totalling 4,142,175 baht that did not match — a number large enough to lead the whole report.
But it was re-checked before sending, and it collapsed entirely:
| What the re-check found | Result |
|---|---|
| 12 of the 14 | Ordinary unpaid balances, not errors |
| A further ~100 receipts with fee gaps | Store credit, used normally |
| What actually remained | 2 receipts marked paid but paid below the item value |
From 4.1 million, down to 2 receipts.
The 4.1 million figure was thrown away and only the 2 provable receipts were reported — and that caution became the selling point instead of the headline.
A big number that has not been re-checked is not a discovery. It is a debt that comes due the moment the client checks it themselves.
A Note: the Bug This Project Exists to Fix, Caught by Measuring Itself
On another day of data, collapsing the lines turned 45 lines into 32 receipts and matched the payment total of 458,218.00 baht exactly.
On that same day, a straight column sum inflated one gateway by 3.2 times and the banking app by 1.9 times.
Voices From the Project
"Risky, and possibly not trustworthy."
— The owner, describing her own existing reconciliation process
"Put a process in with AI at certain points, and let the team fix the points AI cannot do."
— The owner, setting the scope
"She was not rejecting the price — she was afraid of a total with no ceiling. So the answer was a ceiling, not a discount."
— The decision note, written when the cap went into the quotation
Lessons Learned
- •Read the client's real export before quoting, not after winning. Eighteen days of real data shrank this job in three separate places and cut a line off the quotation — and it also produced the finding the whole project now rests on.
- •A report that prints per line will be summed per line by somebody. The payment amount repeated identically down every row of a receipt is not a bug in the source system, but it guarantees that a spreadsheet total is wrong, and nothing anywhere says so.
- •Never write a rule that trusts a label. The channel holding the most money was labelled a transfer and was actually card, and the fee column that should have proved it was zero on all 738 receipts. Mismatches got their own report rather than a quiet correction inside the system.
- •A big number that has not been re-checked is a liability, not a finding. Fourteen receipts worth 4.1 million became two receipts under a second pass. Reporting the 4.1 million would have been the most impressive and the most expensive thing in the project.
- •Give the accuracy range before the sale, with the size of the sample it came from. There is no shared key between the two systems, so a perfect match rate was never possible — saying 75-90% up front, measured on about 30 rows, is worth more than a precise-sounding number nobody can stand behind.
- •Deduct at the right layer. E-wallet deposits are counted in the source system's totals, so removing them at the checking layer instead of the reporting layer would have failed every receipt that used a deposit.
- •A ceiling answers a fear that a discount does not. The client was not resisting the price; she was resisting an open-ended total. Writing the cap into the quotation rather than saying it in chat is what closed it.
Not Sure the Sales Figure You Are Looking At Is Real?
If any of these describes your business, the number you are working from may be more inflated than you think:
- •Your system exports per line, and you total the whole column
- •One customer buying several items on one bill is normal for you
- •You take money through several channels, and each report arrives in a different format on a different cycle
- •Month-end closing runs a week late because the totals never agree
- •The accuracy of your numbers depends on one member of staff
Send a month of your export and we will tell you what the real figure is.
Project Information
More Like This
AI ChatbotAI Concierge Across a Regional Hotel Group — 370 Leads, 92-96.8% Self-Service, Second Property Live in Half the Time
96% MatchAn AI concierge live across Facebook Messenger, LINE, and the brand website for a regional hotel group — 370 cumulative booking leads with no down week, 92-96.8% of conversations handled without a human, and a second property live in 7 days against the first one's 14.
AI ChatbotFive Months After Handover — 96.5% Handled Without a Human, Worth 2.4 Times Its Upkeep, and Leads Recovered from 6 to 23
96% MatchA LINE AI assistant for an occupational safety training institute, built in 7 days and then measured for five months. It handles 96.5% of its conversations without a human and returns 2.4 times what its upkeep costs — and the maintenance habit behind those numbers found 15 of 16 leads quietly going missing, staff alerts dying halfway through every month, and a customer who sent 15 messages without a reply. None of it ever raised an error.
AI ChatbotA 245-Item Catalogue Bot That Would Rather Say Nothing Than Guess a Price — a 156,000 Baht Error Caught Before Go-Live
96% MatchA price-quoting bot on LINE and Facebook for a building-materials distributor with 245 products whose model codes overlap. Every price it is about to say is checked against the catalogue first — if it does not match, the bot stays silent and alerts a human instead. That guard caught a pricing error worth 156,000 baht on a single 1,000 sqm job, two days after the build had already passed 10 out of 10 acceptance tests.