+1

EasyPost — Toàn tập: Design / Spec

Hạng mục Nội dung
Ngày 2026-09-17
Trạng thái duyệt
Nguồn tham chiếu chính https://docs.easypost.com/

1. Mục tiêu và người đọc

Viết một tài liệu tiếng Việt đủ để một backend developer chưa từng chạm EasyPost có thể đọc tuần tự từ đầu tới cuối rồi tích hợp được một hệ vận chuyển đa carrier vào production, và sau đó vẫn mở lại được để tra cứu khi gặp sự cố.

Người đọc: backend developer Node.js/TypeScript và kỹ sư tích hợp e-commerce đang dựng hoặc đang chữa tích hợp EasyPost. Yêu cầu: biết HTTP và REST, đọc được TypeScript. Không yêu cầu kinh nghiệm ngành vận chuyển, không yêu cầu biết trước EasyPost.

Tiêu chí thành công. Tài liệu đạt nếu người đọc sau khi đọc xong trả lời được bốn câu mà docs.easypost.com không trả lời trực tiếp:

  1. Thứ tự học các object là gì, cái nào phụ thuộc cái nào?
  2. Chỗ nào là bẫy — thao tác nào mất tiền thật, thao tác nào không được retry, dữ liệu nào bắt buộc phải tự lưu?
  3. Khi cần chọn (SmartRate hay Luma, Wallet hay BYOA, webhook hay polling) thì căn cứ vào đâu?
  4. Khi hỏng thì tra từ đâu?

2. Phạm vi

Nội dung
Bao gồm — đi sâu Luồng vận chuyển lõi: Address → Parcel → Shipment → Rate → mua label → in → Tracker → Webhook → Refund. Cộng Batch/ScanForm/Pickup/Order, cross-border (Customs, TaxIdentifier, EndShipper, landed cost), Insurance/Claims/Return/Fee, SmartRate và Luma, xử lý lỗi và rate limiting, vận hành production.
Bao gồm — dạng tham chiếu gọn Account Management (API Keys, User, Child Users, Brand, ReferralCustomer, Customer Portal, Embeddable Components), White-label Forge và ba mô hình billing, CarrierType / Carrier Metadata, 10 carrier guide. Trình bày dạng bảng tra "cần gì đọc ở đâu", không đi sâu từng field.
Không bao gồm Client library ngoài Node.js (nguyên lý áp dụng được, API thì không). Hướng dẫn dùng giao diện Dashboard, ngoài những chỗ bắt buộc phải cấu hình trên đó (webhook, Luma ruleset, carrier account). So sánh EasyPost với Shippo/ShipEngine/EasyShip. Nghiệp vụ kho (WMS, picking, packing). Báo giá thương mại và điều khoản hợp đồng với carrier.
Ngôn ngữ code TypeScript với @easypost/api 8.9.0, kèm cURL tương đương cho mỗi endpoint.
Mức kiểm chứng Type-check bằng compiler thật, không gọi API thật. Chi tiết ở mục 7.

3. Quyết định đã chốt

Quyết định Chọn Lý do
Độ phủ Lõi sâu, phần còn lại gọn Toàn bộ bề mặt API sẽ dài hơn doc OTel đáng kể, mà phần lớn người đọc không bao giờ chạm tới Forge hay ReferralCustomer.
Ngôn ngữ code Node.js/TypeScript + cURL Thống nhất với doc OTel. @easypost/api 8.9.0 có sẵn types/index.d.ts nên type-check bắt được lỗi thật. cURL đi kèm vì client library giấu mất hình hài HTTP, mà khi debug thì cần thấy nó.
Kiểm chứng Type-check, không gọi API Chưa có test API key. Type-check vẫn bắt được sai tên method và sai shape tham số — lớp lỗi hay gặp nhất khi chép code từ tài liệu.
Ví dụ xuyên suốt D2C nội địa Mỹ + một chương cross-border Bám đúng đường EasyPost hỗ trợ tốt nhất (USPS/UPS/FedEx, origin US). Đẩy customs lên tuyến đầu sẽ làm rối phần lõi.
Bố cục Soi gương doc OTel: 14 phần đánh số Vừa đọc tuần tự vừa tra cứu được. Giá trị nằm ở chỗ có chỗ cho cả nguyên lý lẫn vận hành, không chỉ liệt kê field.
Sản phẩm Một file .md duy nhất Đúng yêu cầu. Không tạo repo tách chương.

4. Ví dụ xuyên suốt

Mọi phần dùng lại đúng một bối cảnh, không đổi tên biến giữa chừng.

