Forwarding API
API cho phép hệ thống của Quý khách:
- Gửi yêu cầu báo giá (RFQ) cho lô hàng forwarding (hàng không, đường biển, đường bộ, hải quan...).
- Bổ sung thông tin khi Bell yêu cầu.
- Nhận báo giá, rồi đồng ý, đề nghị đàm phán lại hoặc từ chối.
- Theo dõi tiến độ vận chuyển và trao đổi chứng từ.
- Nhận webhook khi trạng thái hồ sơ thay đổi hoặc có cập nhật tiến độ.
1. Thông tin chung
| Base URL | https://expressbe.bellvn.com/api/partner/fwd |
| Định dạng | JSON, UTF-8 (Content-Type: application/json), trừ upload chứng từ dùng multipart/form-data |
| Xác thực | Authorization: Bearer <token> do Bell cấp. Đây cũng là token của Partner API chuyển phát, nếu Quý khách đã có |
| Giới hạn | Mặc định 120 request/phút cho mỗi khách hàng. Vượt quá sẽ nhận 429, xem header Retry-After |
| Ngày tháng | Ngày dạng YYYY-MM-DD, thời điểm dạng ISO-8601 (2026-09-23T10:15:00+07:00) |
Mỗi hồ sơ được định danh bằng rfq_code (mã do Bell cấp, ví dụ RFQ-HCM-2609-0012). Quý khách có thể gắn thêm mã của mình qua partner_ref. Quý khách chỉ nhìn thấy hồ sơ của chính mình.
Cấu trúc response
Thành công:
{ "status": "success", "message": "…", "data": { … } }
Lỗi dữ liệu (422): errors liệt kê lỗi theo từng trường.
{
"status": "error",
"message": "Chưa khai báo điểm nhận hàng.",
"errors": { "origin": ["Chưa khai báo điểm nhận hàng."] }
}
| HTTP | Ý nghĩa |
|---|---|
| 200 / 201 | Thành công (201 = vừa tạo hồ sơ mới) |
| 401 | Thiếu token, token hết hạn, không hợp lệ hoặc đã bị thu hồi. Liên hệ Bell để được cấp token mới |
| 404 | Không có hồ sơ, hoặc hồ sơ không thuộc Quý khách |
| 422 | Dữ liệu không hợp lệ, hoặc thao tác không phù hợp với trạng thái hiện tại |
| 429 | Vượt giới hạn request |
| 5xx | Lỗi phía Bell, có thể thử lại |
2. Trạng thái hồ sơ
status | Ý nghĩa | Quý khách cần làm |
|---|---|---|
submitted | Bell đã nhận yêu cầu | Chờ |
need_info | Bell cần bổ sung thông tin, nội dung ở trường info_request | Gọi PUT /orders/{rfq_code} |
quoting | Bell đang lập báo giá | Chờ |
quoted | Đã có báo giá | Xem báo giá, rồi đồng ý / đàm phán / từ chối |
confirmed | Quý khách đã đồng ý, đơn hàng đang được chuẩn bị | Gửi chứng từ nếu cần |
in_transit | Đang vận chuyển, chi tiết ở operation_status | Theo dõi tracking |
completed | Đã hoàn tất vận chuyển | — |
cancelled | Đã hủy hoặc không thành công | — |
Luồng thông thường: submitted → quoting → quoted → confirmed → in_transit → completed. Hồ sơ có thể quay lại need_info hoặc quoting, ví dụ khi Quý khách đề nghị đàm phán và Bell điều chỉnh giá.
Khi status = in_transit, trường operation_status cho biết tiến độ chi tiết: received, booked, picked_up, customs_declaring, export_cleared, departed, in_transit, arrived, import_cleared, delivering, delivered… Danh sách đầy đủ kèm nhãn lấy từ GET /meta.
3. Danh mục — GET /meta
Trả về các mã dùng khi gửi yêu cầu. Nên gọi định kỳ (ví dụ mỗi ngày) và cache lại.
{
"data": {
"services": [ { "code": "AIR", "name": "Vận chuyển hàng không", "name_en": "Air Freight", "transport_mode": "air" } ],
"statuses": [ { "value": "submitted", "label": "Đã gửi yêu cầu" } ],
"operation_statuses": [ { "value": "picked_up", "label": "Đã lấy hàng", "order": 6 } ],
"delivery_scopes": { "door_door": "Door to Door" },
"incoterms": { "FOB": "FOB" },
"special_requirements": { "insurance": "Yêu cầu bảo hiểm" },
"container_types": { "40HC": "40HC" },
"document_types": { "commercial_invoice": "Commercial Invoice" },
"reject_reasons": [ { "value": "price_high", "label": "Giá cao" } ]
}
}
Tại thời điểm viết tài liệu, các mã đang dùng là:
| Danh mục | Giá trị |
|---|---|
incoterm | EXW, FCA, FOB, CFR, CIF, CPT, CIP, DAP, DPU, DDP, OTHER |
delivery_scope | door_cy, cy_cy, cy_door, door_door, door_cfs, cfs_cfs, cfs_door, airport_airport, door_airport, airport_door, warehouse_warehouse, other |
special_requirements | time, vehicle, carrier, packing, insurance, customs, permit, ior, cod, timed_delivery, confidential, other |
container_type | 20DC, 40DC, 40HC, 45HC, reefer, other |
4. Gửi yêu cầu báo giá — POST /orders
Hồ sơ được gửi thẳng cho Bộ phận báo giá, không có bước nháp. Nếu dữ liệu thiếu, request bị từ chối toàn bộ (422) và không tạo hồ sơ nào.
Chống tạo trùng: nếu gửi kèm partner_ref, việc gửi lại cùng partner_ref (ví dụ do timeout rồi retry) sẽ không tạo hồ sơ mới mà trả về hồ sơ đã có, với HTTP 200 thay vì 201. Chúng tôi khuyến nghị luôn gửi partner_ref.
Trường dữ liệu
| Trường | Bắt buộc | Mô tả |
|---|---|---|
partner_ref | nên có | Mã của Quý khách, tối đa 100 ký tự, duy nhất trong phạm vi tài khoản |
contact_name | ✔ | Người liên hệ |
contact_phone, contact_email, contact_position | Mặc định lấy theo tài khoản nếu bỏ trống | |
service_codes | ✔ | Mảng mã dịch vụ lấy từ /meta, ví dụ ["AIR"] |
origin_country, origin_state, origin_address, origin_port | ✔ ít nhất 1 trong: quốc gia / địa chỉ / cảng | Điểm nhận hàng |
dest_country, dest_state, dest_address, dest_port, dest_postal_code | ✔ ít nhất 1 trong: quốc gia / địa chỉ / cảng | Điểm giao hàng |
incoterm hoặc delivery_scope | ✔ ít nhất 1 | Điều kiện giao hàng / phạm vi giao nhận |
delivery_required_date, pickup_date hoặc transit_time_required | ✔ ít nhất 1 | Yêu cầu về thời gian |
cargo_ready_date, etd_expected | Ngày hàng sẵn sàng, ngày khởi hành dự kiến | |
special_requirements | Mảng mã yêu cầu đặc biệt | |
special_note | Ghi chú thêm | |
currency | Tiền tệ mong muốn cho báo giá, ví dụ USD | |
customer_tax_code, customer_address, customer_country | Thông tin xuất hóa đơn | |
cargos | ✔ | Danh sách dòng hàng, 1 đến 200 dòng |
Mỗi dòng trong cargos:
| Trường | Bắt buộc | Mô tả |
|---|---|---|
name | ✔ | Tên hàng |
package_count | ✔ | Số kiện, ≥ 1 |
gross_weight | ✔* | Tổng trọng lượng (kg) |
length, width, height, dimension_unit | ✔* | Kích thước mỗi kiện. dimension_unit: cm (mặc định), m, mm, inch |
container_type, container_count | Dùng cho hàng nguyên container. Khi có container_count thì không bắt buộc trọng lượng và kích thước | |
package_type, weight_per_package, description, hs_code, origin_country, nature, goods_value, goods_currency, temperature_requirement, note | Thông tin thêm | |
has_battery, has_liquid, has_chemical, is_dangerous, requires_permit, permit_note | Đặc tính hàng hóa (true/false) |
* Trừ khi khai báo container_count.
Ví dụ
POST /api/partner/fwd/orders
Authorization: Bearer eyJ0eXAiOi...
Content-Type: application/json
{
"partner_ref": "PO-2026-0917",
"contact_name": "Nguyễn Văn A",
"contact_email": "logistics@khachhang.vn",
"service_codes": ["AIR"],
"origin_country": "VN",
"origin_address": "KCN Tân Bình, TP.HCM",
"dest_country": "US",
"dest_address": "Los Angeles, CA 90001",
"incoterm": "FOB",
"delivery_required_date": "2026-10-20",
"cargos": [
{
"name": "Áo thun cotton",
"package_count": 10,
"gross_weight": 150,
"length": 60, "width": 40, "height": 40, "dimension_unit": "cm",
"hs_code": "6109.10",
"goods_value": 3200, "goods_currency": "USD"
}
]
}
Response 201: chi tiết hồ sơ (xem mục 5). Lưu lại data.rfq_code để dùng cho các lệnh sau.
Nếu tài khoản của Quý khách chưa được Bell gán nhân viên kinh doanh phụ trách, request bị từ chối với 422 và errors.sales_user_id. Khi gặp lỗi này, vui lòng liên hệ Bell.
5. Tra cứu hồ sơ
GET /orders — danh sách
| Query | Mô tả |
|---|---|
status | Lọc theo trạng thái, nhận một hoặc nhiều giá trị: status[]=quoted&status[]=confirmed |
partner_ref | Tìm đúng theo mã của Quý khách |
search | Tìm theo rfq_code, số báo giá, mã đơn hàng, partner_ref |
from_date, to_date | Lọc theo ngày tạo (YYYY-MM-DD) |
updated_since | Chỉ lấy hồ sơ thay đổi từ thời điểm này. Dùng để đồng bộ định kỳ nếu không dùng webhook |
page, per_page | Phân trang, per_page tối đa 100, mặc định 20 |
{
"data": {
"items": [
{
"rfq_code": "RFQ-HCM-2609-0012",
"partner_ref": "PO-2026-0917",
"quotation_no": "QT-HCM-2609-0005",
"job_number": null,
"status": "quoted",
"status_label": "Đã có báo giá",
"services": [ { "code": "AIR", "name": "Vận chuyển hàng không", "transport_mode": "air" } ],
"origin_country": "VN",
"dest_country": "US",
"created_at": "2026-09-17T09:00:00+07:00",
"updated_at": "2026-09-18T15:20:00+07:00"
}
],
"meta": { "page": 1, "per_page": 20, "total": 1, "last_page": 1 }
}
}
GET /orders/{rfq_code} — chi tiết
Gồm các trường ở danh sách, cộng thêm:
| Trường | Mô tả |
|---|---|
submitted_at | Thời điểm gửi yêu cầu |
info_request | Nội dung Bell cần bổ sung. Chỉ có giá trị khi status = need_info |
operation_status, operation_status_label | Tiến độ vận hành hiện tại |
is_on_hold | true nếu lô hàng đang tạm dừng |
contact, origin, destination, schedule | Thông tin đã khai báo |
delivery_scope, incoterm, special_requirements, special_note, currency | |
cargos | Dòng hàng, kèm các giá trị Bell tính: volume (m³), volumetric_weight, chargeable_weight |
quotation | Tóm tắt báo giá (quotation_no, version, sent_at, valid_until), hoặc null nếu chưa có |
job_number là mã đơn hàng Bell cấp sau khi đơn được duyệt triển khai.
PUT /orders/{rfq_code} — bổ sung thông tin
Chỉ gọi được khi status = need_info. Chỉ cần gửi các trường thay đổi, cùng định dạng như lúc tạo. Nếu gửi cargos hoặc service_codes thì danh sách mới thay thế toàn bộ danh sách cũ. Có thể kèm note giải thích cho Bell. Sau khi gọi thành công, hồ sơ trở về submitted.
{
"cargos": [ { "name": "Áo thun cotton", "package_count": 10, "gross_weight": 150,
"length": 60, "width": 40, "height": 40, "hs_code": "6109.10" } ],
"note": "Đã bổ sung HS code theo yêu cầu"
}
6. Báo giá
GET /orders/{rfq_code}/quotation
Gọi được khi hồ sơ đã có báo giá; nếu chưa có sẽ nhận 404. Số liệu khớp với bản báo giá Bell gửi qua email.
{
"data": {
"quotation_no": "QT-HCM-2609-0005",
"version": 1,
"sent_at": "2026-09-18T15:20:00+07:00",
"valid_until": "2026-09-25",
"is_expired": false,
"currency": "USD",
"services": "Vận chuyển hàng không",
"route": "Hồ Chí Minh → Los Angeles",
"delivery_scope": "Door to Door",
"carrier": "Vietnam Airlines",
"schedule": "Thứ 2, 4, 6",
"transit_time": "3-4 ngày",
"inclusions": ["Cước hàng không", "Phí THC đầu xuất"],
"exclusions": ["Thuế nhập khẩu tại Mỹ"],
"risks": [],
"charges": [
{ "line_no": 1, "name": "Cước hàng không", "unit": "kg", "quantity": 160,
"unit_price": 4.2, "amount": 672, "currency": "USD",
"vat_rate": 0, "vat_amount": 0, "total": 672, "applied_condition": null }
],
"totals": { "amount": 672, "vat": 0, "total": 672 },
"terms": ["Báo giá chỉ có hiệu lực trong thời hạn ghi trên báo giá…"],
"can_respond": true
}
}
can_respond = true nghĩa là Quý khách còn phản hồi được báo giá này.
GET /orders/{rfq_code}/quotation/document
Trả về bản báo giá dạng HTML (text/html) để hiển thị, in hoặc lưu thành PDF.
Phản hồi báo giá
Cả ba lệnh dưới đây chỉ gọi được khi can_respond = true. Mỗi lệnh trả về chi tiết hồ sơ sau khi cập nhật.
| Lệnh | Body | Kết quả |
|---|---|---|
POST /orders/{rfq_code}/quotation/accept | { "note": "…" } (tùy chọn) | status → confirmed. Không đồng ý được báo giá đã hết hạn (422) |
POST /orders/{rfq_code}/quotation/negotiate | { "note": "Đề nghị giảm 5%…" } (bắt buộc) | Bell nhận đề nghị và có thể gửi báo giá phiên bản mới (version tăng). status vẫn là quoted |
POST /orders/{rfq_code}/quotation/reject | { "reason_code": "price_high", "note": "…" } (bắt buộc) | status → cancelled |
reason_code nhận một trong các giá trị: price_high, chose_competitor, transit_time_unsuitable, service_not_met, customer_cancelled_plan, payment_terms_disagreement, commercial_terms_disagreement, other.
7. Tiến độ vận chuyển — GET /orders/{rfq_code}/trackings
Danh sách các mốc tiến độ, mới nhất ở đầu. Trả về mảng rỗng nếu lô hàng chưa triển khai.
{
"data": [
{ "id": 881, "operation_status": "departed", "operation_status_label": "Đã khởi hành",
"happened_at": "2026-09-22T23:40:00+07:00", "location": "SGN", "content": "Chuyến bay VN 1234" }
]
}
8. Chứng từ
GET /orders/{rfq_code}/attachments
Danh sách chứng từ Quý khách đã gửi và chứng từ Bell chia sẻ, như vận đơn (bill_of_lading), tờ khai hải quan (customs_declaration), POD (pod), biên bản bàn giao (handover_record).
{ "data": [ { "id": 52, "doc_type": "bill_of_lading", "file_name": "AWB-738-1234.pdf",
"mime_type": "application/pdf", "file_size": 184233, "note": null,
"uploaded_at": "2026-09-22T10:00:00+07:00" } ] }
GET /orders/{rfq_code}/attachments/{id}/download
Trả về một đường dẫn tải có thời hạn (presigned URL), không trả nội dung file. Dùng GET tới url để tải file, không gửi kèm header Authorization. Đường dẫn chỉ dùng được đến expires_at (mặc định 10 phút); hết hạn thì gọi lại endpoint này để lấy đường dẫn mới. Không lưu url để dùng về sau.
{
"data": {
"url": "https://<bucket>.s3.<region>.amazonaws.com/fwd/123/AbC.pdf?X-Amz-Algorithm=...&X-Amz-Signature=...",
"expires_at": "2026-09-23T10:25:00+07:00",
"file_name": "AWB-738-1234.pdf"
}
}
POST /orders/{rfq_code}/attachments — gửi chứng từ
multipart/form-data:
| Trường | Bắt buộc | Mô tả |
|---|---|---|
file | ✔ | pdf, jpg, jpeg, png, webp, doc, docx, xls, xlsx, csv, txt, zip; tối đa 20 MB |
doc_type | ✔ | commercial_invoice, packing_list, contract, purchase_order, cargo_photo, catalogue, msds, coa, permit, certificate_of_origin |
note | Ghi chú |
curl -X POST https://expressbe.bellvn.com/api/partner/fwd/orders/RFQ-HCM-2609-0012/attachments \
-H "Authorization: Bearer $TOKEN" \
-F "doc_type=commercial_invoice" -F "file=@invoice.pdf"
Hồ sơ đã đóng (hoàn thành hoặc đã hủy) không nhận thêm chứng từ.
9. Webhook
Nếu Quý khách đăng ký URL webhook với Bell, hệ thống sẽ gửi POST tới URL đó khi có sự kiện. Nên dùng webhook thay cho việc gọi GET /orders liên tục. Webhook dùng chung URL, secret, cách ký và cơ chế retry với webhook hành trình đơn chuyển phát.
Headers
Content-Type: application/json
User-Agent: BellVN-Webhook/1.0
X-Bell-Event: fwd.status_changed
X-Bell-Delivery: 12345
X-Bell-Signature: sha256=<hex>
- Xác thực chữ ký: tính
HMAC-SHA256(raw_body, webhook_secret)dạng hex, rồi so sánh với phần sausha256=. Phải dùng hàm so sánh an toàn về thời gian, nhưhash_equalshoặccrypto.timingSafeEqual. - Phản hồi: trả HTTP 2xx trong vòng 15 giây. Nếu không, Bell sẽ gửi lại tối đa 5 lần (sau 10 giây, 30 giây, 2 phút, 10 phút, 30 phút).
- Chống xử lý trùng:
X-Bell-Deliverykhông đổi giữa các lần gửi lại, nên dùng nó làm khóa idempotency. - Thứ tự sự kiện: không đảm bảo. Nếu cần trạng thái chính xác, hãy gọi
GET /orders/{rfq_code}.
fwd.status_changed
Gửi khi status (trạng thái công khai ở mục 2) thay đổi. Các bước xử lý nội bộ của Bell không làm đổi status thì không phát sinh webhook.
{
"event": "fwd.status_changed",
"timestamp": "2026-09-18T15:20:03+07:00",
"data": {
"rfq_code": "RFQ-HCM-2609-0012",
"partner_ref": "PO-2026-0917",
"quotation_no": "QT-HCM-2609-0005",
"job_number": null,
"status": "quoted",
"status_label": "Đã có báo giá",
"previous_status": "quoting",
"changed_at": "2026-09-18T15:20:03+07:00"
}
}
fwd.tracking.created
Gửi mỗi khi Bell cập nhật một mốc tiến độ vận chuyển.
{
"event": "fwd.tracking.created",
"timestamp": "2026-09-22T23:41:00+07:00",
"data": {
"rfq_code": "RFQ-HCM-2609-0012",
"partner_ref": "PO-2026-0917",
"quotation_no": "QT-HCM-2609-0005",
"job_number": "FWD-HCM-2026-09-0031",
"status": "in_transit",
"tracking": {
"id": 881, "operation_status": "departed", "operation_status_label": "Đã khởi hành",
"happened_at": "2026-09-22T23:40:00+07:00", "location": "SGN", "content": "Chuyến bay VN 1234"
}
}
}
10. Quy trình tích hợp gợi ý
POST /orders (partner_ref) → lưu rfq_code
│
├─ webhook status = need_info → GET /orders/{rfq} (đọc info_request) → PUT /orders/{rfq}
├─ webhook status = quoted → GET /orders/{rfq}/quotation → accept | negotiate | reject
├─ status = confirmed → POST /orders/{rfq}/attachments (invoice, packing list…)
├─ webhook fwd.tracking.created → cập nhật tiến độ trên hệ thống của Quý khách
└─ status = completed → GET /orders/{rfq}/attachments (POD, vận đơn)
Mọi thắc mắc về tài khoản, token và đăng ký webhook, vui lòng liên hệ bộ phận Kinh doanh của Bell.