E-commerce · AI
Onvela
A cosmetics retailer whose first line of support is an AI bot. It does not recite the FAQ: it finds the order, checks delivery and payment, and answers from data.
- Role
- Backend, integrations, AI bot
- Timeline
- 8 weeks
- Stack
- NestJS · PostgreSQL · Qdrant
What changed
- 58%
- of tickets close without an agent: the bot finds the data, answers and, where needed, carries the action out.
- 27 minutes → 18 seconds
- to a first useful answer. The twenty-seven minutes were what a peak-hour queue cost.
- −41%
- load on the first line of support.
- 4.1 → 4.6
- CSAT for automated conversations, out of 5. The score rose not at launch but across two months of adjustments after it.
The CRM and the helpdesk were left alone: the queue, the ticket history and the familiar screens stayed where they were. The bot did not replace the second line — what it cannot close goes to a person with the context attached, and that is still more than a third of the tickets.
Before
Support took in around 90,000 messages a month, and close to two thirds of them were the same question in different words: where is my order, can the address be changed, has the refund gone out. Answering one meant an agent opening as many as six systems — the catalogue, the CRM, the OMS, the loyalty scheme, the courier’s portal and the helpdesk itself. At peak hours the wait for a first reply reached 27 minutes.
- Around 90,000 messages a month arrived at the first line. Every one of them was read by somebody, and every one was answered by hand.
- 64% were repeat questions: the same wording, the same route through the systems, a different order each time. Two thirds of the queue was work already done the day before.
- The average wait for a first reply at peak hours reached 27 minutes. That is how long a customer whose parcel had not arrived waited to be told where it was.
- As many as six systems were opened by an agent to answer one question about an order, a delivery or a refund. The answer was not difficult; it was slow.
What we built
Facts over APIs, rules through RAG: order status, parcel movement and payment are fetched at the moment the question is asked, while the shop’s instructions reach the model as retrieved fragments.
One customer instead of five records: the identifiers held by the catalogue, CRM, OMS, loyalty scheme and couriers are mapped together — every answer about an order starts from that table.
Actions kept apart from answers: refunds, cancellations and address changes run as deterministic steps with the customer’s confirmation, rather than as something the model phrases.
Outcome
The bot holds the first line: 58% of tickets close without an agent, and a first useful answer arrives in 18 seconds where the peak-hour wait used to be 27 minutes. The CRM and the helpdesk are unchanged, agents work in the same screens as before, and whatever the bot cannot close reaches them with the thread, the order it found and the delivery status attached. First-line load is down by 41%; what remains is still worked by people.
The client
An online retailer of cosmetics and personal care goods, with its own warehouse, its own loyalty scheme and delivery through outside couriers. The easiest measure of its size is support: around 90,000 messages a month.
A customer’s question is almost always about one particular order, and the answer to it does not sit in one place: the item is in the catalogue, the order in the OMS, the buying history in the CRM, the parcel’s movement with the courier, and the loyalty scheme knows that same customer under a number of its own.
The brief
- What the client asked for
- Build an AI bot that answers from the knowledge base: load the shop’s help pages into it and take questions about delivery, payment and refund terms off the agents.
- What the brief turned into
- Knowledge-base answers covered only a small share of the load. Customers were not asking about policy, they were asking about their own order: why has it still not arrived, can the address be changed, has the refund been sent yet. Answering that takes more than understanding the text — it takes identifying who is writing, finding their order, checking the status with the courier, looking at the payment and working out what the shop’s rules permit. The FAQ bot became a first-line AI agent.
Why the obvious route was closed
None of these conditions was created by the project. The catalogue, CRM, OMS, loyalty scheme and couriers were already running, the shop’s rules were already changing, and the agents were already working in their own helpdesk. Every one of them is visible in the architecture.
- Five systems, five identifiers
- The catalogue, the CRM, the OMS, the loyalty scheme and the couriers each name a customer and an order in their own way. Until those are matched, “where is my order” has no addressee: whose order it is, and which one, is not known.
- Refund rules keep moving
- They are revised regularly, and anything learned goes stale in silence: the model answers confidently and wrongly, and the signal that it did is a complaint. So not one shop rule went into the model — the rules reach it at the moment of the question, out of the knowledge base.
- The facts live outside and go stale on their own
- Order status, parcel movement and the state of a payment sit in other people’s systems and change there, without anyone telling us. They cannot be learned and cannot be cached for long — only asked for over an API at the moment of the question.
- Money and changes to an order
- A refund, a cancellation or a new delivery address is an action rather than an answer. An awkward sentence is repaired by the next one; a refund issued in error is not repaired by anything. Those steps could not be left to the model’s judgement.
- The CRM and the helpdesk are not ours
- Nobody planned to replace them: the queue is there, the ticket history is there, and that is where the agents are used to working. The bot had to fit into someone else’s process, and hand a conversation over inside it rather than beside it.
- Three ways into one conversation
- The website, WhatsApp and Telegram recognise the person on the other end differently, while the answer and the set of permitted actions have to be identical. Otherwise the same customer gets two different answers depending on where they wrote.
How the work ran
- 01
Reading the tickets
Week 118,000 historical tickets were read through and sorted by request type. What came out was not a list of topics for the knowledge base but a list of actions: what the bot has to be able to find, and what it has to be able to do. It also showed how much of the queue was not about policy at all.
- 02
Integrations
Weeks 2–3APIs into the CRM, orders, payments and logistics, plus the mapping table that reduces a customer and an order to a single record. Two weeks of work, all of it before the bot: without data a bot has nothing to answer with beyond the help pages.
- 03
Knowledge base and RAG
Week 4The shop’s instructions and help pages were broken into fragments and put into Qdrant. The model receives them alongside the question rather than holding them: an edit to a refund rule shows up in answers straight away, with no retraining and no release.
- 04
The bot and its action scenarios
Weeks 5–6This is where two different things parted company: the answer the model assembles from what it found, and the action it only proposes. An address change, a cancellation and a refund became written-down steps that wait for the customer to confirm.
- 05
Website, WhatsApp, Telegram
Week 7Three channels wired to one service: the rules, the scenarios and the escalation threshold are shared, and only the way a person is recognised and the message format differ. A conversation started in a messenger lands in the same helpdesk queue as one started on the site.
- 06
Pilot and escalation
Week 8An A/B pilot: part of the traffic went through the bot, part the old way. What was tuned was not the wording but the boundary: the point at which the bot has to hand the conversation to a person. That boundary decides whether automation saves time or adds a lap of waiting before the agent.
Technical decisions
Each part covers its own ground and stays off the neighbour’s: NestJS talks to other people’s systems and runs the scenarios, PostgreSQL keeps the identifier mapping and the conversation history, Qdrant hands over the knowledge-base fragments that fit the question.
Facts over APIs, rules through RAG
Not a single figure in an answer comes from the model. Order status, parcel movement and payment are requested at the moment of the question; the shop’s instructions arrive as fragments out of Qdrant. The split follows ownership: the order data and the shop’s rules both change on the client’s side, and neither of them belongs inside the model.
A mapping table instead of guesswork
The first step in any answer about an order is working out whose it is. The five systems’ identifiers sit side by side in PostgreSQL, and the bot assembles the customer from them before it asks anything about the order. Without that step it would answer about somebody else’s order just as confidently.
Actions as deterministic scenarios
The model recognises the intent and gathers the data; from there the steps are written down — show exactly what will change, wait for the customer to confirm, carry it out, record the result. Refunds, cancellations and address changes are not handed to generation at any point.
Handover with the context attached
When the bot cannot close a question, the agent does not receive an empty thread in the helpdesk: the conversation, the order it found, the delivery status and whatever it managed to check all arrive with it. Otherwise automation would cost the customer a lap of waiting and leave the agent starting from scratch.
One service behind three channels
The website, WhatsApp and Telegram are three doors into a single NestJS application. Rules, scenarios and the escalation threshold are described once, an edit reaches all three at the same time, and what the shop promises does not depend on where the customer read it.
What stayed outside the scope
Some of the work stayed deliberately on the client’s side, and some of it lies beyond the first line.
Agent-facing interfaces. We delivered the conversation and its context into the existing helpdesk, but built no screens for staff and rewrote none of the second-line process.
The second line. Anything the bot does not close is worked by a person under the shop’s rules: it is the first line that was automated, not support as a whole.
The content of the knowledge base. The client writes it and edits it. Our part was making sure an edit reaches the answer without a release, not what the edit says.
Refund and exchange policy. The shop sets it and the scenarios carry it out, which is why changing a rule stays an edit in the knowledge base rather than a development task.
In progress: the escalation boundary. It is being moved on the same data that took CSAT for automated conversations from 4.1 to 4.6 over two months after launch — the tuning did not end with week eight.
More projects
Fintech
Tallim
Matching payments to invoices inside a payments company’s finance team: strict rules, similarity and a model each take their turn, and a person confirms what stays unclear.
31,000 payments
a month go through reconciliation; 1.9% of them reach a person
Python · PostgreSQL · OpenAI API
Construction
Prokta
Tender documents read for a general contractor’s commercial department: an archive of files goes in, a report of terms comes out, every line of it with a page reference.
91%
of the control sample’s critical terms the system found on its own
Python · Qdrant · OpenAI API
Contact
Send a description of the task
A reply with the scope, the timeline and a budget estimate comes within 24 hours.
The first call is 30 minutes, with no commitment on your side.