Lumière Skincare — shop mỹ phẩm bán thẳng cho khách, kho duy nhất ở Oakland, California.

Thông số Giá trị dùng nhất quán trong toàn tài liệu
Địa chỉ gửi Kho Oakland, CA — dùng làm from_address ở mọi ví dụ
Địa chỉ nhận mẫu Một địa chỉ dân cư ở Austin, TX (ca thường) và một địa chỉ thiếu số căn hộ (ca verify thất bại)
Kiện hàng chuẩn Hộp 9 × 6 × 3 inch, 1.2 lb — đơn 1–3 sản phẩm
Kiện hàng lớn 12 × 10 × 8 inch, 6.5 lb — đơn combo, dùng ở ví dụ so sánh giá theo dimensional weight
Sản lượng ~250 đơn/ngày thường, ~800 đơn/ngày mùa cao điểm — con số này biện minh cho Batch và ScanForm
Carrier USPS, UPS, FedEx bật đồng thời để có gì mà rate shopping
Cross-border Cùng shop đó gửi sang Canada — dùng ở Phần 8, không dùng ở chỗ khác

Ba tình huống hỏng được tái sử dụng xuyên suốt để minh hoạ xử lý lỗi, thay vì bịa tình huống mới mỗi lần:

  • Địa chỉ thiếu số căn hộ → AddressVerificationFieldError.
  • Carrier không trả rate cho một kiện quá khổ → Messages trên Shipment.
  • Rate hết hạn giữa lúc khách còn đang ở bước thanh toán → lỗi khi mua.

5. Bố cục — 14 phần

Phần 1 — Nền tảng: EasyPost là gì và mô hình object

Bài toán multi-carrier và chỗ đứng của EasyPost trong stack. Bản đồ quan hệ giữa Address, Parcel, Shipment, Rate, PostageLabel, Tracker, Event — kèm sơ đồ. Vòng đời một shipment dạng máy trạng thái. Tính bất biến: object EasyPost gần như không sửa được, "sửa" nghĩa là tạo mới — và hệ quả của điều đó tới thiết kế phía mình. Test mode và production mode khác nhau ở bản chất nào, không chỉ khác cái key. Giới thiệu Lumière Skincare.

Phần 2 — Kết nối: authentication, client, môi trường

Basic auth với API key làm username và không có password; bắt buộc TLS 1.2. Test key vs production key, và tại sao key phải được đối xử như mật khẩu. Cài @easypost/api 8.9.0 dưới TypeScript strict với moduleResolution: NodeNext; khởi tạo client; cấu hình timeout. Quy ước tiền tố ID (adr_, prcl_, shp_, rate_, trk_, batch_…) và vì sao nó tiện khi đọc log. Pagination kiểu cursor. CarrierAccount: Wallet do EasyPost quản lý vs BYOA mang tài khoản riêng — chọn cái nào và đánh đổi là gì.

Phần 3 — Address và xác thực địa chỉ

Address object và các field. verify vs verify_strict: khác nhau ở chỗ lỗi làm hỏng request hay chỉ báo cáo lại. Đọc AddressVerificationFieldError với field / message / suggestion / code. Residential vs commercial ảnh hưởng trực tiếp tới giá và tới phụ phí. Verify ở thời điểm nào trong checkout — verify quá muộn thì khách đã đi mất, verify quá sớm thì tốn call vô ích.

Phần 4 — Parcel, Shipment, Rate

Parcel: kích thước, cân nặng, predefined_package và khi nào nên dùng. Shipment: to_address / from_address / return_address, parcel, options, carrier_accounts. Những options đáng dùng thật sự (xác nhận chữ ký, delivery confirmation, label_format) tách khỏi những cái hiếm khi chạm. Ba loại giá và ý nghĩa thương mại của từng loại: rate là cái sẽ bị trừ, list_rate là giá niêm yết chưa chiết khấu, retail_rate là giá ra bưu cục mua lẻ. delivery_days vs est_delivery_days. Messages giải thích vì sao một carrier im lặng. Trần 60 carrier account mỗi lần rating. Chiến lược rate shopping và cái bẫy của lowestRate() — rẻ nhất không đồng nghĩa giao được đúng hẹn.

Phần 5 — Mua label và in

Mua theo rate đã chọn. Race condition khi rate hết hạn giữa chừng và cách thiết kế để không bán hớ. PostageLabel: label_url tồn tại bao lâu, các format PNG / PDF / ZPL / EPL2 dùng cho loại máy in nào, cách yêu cầu lại label ở format khác. Form object (commercial invoice, return label). In thực tế: PrintNode, máy in nhiệt, bẫy DPI làm mã vạch không quét được. ReadyDocs.

