Chuyển tới nội dung chính

Forwarding API

API cho phép hệ thống của Quý khách:

  1. 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...).
  2. Bổ sung thông tin khi Bell yêu cầu.
  3. Nhận báo giá, rồi đồng ý, đề nghị đàm phán lại hoặc từ chối.
  4. Theo dõi tiến độ vận chuyển và trao đổi chứng từ.
  5. 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 URLhttps://expressbe.bellvn.com/api/partner/fwd
Định dạngJSON, UTF-8 (Content-Type: application/json), trừ upload chứng từ dùng multipart/form-data
Xác thựcAuthorization: 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ạnMặ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ángNgà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 / 201Thành công (201 = vừa tạo hồ sơ mới)
401Thiế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
404Không có hồ sơ, hoặc hồ sơ không thuộc Quý khách
422Dữ 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
429Vượt giới hạn request
5xxLỗi phía Bell, có thể thử lại

2. Trạng thái hồ sơ

statusÝ nghĩaQuý khách cần làm
submittedBell đã nhận yêu cầuChờ
need_infoBell cần bổ sung thông tin, nội dung ở trường info_requestGọi PUT /orders/{rfq_code}
quotingBell đ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
confirmedQuý 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_statusTheo 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ụcGiá trị
incotermEXW, FCA, FOB, CFR, CIF, CPT, CIP, DAP, DPU, DDP, OTHER
delivery_scopedoor_cy, cy_cy, cy_door, door_door, door_cfs, cfs_cfs, cfs_door, airport_airport, door_airport, airport_door, warehouse_warehouse, other
special_requirementstime, vehicle, carrier, packing, insurance, customs, permit, ior, cod, timed_delivery, confidential, other
container_type20DC, 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ườngBắt buộcMô tả
partner_refnê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_nameNgười liên hệ
contact_phone, contact_email, contact_positionMặc định lấy theo tài khoản nếu bỏ trống
service_codesMả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 1Yêu cầu về thời gian
cargo_ready_date, etd_expectedNgày hàng sẵn sàng, ngày khởi hành dự kiến
special_requirementsMảng mã yêu cầu đặc biệt
special_noteGhi chú thêm
currencyTiền tệ mong muốn cho báo giá, ví dụ USD
customer_tax_code, customer_address, customer_countryThông tin xuất hóa đơn
cargosDanh sách dòng hàng, 1 đến 200 dòng

Mỗi dòng trong cargos:

TrườngBắt buộcMô tả
nameTên hàng
package_countSố 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_countDù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, noteThô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 422errors.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

QueryMô tả
statusLọc theo trạng thái, nhận một hoặc nhiều giá trị: status[]=quoted&status[]=confirmed
partner_refTìm đúng theo mã của Quý khách
searchTìm theo rfq_code, số báo giá, mã đơn hàng, partner_ref
from_date, to_dateLọc theo ngày tạo (YYYY-MM-DD)
updated_sinceChỉ 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_pagePhâ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ườngMô tả
submitted_atThời điểm gửi yêu cầu
info_requestNội dung Bell cần bổ sung. Chỉ có giá trị khi status = need_info
operation_status, operation_status_labelTiến độ vận hành hiện tại
is_on_holdtrue nếu lô hàng đang tạm dừng
contact, origin, destination, scheduleThông tin đã khai báo
delivery_scope, incoterm, special_requirements, special_note, currency
cargosDòng hàng, kèm các giá trị Bell tính: volume (m³), volumetric_weight, chargeable_weight
quotationTó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ệnhBodyKế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ườngBắt buộcMô tả
filepdf, jpg, jpeg, png, webp, doc, docx, xls, xlsx, csv, txt, zip; tối đa 20 MB
doc_typecommercial_invoice, packing_list, contract, purchase_order, cargo_photo, catalogue, msds, coa, permit, certificate_of_origin
noteGhi 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 sau sha256=. Phải dùng hàm so sánh an toàn về thời gian, như hash_equals hoặc crypto.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-Delivery khô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.