Gunshop.vn
Customer care that runs itself on Zalo — ten journeys, nobody pressing send.
export const project = {
type: "fullstack",
stack: [
"n8n",
"Zalo ZNS",
"PostgreSQL",
"Python",
"Haravan API",
"Smartgift middleware",
"Chrome extension",
"Telegram alerts",
],
shipped: true,
};- Year
- 2026
- Client
- Gunshop.vn
- Role
- Solo engineer
- Duration
- 5 months
Architecture
The story
Before this existed
Gunshop.vn sells airsoft and outdoor gear. The shop already had a list of past customers, an order history, even a loyalty balance — but once a parcel was delivered, nobody said anything to that customer again.
A first-time buyer got no thank-you. Someone on their tenth order got exactly what someone on their first got. An abandoned cart got no nudge. Anyone drifting away simply drifted away.
Doing it by hand was never realistic: dozens of orders a day, every customer sitting at a different point in time, and staff who still have a shop to run.
What got built
Customer care that runs itself on Zalo — the channel Vietnamese customers actually open, unlike email.
Customers are sorted by how they buy: first-time, returning, VIP, gone cold, and people who created an account but never ordered. Each group gets its own path laid out over days. When a customer reaches a milestone, that milestone's message goes out on its own, carrying a discount code made for that one person.
Ten care journeys run in parallel without anybody pressing a button: thank-you after a purchase, abandoned-cart nudges, tier offers, win-backs for customers gone quiet, birthday greetings, and reminders before loyalty points expire.
Three decisions worth the money
Never send twice. A customer placing three orders in one evening gets one thank-you. In the first 33 hours alone, the system blocked 9 messages for this reason. A blocked message is a lost touchpoint, but an annoyed customer is a lost customer.
Never send at the wrong hour. Care messages only go out between 6am and 10pm. Anything falling outside that window queues and goes in the morning.
Never promise something that doesn't exist. A discount code has to be created on the shop's real system before any message mentions it. If creating it fails, the message is held back and Telegram is told immediately. Better a customer gets no message than a code that does nothing when tapped — the second one is what people remember.
Codes were also cut to 9 characters with the easily-confused ones removed (0, 1, I, O, U), so a customer reading one out over the phone doesn't get it wrong.
Results
The first 33 hours live: 200 messages delivered, an 87% delivery rate. All 30 that did not arrive were customers whose number has no Zalo account or whose account had gone dormant — not system failures.
It has run continuously since, now past 165,000 automated runs, without a single manual intervention needed to get messages out.
What the shop can see for itself
An admin of its own, no need to ask anyone:
- which messages each customer received, and when
- the exact wording of the message that went to that customer
- the cost of sending set against the revenue from discount codes, code by code
- a button to re-run a data sync when needed
The system also watches itself: every half hour it checks whether data has gone stale, whether an unusual share of messages failed, whether any discount code failed to be created — and sends a Telegram alert, so a person knows before a customer does.
The hardest part wasn't technical
Zalo reviews every message template before it can be sent. Each one waits hours to days, and changing a single word makes it a new template waiting from the start. No drafts, no A/B testing.
Which means the wording has to be right the first time, and designed not to need changing: a small set of templates shared across groups, with the variable bits carried by variables — customer name, order number, discount code, how many days it lasts. 36 templates written and submitted.
It also means the customer grouping had to be settled before the templates were written. Getting the grouping wrong doesn't just mean rewriting — it means resubmitting and waiting all over again.
Diagrams & screens
Two ways in: a fresh order goes straight through, while date-based milestones run on a schedule. Both meet at the same set of checks before anything is sent.
Each dot is a pre-approved message. The hollow dot at D150 sends nothing — it tags the customer cold and hands them to the next journey.
The last check is the expensive one: the discount code has to exist on the real sales system before any message names it.
Changing one word means a new template and a new wait — so the copy had to be designed not to need changing.

The thank-you sent right after an order. On the left, what the customer sees in Zalo; on the right, what the shop sees: which template, what it cost, which code was issued. Name, phone and order number are masked.

The same customer, fourteen days on: a free-shipping offer with its expiry written into the message, so nobody has to ask whether the code still works.