Phần 6 — Tracker, Event, Webhook

Tracker object và tracking_details; tạo tracker độc lập cho kiện không mua label qua EasyPost. Event object và previous_attributes — thứ cho biết cái gì vừa đổi. Cấu hình webhook, webhook_secret, xác thực HMAC qua header X-Hmac-Signature, phân loại qua X-Easypost-Event-Type. Giới hạn cứng: endpoint phải trả 2XX trong 7 giây, EasyPost retry tối đa 6 lần với khoảng cách tăng dần, tối đa 30 endpoint mỗi tài khoản, header tự thêm không được mang tiền tố X-EasyPost-*. Hai giới hạn đầu là lý do bắt buộc phải nhận-rồi-xếp-hàng chứ không xử lý nghiệp vụ tại chỗ. Code mẫu: endpoint Express xác thực HMAC rồi enqueue. Payloads — log giao webhook của EasyPost, công cụ đầu tiên phải mở khi nghi webhook không tới. Webhook vs polling — khi nào buộc phải polling.

Phần 7 — Xử lý lô: Batch, ScanForm, Pickup, Order

Batch: tạo, thêm bớt shipment, mua cả lô, lấy label gộp. Batch chạy bất đồng bộ nên phải nghe webhook chứ không đứng đợi. ScanForm (manifest): carrier nào bắt buộc và hậu quả khi thiếu. Pickup: đặt lấy hàng tại kho. Order: kiện nhiều gói. Ghép lại thành quy trình cuối ngày của kho Lumière ở mức 800 đơn.

Phần 8 — Cross-border: customs, thuế, landed cost

CustomsInfo và CustomsItem, HS code và hậu quả khi khai sai. TaxIdentifier (IOSS, VAT). EndShipper — bắt buộc trong tình huống nào. Commercial invoice. Landed cost: cho khách thấy tổng tiền phải trả trước khi đặt, thay vì để carrier thu hộ lúc giao. Điều gì đổi khi origin không nằm ở Mỹ — phần này nói thẳng giới hạn của EasyPost thay vì lờ đi.

Phần 9 — Tiền: Insurance, Claims, Refund, Return, Fee

Bảo hiểm mua kèm lúc tạo shipment vs mua rời sau đó; giới hạn giá trị; khi nào bảo hiểm sẵn có của carrier là đủ. Claims: nộp thế nào, cần chứng từ gì, thời hạn. Refund: hoàn label chưa dùng, bao lâu thì tiền về, các trạng thái refund. Return label: hai cách tạo và khác biệt về thời điểm tính tiền. Fee object và cách đọc để đối soát hoá đơn. Billing và phương thức thanh toán.

Phần 10 — SmartRate và Luma: chọn rate bằng dữ liệu

SmartRate: time-in-transit theo percentile từ p50 tới p99, khác delivery_days ở chỗ nó là một phân phối chứ không phải một con số hứa hẹn. Cách chọn rate theo độ tin cậy giao hàng thay vì chỉ theo giá, và cách đặt ngưỡng percentile theo loại đơn. Luma: đẩy luật chọn rate lên Dashboard rồi gọi một lệnh mua. Hai thứ trả lời cùng một câu hỏi — ai là người quyết — nên đặt cạnh nhau: đánh đổi giữa kiểm soát trong code và giảm code phải bảo trì.

Phần 11 — Lỗi, giới hạn, độ bền

Cấu trúc error: code máy đọc được, message người đọc được, mảng errors chứa FieldError với field / message / suggestion. Bảng mã lỗi hay gặp và hành động tương ứng. Các HTTP status đáng chú ý, đặc biệt 402 Payment Required — lớp lỗi mà tích hợp mới hay bỏ quên. Rate limit 5 request/giây trên các endpoint Index; 429; backoff kèm jitter. EasyPost không có idempotency key — đây là một sự thật kiến trúc, không phải thiếu sót của tài liệu này: hệ quả là một cú retry mù vào lệnh mua label sẽ mua hai lần và mất tiền thật. Cách tự phòng: khoá theo order_id phía mình, lưu shipment_id trước khi gọi mua, tra trước khi mua lại. Phân loại thao tác an toàn để retry và thao tác tuyệt đối không. Dùng test mode để tái hiện lỗi mà không mất tiền.

Phần 12 — Vận hành production

