Zalo's June 2026 OA Pricing Lands: How One Cross-Border Team Kept Receiving Zalo Messages Without the OA Treadmill — UnifyPort
On June 1, 2026, Zalo’s new Official Account service pricing took effect. For a five-person cross-border team selling skincare from a small office in Ho Chi Minh City, the email landed at the worst possible time: mid-campaign, with two staff already spending most of the day answering customer questions in Zalo and WhatsApp chats. The pricing notice forced a question they’d been putting off — how much is it actually going to cost us to keep doing what we already do?
What they already did was simple. Customers messaged the shop on Zalo. Someone replied. That’s it. No newsletters, no marketing blasts, no automated drip sequences. But the path Zalo lays out for “a business that wants to receive messages programmatically” runs through the full Official Account apparatus — and that apparatus is built for broadcasters, not for a team that just needs to answer the people who message first.
The OA treadmill, and why it didn’t fit
Receiving Zalo messages through the official route means standing up an Official Account, getting it verified, and then living inside the OA’s rules. The interaction window is the one that bites: an OA can message a user freely only inside a limited window after that user contacts you. Step outside it — follow up the next morning, send an order update two days later — and you’re into Zalo Notification Service (ZNS) territory, where each message rides a pre-approved template and carries a per-message fee.
For a broadcaster, that’s a reasonable trade. For a five-person shop, it’s a treadmill. Every template needs to be written, submitted, and approved before it can be used. The June 1 pricing changes re-tiered the OA plans and shifted what falls inside the free quota versus what’s billed — and the team’s read of it was that their genuinely low-volume, conversational usage was about to get more expensive and more procedural at the same time. They weren’t sending thousands of marketing messages. They were answering questions. They were being asked to adopt the cost structure and the approval workflow of a company doing something they simply weren’t doing.
And Zalo was only one inbox. The same two staff were also in WhatsApp all day, which has its own separate machinery — Business Verification, per-message billing, its own template approvals. Two platforms, two completely different sets of rules, two integrations if they ever wanted to automate. The cost wasn’t only money; it was that every channel demanded the team learn a different system before they could do the one thing they actually wanted: see a message come in, and reply.
The reframe: they needed inbound, not an Official Account
The insight that unlocked it was separating what they needed from what the official path bundles. The OA broadcast machinery — templates, ZNS, interaction windows, quota tiers — exists to let a business initiate contact at scale. This team never initiated. Every conversation started with the customer. All they needed was to reliably receive the inbound message and reply inside the conversation the customer had already opened.
That’s a much smaller problem than “run an Official Account.” Inbound-only, reply-in-session usage doesn’t need the broadcast tooling at all. Once they saw it that way, the question stopped being “which OA plan do we buy” and became “what’s the simplest way to get an inbound Zalo message onto our backend and send a reply back into the same chat.”
One webhook for Zalo and WhatsApp
They connected both accounts through UnifyPort’s unofficial inbound interface, which puts every channel behind one normalized webhook instead of one integration per platform. A Zalo message and a WhatsApp message arrive in the exact same shape — the only difference is the provider field:
{
"event": "message.received",
"account_id": "acct_7Hm2pX",
"provider": "zalo",
"from": "user_9a3f21",
"text": "Sản phẩm này còn hàng không ạ?",
"timestamp": 1749513600,
"message_id": "zalo_msg_4c8e1d"
}
Their backend subscribes to one event, message.received, and routes on a single field. The handler that processes a Zalo message is the same handler that processes a WhatsApp message — there is no per-platform branch in the receiving code:
app.post("/webhook", (req, res) => {
if (!verifySignature(req)) return res.sendStatus(401);
const evt = req.body;
if (evt.event === "message.received") {
// evt.provider is "zalo" or "whatsapp" — routing is identical
queue.add({
channel: evt.provider,
customer: evt.from,
text: evt.text,
accountId: evt.account_id,
});
}
res.sendStatus(200);
});
Each delivery is signed, so the team verifies authenticity with an HMAC-SHA256 check against the signing_secret they set on the webhook before trusting any payload — the same verifySignature step regardless of which platform the message came from. Replying is the mirror image: a single POST /v1/messages call that names the account and the recipient. Because the customer messaged first, every reply lands inside the conversation the customer opened — exactly the in-session interaction the team was already doing by hand, now scriptable, with no template to pre-approve.
The practical result was that the June 1 pricing notice stopped being a budgeting emergency. The team wasn’t buying their way up an OA tier to keep answering low-volume, customer-initiated chats. Their two support staff kept working the same two inboxes, except both inboxes now fed one queue, one schema, and one reply path — and adding LINE later, for a Thai supplier, was the same message.received handler again, not a third integration project.
What to take from it
The lesson isn’t “avoid Zalo’s Official Account” — if you’re running broadcast campaigns at volume, the OA and ZNS are built for exactly that, and you should use them. The lesson is to check whether the official path is solving your problem or a bigger problem you don’t have. A pricing change aimed at high-volume broadcasters can quietly raise the cost of simply answering your customers, because the official route bundles both behaviors into the same account.
If all you need is to receive inbound messages and reply in-session, that’s a separable, much cheaper problem. Point your backend at a normalized inbound webhook, subscribe to message.received, and you’ll have Zalo messages hitting your queue without the OA treadmill — and the other five channels arrive through the same handler the day you need them.