Every Vietnamese seller eventually asks: which carrier should my automation integrate with? The honest answer is that the question is slightly wrong. All four majors — ViettelPost, GHN (Giao Hàng Nhanh), GHTK (Giao Hàng Tiết Kiệm) and J&T Express — are integrable, all have real strengths, and all will disappoint you somewhere. The durable decision is not which carrier to marry but how to build so that switching, or splitting traffic between them, is a configuration change rather than a rewrite.
What actually matters for automation builders
Marketing pages compare price per kilogram. Automation builders should compare different things: how good the API contract is, whether webhooks actually arrive, how COD money comes back, and what happens on the edge cases — rural addresses and returns. Ratings below are qualitative, based on integration experience; every carrier evolves, so verify against current documentation before committing.
| Dimension | ViettelPost | GHN | GHTK | J&T Express |
|---|---|---|---|---|
| API style & docs quality | Functional but dated; docs partly Vietnamese-only | Cleanest developer experience of the four | Workable; conventions take getting used to | Improving; partner-onboarding heavy |
| Webhook reliability | Good, with occasional gaps — reconcile | Good; still reconcile | Adequate; expect duplicates | Variable by region; reconcile aggressively |
| COD remittance cadence | Regular batches, own reference format | Regular, comparatively clean statements | Frequent; strong COD heritage | Regular; format differs again |
| Pickup reliability | Strong nationwide network | Strong in major cities | Good; shop-oriented model | Good urban, variable rural |
| Rural coverage | Best of the four (Viettel infrastructure) | Good, urban-strongest | Good | Wide but uneven |
| Returns flow | Supported; track statuses closely | Supported, reasonably clear | Supported; process-heavy | Supported; verify per region |
Read the table as a map of failure modes, not a scoreboard. GHN's developer experience makes it a pleasant first integration; ViettelPost's rural reach makes it hard to drop entirely if your customers live outside the big cities (and its pickup automation is covered step-by-step in our ViettelPost guide); GHTK's COD-first heritage suits shops whose buyers overwhelmingly pay on delivery; J&T's network keeps expanding but demands the most defensive engineering.
The carrier-abstraction layer
Whatever you pick first, put a boundary between your business logic and the carrier:
- One internal shipment model. Your order service creates a Shipment with a canonical address, parcel dimensions, COD amount and service class (economy/express). It never mentions a carrier.
- One adapter per carrier. Each adapter translates the internal model to that carrier's API — its field names, its service codes, its authentication — and translates carrier statuses back to your canonical status set (created, picked up, in transit, out for delivery, delivered, failed, returned).
- A routing policy. A rule decides which carrier gets which shipment: by destination province, by weight, by COD amount, or by live rate shopping — query two or three adapters for a quote and take the best. The policy is configuration, so "switch carriers" or "send rural COD to ViettelPost, urban express to GHN" is an afternoon, not a project.
The two traps that bite every multi-carrier build
1. Four carriers, four address-code systems
Each carrier maintains its own numeric codes for provinces, districts and wards, and they do not agree with each other. If you store carrier-specific codes on your customer records, you are locked in at the data layer. Build one canonical address model — your own normalized representation of province/district/ward plus street text — and let each adapter map canonical → carrier codes at booking time, against code tables refreshed from that carrier's API. When Vietnam reorganizes administrative boundaries (it does), you update the mappings in one place.
2. Webhook retries need idempotent handlers
Every carrier retries webhooks on timeout, some retry enthusiastically, and out-of-order delivery is normal. A handler that naively appends events will send customers duplicate "delivered!" messages and corrupt your COD expectations. The pattern: derive a stable event key per (shipment, status), upsert rather than insert, compute customer-facing state from the full status history, and make notification sending exactly-once per state transition. The same discipline pays off downstream when you match COD remittances to orders — see our COD reconciliation article for that half of the money flow.
Recommendation
Start with the carrier whose network fits your customer geography — for most sellers with significant rural COD volume that means ViettelPost, for urban-centric shops GHN is the gentler first integration. Build the abstraction layer from day one even with a single carrier behind it. Add a second carrier when you have volume to split or a failure mode to escape, and let rate shopping pay for the work. This is exactly the fulfilment layer of the end-to-end automation stack — and the layer where defensive engineering most directly translates into customers who actually receive their parcels.