Bảo mật và xoay API key; dùng child user key để giới hạn phạm vi. Kiến trúc tích hợp: gọi EasyPost đồng bộ trong luồng checkout hay đẩy qua queue — đánh đổi giữa trải nghiệm và độ bền. Những gì bắt buộc phải tự lưu: file label, tracking code, rate đã chọn và giá tại thời điểm mua, vì URL của EasyPost không tồn tại vĩnh viễn và giá thì đổi. Giám sát: tỉ lệ lỗi theo carrier, độ trễ của bước rating, chi phí theo đơn. Report object và quy trình đối soát hoá đơn. Checklist chuyển từ test sang production.

Phần 13 — Tham chiếu gọn

Bảng tra cho những phần cố ý không đi sâu: Account Management (API Keys, User, Child Users, Brand, ReferralCustomer, Customer Portal, Embeddable Components), White-label Forge với ba mô hình billing (self-managed, EasyPost-managed, Stripe Connect), CarrierType và Carrier Metadata, và 10 carrier guide (USPS, UPS, FedEx, DHL, Canada Post, Royal Mail, LSO, Canpar, GOFO/Cirro, FirstMile). Mỗi mục: một đoạn nói nó là gì và khi nào cần, kèm link tới trang gốc. Mục tiêu là người đọc biết thứ đó tồn tại và biết đi đâu, không phải học thuộc.

Phần 14 — Troubleshooting, anti-patterns, cheatsheet, glossary

Cây quyết định cho ba sự cố hay gặp nhất: "không có rate nào trả về", "mua được label nhưng in ra không quét được", "webhook không tới". Bảng anti-pattern đặt cạnh best practice tương ứng. Cheatsheet: object, tiền tố ID, endpoint, field bắt buộc, gói trong một trang. Glossary Anh-Việt cho thuật ngữ ngành vận chuyển (manifest, SCAN form, dimensional weight, zone, surcharge, EndShipper…). Danh sách tài liệu tham khảo kèm ngày truy cập.


6. Quy ước viết

  • Tiếng Việt. Tên object và thuật ngữ kỹ thuật giữ nguyên tiếng Anh — dịch Shipment thành "lô hàng" sẽ khiến người đọc không tra ngược được docs gốc. Nghĩa tiếng Việt nằm ở glossary Phần 14.
  • Mỗi khối TypeScript tự đứng được: đủ import, chạy được không cần sửa, dưới strict: true.
  • Mỗi endpoint có cặp song song TypeScript + cURL. Client library che mất hình hài HTTP thật; khi debug thì cần nhìn thấy nó.
  • Response JSON lấy từ tài liệu gốc. Rút gọn thì đánh dấu , không bịa thêm field.
  • Anti-pattern trình bày thành khối riêng có nhãn rõ, không trộn vào văn xuôi.
  • Đầu file, trước mục lục, theo đúng thứ tự: bảng phạm vi → cảnh báo drift → bảng kiểm chứng → mục lục.

Cảnh báo drift phải chỉ đúng chỗ đang trôi. Doc OTel cảnh báo về spec và semantic conventions vì đó là chỗ OTel trôi. EasyPost trôi ở chỗ khác, và cảnh báo phải nói đúng ba chỗ đó:

  1. Carrier — dịch vụ bị khai tử, bảng giá đổi theo năm, ràng buộc mới từ USPS/UPS/FedEx. Đây là nguồn lỗi thời nhanh nhất.
  2. Client library@easypost/api đang ở 8.x; major version đổi chữ ký hàm.
  3. Phí và giới hạn tài khoản — phụ thuộc hợp đồng, không phải hằng số chung.

REST API cốt lõi thì ổn định hơn — nói rõ điều đó, để người đọc biết chỗ nào tin được lâu và chỗ nào phải kiểm lại.


7. Kế hoạch kiểm chứng

Việc Cách làm Bắt được lớp lỗi nào
Mọi khối TypeScript Dựng project trong scratchpad, npm i @easypost/api@8.9.0 typescript, tsconfig strict + NodeNext, trích từng khối ra file, chạy tsc --noEmit Sai tên method, sai shape tham số, import không tồn tại, thiếu await
Tên field / enum / kiểu trả về Đối chiếu trực tiếp với types/index.d.ts trong package Field đúng trên docs web nhưng khác trong library, hoặc ngược lại
Con số và tên header Đối chiếu từng trang docs tương ứng, ghi ngày truy cập 2026-09-17 Số liệu nhớ sai
Payload cURL Đối chiếu shape với docs; không gửi request thật Sai tên field trong body

Bảng kiểm chứng đầu file phải nói thật cả phần chưa làm. Ghi rõ: chưa có lệnh gọi API thật nào, response mẫu lấy từ tài liệu chứ không từ tài khoản thật, và vì thế những gì tài liệu khẳng định về hành vi runtime là dẫn lại chứ không phải quan sát được. Bảng kiểm chứng nói quá sẽ phá đúng thứ làm nó đáng tin.

Cách thu thập nội dung. Chạy một workflow fan-out: mỗi agent đọc kỹ một cụm trang docs và trả về dữ liệu có cấu trúc (field, endpoint, ràng buộc, cảnh báo, ví dụ payload), rồi một pass đối chiếu chéo trước khi viết — để nội dung dựa trên trang thật chứ không dựa vào trí nhớ. Các cụm:

  1. core / authentication / pagination / EasyPost Objects / errors
  2. address và address verification
  3. parcel + shipment + rate + options + messages
  4. postage label + form + ReadyDocs + in ấn
  5. tracker + event + webhook
  6. batch + scanform + pickup + order
  7. customs + taxidentifier + endshipper + landed cost + commercial invoice
  8. insurance + claims + refund + return + fee + billing
  9. smartrate + luma
  10. rate limiting + retry
  11. account management + white-label Forge + embeddables
  12. carrier guides (10 carrier)

8. Sản phẩm bàn giao

Một file: C:\Users\nguye\source\repos\EasyPost-Toan-Tap.md, đặt cạnh OpenTelemetry-Tracing-Toan-Tap.md. Quy mô ước tính 5.000–7.000 dòng. Không tạo repo tách chương, không giữ lại project scratchpad.


9. Sự thật kỹ thuật đã xác minh (2026-09-17)

Ghi lại để bước viết không phải tra lại và không suy đoán.

Sự thật Nguồn
@easypost/api phiên bản 8.9.0; có types/index.d.ts; engines.node >= 16 npm view @easypost/api
Basic auth, API key làm username, không password; bắt buộc TLS 1.2; có test key và production key docs.easypost.com/docs/authentication
Rate limit 5 request/giây trên các endpoint Index; vượt thì 429; tối đa 60 carrier account mỗi lần rating docs.easypost.com/guides/rate-limiting-guide
Webhook: webhook_secret sinh chữ ký ở header X-Hmac-Signature; phân loại qua X-Easypost-Event-Type; endpoint phải trả 2XX trong 7 giây; retry tối đa 6 lần; tối đa 30 endpoint; header tuỳ biến tối đa 3 và không được mang tiền tố X-EasyPost-* docs.easypost.com/guides/webhooks-guide
Error object: code, message, errors[]; FieldErrorfield / message / suggestion; AddressVerificationFieldError thêm code; dùng các status 400/401/402/403/404/405/422/429/500/503 docs.easypost.com/docs/errors
Ba loại giá: rate, list_rate, retail_rate kèm trường currency tương ứng docs.easypost.com/docs/shipments/rates
Không có idempotency key — trang Authentication không nhắc tới, và không có header idempotency nào trong tài liệu docs.easypost.com/docs/authentication
Client library chính thức: .NET, Go, Java, Node.js, PHP, Python, Ruby docs.easypost.com/libraries
Cây nav API Docs gồm các nhóm Core, Basics, SmartRate, Advanced, Account Management — danh sách object đầy đủ đã lấy docs.easypost.com/docs/shipments (sidebar)

10. Rủi ro và cách xử lý

Rủi ro Xử lý
Không gọi API thật nên có chỗ tài liệu gốc mô tả mơ hồ mà không kiểm lại được Nói thẳng trong bảng kiểm chứng. Chỗ nào tài liệu gốc mơ hồ thì ghi là mơ hồ, không lấp bằng phỏng đoán nghe hợp lý.
Tài liệu quá dài, người đọc bỏ giữa chừng Mục lục có mô tả từng phần; mỗi phần tự đứng được; cheatsheet Phần 14 phục vụ người chỉ cần tra.
Phần "tham chiếu gọn" bị viết hời hợt thành danh sách link Mỗi mục bắt buộc trả lời "nó là gì" và "khi nào bạn cần nó" — nếu không trả lời được thì bỏ khỏi tài liệu.
Chi tiết carrier lỗi thời nhanh Hạn chế nêu giá và ràng buộc cụ thể của từng carrier; chỗ nào buộc phải nêu thì ghi ngày và trỏ về trang gốc.
Ví dụ trôi khỏi bối cảnh, mỗi phần một tên biến khác Bảng thông số Lumière ở mục 4 là bắt buộc dùng chung; không phần nào tự bịa địa chỉ hay kích thước mới.

11. Ghi chú

C:\Users\nguye\source\repos không phải git repository, nên spec này không commit được. Nếu muốn theo dõi lịch sử, cần git init ở thư mục đó, hoặc chuyển tài liệu vào một repo có sẵn.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí