Order API
Create an order
| Method | POST |
|---|---|
| URL | /api/order/create-order |
Request body
{
"address_id": 2,
"consignee_name": "sarenda mcguire",
"consignee_phone": "",
"consignee_address": "6105 W Elmwood Dr",
"consignee_address_other": "",
"consignee_country": "US",
"consignee_city": "Wildomar",
"consignee_postal_code": "57718",
"consignee_state_code": "SD",
"order_number": "DEV123456789",
"product_name": "Metal",
"product_price": 5.5,
"length": 1.93,
"width": 108,
"height": 1.9,
"weight": 1.1,
"service_code": "EPK",
"service_add": "",
"money_collection": 0,
"order_code_upstream_partner": "partner_of_partner",
"package_option": "818",
"unit_type": "cm-g",
"label_sender": "WRIGLEYS Cool Air",
"mode": null
}
Field reference
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | address_id | Long | Warehouse ID (see Reference API) |
| 2 | consignee_name | String | Recipient full name |
| 3 | consignee_phone | String | Recipient phone number |
| 4 | consignee_address | String | Recipient address line 1 |
| 5 | consignee_address_other | String | Recipient address line 2 |
| 6 | consignee_country | String | Country code (fixed: US) |
| 7 | consignee_city | String | City / province (see Reference API) |
| 8 | consignee_postal_code | String | 5 digits (entered by the user) |
| 9 | consignee_state_code | String | State code (see Reference API) |
| 10 | order_number | String | Partner's order number |
| 11 | product_name | String | Description of goods |
| 12 | product_price | Decimal | Value of goods |
| 13 | length | Decimal | Length (cm) |
| 14 | width | Decimal | Width (cm) |
| 15 | height | Decimal | Height (cm) |
| 16 | weight | Decimal | Weight (grams) |
| 17 | service_code | String | Main service (contact Bell for the list) |
| 18 | service_add | String | Value-added services |
| 19 | money_collection | Decimal | Cash on delivery amount |
| 20 | order_code_upstream_partner | String | Order code of your own upstream partner |
| 21 | package_option | String | Lithium battery: 818 (Marked), 820 (Unmarked). For service LBE6 use the full code: 8181, 8182, 8201, 8202. Leave empty if there is no battery |
| 22 | unit_type | String | Unit for dimensions and weight. Defaults to cm-g when omitted (mm-g, cm-g, cm-kg, in-lb) |
| 23 | label_sender | String | Sender agent name printed on the label. If not set, the label_sender configured on your customer account is used |
| 24 | mode | String | Pass "advanced" to use Advanced Mode (see below). Leave empty or null for Simple Mode |
Validation Rules
Contact Bell to get the list of available service_code values. The service must exist in the BELL system.
address_id
- Required, integer.
- Each service only accepts specific address_id values (provided by Bell on request).
order_number
- Required, up to 50 characters.
- Must be unique per customer (must not repeat an order number you have already used).
- For services
US_UPS_GROUNDandUS_UNIUNI_D: only the charactersa-z A-Z 0-9 _ ( ) - , # spaceare allowed.
consignee_name
- Required, up to 100 characters.
- For service
US_UPS_GROUND: must contain the full first and last name (at least 2 words separated by a space).
consignee_phone
- Optional.
- If provided, must be exactly 10 digits (e.g.
0123456789).
consignee_email
- Optional.
- If provided, must be a valid email address, up to 50 characters.
consignee_country
- Required, 2-letter country code (ISO-2).
consignee_state_code
- Required when
consignee_country = US. - Optional for other countries.
- Up to 50 characters.
consignee_postal_code
- Required, up to 20 characters.
- Format depends on the country:
- US: 5 digits or 5+4 (e.g.
57718or57718-1234). - GB/UK: Royal Mail format (e.g.
SW1A 1AA).
- US: 5 digits or 5+4 (e.g.
- For services
US_UNIUNIandUS_UNIUNI_D: the postal code must be within UniUni's service area.
consignee_address
- Required, up to 200 characters.
consignee_address_other
- Optional, up to 200 characters.
unit_type
- Optional. Default:
cm-g. - Allowed values:
mm-g,cm-g,cm-kg,in-lb.
length / width / height
- Required, numeric value > 0.
- Maximum: 150 cm (converted according to unit_type).
weight
- Required, numeric value > 0.
product_name
- Optional, up to 100 characters.
product_price
- Optional, numeric value ≥ 0.
service_add
- Optional, up to 100 characters.
label_sender
- Optional, up to 50 characters.
- If not set, the label_sender configured on your customer account is used.
package_option (Lithium Battery)
- Optional.
- Only allowed with services
EPK,LBE,LBE6. - Allowed values per service:
| Service | Allowed values | Description |
|---|---|---|
| EPK, LBE | 818, 820 | 818 = Marked, 820 = Unmarked |
| LBE6 | 8181, 8182, 8201, 8202 | Full code: 818x = Marked, 820x = Unmarked |
- When
package_optionis set, packages must meet these limits:
| Service | Maximum weight | Length + 2×Width + 2×Height |
|---|---|---|
| EPK | ≤ 11 lbs | < 108 inch |
| LBE | ≤ 11 lbs | < 108 inch |
| LBE6 | ≤ 11 lbs | < 108 inch |
If the goods contain batteries, set package_option to the code that matches the service,
and attach the mark
https://express.bellvn.com/UN-3091.jpg or
https://express.bellvn.com/UN-3481.jpg
Response
{
"error": false,
"ref": null,
"message": "Tạo đơn thành công",
"data": "DEV123456789",
"warnings": [],
"zone": "002"
}
Field reference
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | error | boolean | Error flag |
| 2 | ref | String | Transaction reference code |
| 3 | message | String | Message |
| 4 | data | String | BELL order code |
| 5 | warnings | Array | Warnings listing fields the system automatically converted to ASCII (services US_UNIUNI_D, US_UPS_GROUND, LBE6) |
The create-order response includes zone at the top level. Its type is String/null; the value remains a string (for example, "002"). It is null when the zone cannot be determined. The zone field is not returned in error responses.
Each item in warnings:
| Field | Description |
|---|---|
| field | Name of the converted field |
| label | Display name of the field |
| original | Original value before conversion |
| sanitized | Value after conversion to ASCII |
| message | Description of the automatic action |
Create an order (Advanced Mode)
For international services that require detailed customs declarations and support multiple packages (multi-package).
| Method | POST |
|---|---|
| URL | /api/order/create-order |
The following services must use advanced mode:
UPS_WW_SAVER2,UPS_WW_SAVERUPS_WW_EXPEDITED2,UPS_WW_EXPEDITEDUPS_WW_EXPRESS2,UPS_WW_EXPRESSFEDEX_IPE2,FEDEX_IPE,FEDEX_IP2,FEDEX_IPFEDEX_IE2,FEDEX_IE,FEDEX_ICP2,FEDEX_ICPFEDEX_IP_SBP,FEDEX_IP_SBP2
If you use one of these services without mode: "advanced" or without goods information, the request fails validation.
For services US_UNIUNI_D, US_UPS_GROUND, LBE6: the system automatically converts accented Vietnamese characters to ASCII (e.g. á → a, đ → d) in name and address fields. The response includes a warnings array listing the converted fields.
Request body
{
"mode": "advanced",
"address_id": 0,
"service_code": "UPS_WW_EXPRESS",
"order_number": "PARTNER-ADV-001",
"order_code_upstream_partner": "UPSTREAM-001",
"consignee_name": "John Doe",
"consignee_company": "ACME Corp",
"consignee_phone": "2125550001",
"consignee_email": "john@example.com",
"consignee_country": "US",
"consignee_state_code": "NY",
"consignee_city": "New York",
"consignee_postal_code": "10001",
"consignee_address": "123 Main Street",
"consignee_address_other": "Suite 100",
"unit_type": "cm-g",
"package_option": null,
"label_sender": "MY STORE",
"service_add": "",
"packages": [
{
"weight": 1000,
"length": 30,
"width": 20,
"height": 15,
"goods": [
{
"name_en": "Electronic Device",
"name_vi": "Thiết bị điện tử",
"brand": "BrandName",
"packaging_type": "Box",
"origin": "CN",
"price": 150.00,
"quantity": 2,
"unit": "pcs",
"hs_code": "8471.30",
"material": "Plastic"
}
]
},
{
"weight": 500,
"length": 25,
"width": 18,
"height": 10,
"goods": [
{
"name_en": "Accessories",
"name_vi": "Phụ kiện",
"brand": null,
"packaging_type": null,
"origin": "VN",
"price": 25.50,
"quantity": 5,
"unit": "pcs",
"hs_code": null,
"material": null
}
]
}
],
"extra_fields": {
"trade_terms": "DDP",
"tax_type": "IOSS",
"tax_number": "IOSS-123456789",
"signature_required": true
}
}
Simple vs Advanced
| Feature | Simple (default) | Advanced |
|---|---|---|
| Number of packages | 1 package (pass length, width, height, weight directly) | Multiple packages (packages array) |
| Goods declaration | product_name + product_price | Detailed goods array (name, origin, HS code, ...) |
| Supported services | All domestic services | Required for international services (UPS WW, FedEx IP) |
| Extra fields | No | Supports trade_terms, tax_type, tax_number |
Additional fields
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | mode | String | Required for advanced services. Value: "advanced" |
| 2 | consignee_company | String | Recipient company name. Required for advanced services, up to 100 characters |
| 3 | consignee_email | String | Recipient email. Optional, up to 50 characters |
| 4 | packages | Array | Array of packages. Required, at least 1 package |
| 5 | packages[].weight | Decimal | Package weight (per unit_type, grams by default) |
| 6 | packages[].length | Decimal | Length (per unit_type, cm by default) |
| 7 | packages[].width | Decimal | Width (per unit_type, cm by default) |
| 8 | packages[].height | Decimal | Height (per unit_type, cm by default) |
| 9 | packages[].goods | Array | Goods in this package. Required, at least 1 item |
| 10 | packages[].goods[].name_en | String | Goods name (English). Required, up to 100 characters |
| 11 | packages[].goods[].name_vi | String | Goods name (Vietnamese). Optional |
| 12 | packages[].goods[].brand | String | Brand. Optional |
| 13 | packages[].goods[].packaging_type | String | Packaging type. Optional |
| 14 | packages[].goods[].origin | String | Country of origin code (ISO-2). Required, 2 characters |
| 15 | packages[].goods[].price | Decimal | Goods value (USD). Required, minimum 0.01 |
| 16 | packages[].goods[].quantity | Integer | Quantity. Required, minimum 1 |
| 17 | packages[].goods[].unit | String | Unit of measure (e.g. pcs, kg, set). Optional |
| 18 | packages[].goods[].hs_code | String | Customs HS code. Optional |
| 19 | packages[].goods[].material | String | Material. Optional |
| 20 | extra_fields | Object | Additional tax / trade information. Optional |
| 21 | extra_fields.trade_terms | String | Trade terms: DDU, DDP, CIP, C&F. For FEDEX_ICP/FEDEX_ICP2 only DDP is allowed |
| 22 | extra_fields.tax_type | String | Tax type: IOSS, EORI, VAT |
| 23 | extra_fields.tax_number | String | Corresponding tax number (up to 100 characters) |
| 24 | extra_fields.signature_required | Boolean | Signature required on delivery. JingDong services only, Advanced Mode only |
Advanced Mode validation
packages
- Required, array with at least 1 item.
- Each package must include
weight,length,width,height. - Values must be > 0. Maximum dimension 150 cm (converted according to
unit_type).
goods (for services that require advanced mode)
- Required, array with at least 1 item.
- Each goods item must include:
name_en: required, up to 100 charactersorigin: required, 2 characters (ISO-2 country code)price: required, number > 0 (minimum 0.01)quantity: required, integer ≥ 1
extra_fields
- Optional.
- If provided, every value inside must be a string or a boolean.
extra_fields.trade_terms
- Allowed values:
DDU,DDP,CIP,C&F. - For services
FEDEX_ICPandFEDEX_ICP2: onlyDDPis accepted.
extra_fields.tax_type
- Allowed values:
IOSS,EORI,VAT.
extra_fields.signature_required
- Type
boolean. Only supported by JingDong services. - Only allowed in Advanced Mode; rejected (
prohibited) if sent in Simple Mode.
Unit conversion
The system stores values internally in mm-g. When you send a different unit_type, values are converted automatically:
| unit_type | Weight → grams | Dimensions → mm |
|---|---|---|
| mm-g | ×1 | ×1 |
| cm-g | ×1 | ×10 |
| cm-kg | ×1000 | ×10 |
| in-lb | ×453.59 | ×25.4 |
Response
Same as the standard create order response:
{
"error": false,
"ref": null,
"message": "Tạo đơn thành công",
"data": "BELL240625120345123"
}
Validation error example
{
"error": true,
"ref": null,
"message": "Service này yêu cầu mode \"advanced\" với đầy đủ thông tin goods.",
"errors": {
"goods.0.origin": ["origin là bắt buộc"],
"goods.0.price": ["price là bắt buộc"]
},
"data": null
}
Edit the address of a failed order and resubmit
For orders in the Error status (status = 11) that the carrier rejected because of the recipient address. The API saves the new address and puts the order back into the resubmission flow, keeping the same BELL order code — no new order is created.
The request is asynchronous: BELL returns 202 as soon as the address has been saved and queued, and only then calls the carrier. Get the final result by looking up the order (see Track the result).
| Method | POST |
|---|---|
| URL | /api/order/edit-order |
- Call
/api/order/get-order-info/{order_number}to readstatusandprocessing_error, so you know exactly which field to fix. - Call
/api/order/edit-orderwith the address fields to change. - A
202means the request has been queued. Call/api/order/get-order-info/{order_number}again to get the final result. - On error, handle it based on the
codein the error response (see the result code table).
Request body
{
"order_code": "BELL240625120345123",
"consignee_country": "US",
"consignee_state_code": "CA",
"consignee_city": "San Francisco",
"consignee_postal_code": "94105",
"consignee_address": "123 Main Street",
"consignee_address_other": "Suite 200"
}
Field reference
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | order_code | String | Required. BELL waybill code (order_code), up to 100 characters. Do not use your own order number or the carrier's code |
| 2 | consignee_country | String | Country code (ISO-2), up to 2 characters |
| 3 | consignee_state_code | String | State code, up to 50 characters. Required for US and for advanced services |
| 4 | consignee_city | String | City / province, up to 50 characters |
| 5 | consignee_postal_code | String | Postal code, up to 20 characters. Must follow the country / UniUni rules in Validation Rules |
| 6 | consignee_address | String | Recipient address line 1, up to 200 characters |
| 7 | consignee_address_other | String | Recipient address line 2, up to 200 characters. Send null to clear it |
Editing rules
- You must send at least one address field. Sending only
order_codeis rejected withVALIDATION_FAILED. - Address fields you do not send keep their current value. Sending the same value as before still counts as a resubmission request.
- Only the 6 address fields above are allowed. Any other key (
address_id,service_code,status,consignee_name,consignee_phone,packages,goods,extra_fields, amount fields...) is rejected, even if sent asnull. - The warehouse, BELL order code, service, packages and goods cannot be changed. To change them, cancel the order and create a new one.
- The new address is validated against the rules of the service attached to the order.
consignee_countryis normalized to upper case andUK→GB. - For services that require ASCII (
US_UNIUNI_D,US_UPS_GROUND,LBE6), the submitted address is converted to ASCII automatically and the response includes the correspondingwarningsarray.
Order eligibility
The order must meet all of the following conditions, otherwise you receive 409:
- It belongs to the customer calling the API (a different owner returns
404, indistinguishable from an order that does not exist). - It is in the
Errorstatus (status = 11) and has not been deleted. - It has no carrier order code and no tracking yet.
- No resubmission is currently in progress for it.
Do not import, edit or cancel the order from the web interface while a resubmission is running. Each order is processed one attempt at a time.
Success response (202)
Short envelope, same as the create order API:
{
"error": false,
"ref": null,
"message": "Cập nhật đơn thành công",
"data": "BELL240625120345123",
"warnings": []
}
Field reference
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | error | boolean | false = BELL has received and recorded the request |
| 2 | ref | String | Always null in a success response |
| 3 | message | String | Cập nhật đơn thành công ("Order updated successfully") |
| 4 | data | String | BELL waybill code (unchanged by the edit) |
| 5 | warnings | Array | Non-blocking warnings, e.g. ASCII auto-sanitize. Same structure as the create order API |
202 does not mean the carrier has accepted the order202 only confirms that the new address has been saved and the order has been queued for resubmission. The call to the carrier happens after the response is returned. You must look up the final result separately — see Track the result.
Processing flow
Synchronous (before the response is returned):
- Check ownership and order eligibility.
- Validate the new address against the rules of the service attached to the order.
- Verify the address with the address validation provider (can take up to ~25 seconds).
- Estimate the shipping charge for the new address — reads the price list only, nothing is posted to your account balance.
- Save the new address and the carrier payload. The order moves from
Error(11) toInit(1). - Return
202.
Asynchronous (after the response is returned):
- BELL calls the carrier to create the shipment and records the result.
- If accepted: the charge is updated, the label is downloaded and the order moves to
Label(3). - If rejected: the order goes back to
Error(11) andprocessing_erroris updated with the new reason.
Track the result
Call /api/order/get-order-info/{order_number} and read status:
status | Meaning |
|---|---|
Init | Being sent to the carrier. Wait and check again |
Label | Resubmitted successfully. tracking_code and tracking_label_url are now populated |
Error | The carrier rejected the order. Read processing_error for the reason, fix it and call the API again |
This usually takes from a few seconds to a few tens of seconds. Wait 5–10 seconds before the first check, and limit the number of polls. If the order stays in Init for too long (more than a few minutes), contact support with the order code instead of calling the API again — a repeated call will be blocked with 409.
Result codes
Success is always 202 with the short envelope above. Every code below is an error that happens before the address is saved. Error envelopes include an extra code field — always handle errors by code, not by message.
| HTTP | code | Meaning and what to do |
|---|---|---|
| 404 | ORDER_NOT_FOUND | Order not found or it does not belong to your account. data = null |
| 409 | ORDER_BUSY | The order is being processed by another process. Try again later |
| 409 | ORDER_NOT_EDITABLE | The order is not in the Error status, or already has a carrier order code / tracking |
| 409 | RECOVERY_NOT_RETRYABLE | No valid recovery attempts remain for this order |
| 409 | PROVIDER_RESULT_UNKNOWN | A resubmission is already in progress or its result is unknown. Do not call again — wait or contact support with the ref |
| 409 | BILLING_LOCKED | The invoice has been closed or the account balance needs review. Contact support |
| 409 | ADDRESS_RECOMMENDATION_AVAILABLE | A corrected address is suggested. Review suggestions, confirm, then call again |
| 422 | VALIDATION_FAILED | Invalid payload or address. See errors |
| 422 | ADDRESS_VALIDATION_FAILED | The address could not be verified |
| 422 | PRICING_UNAVAILABLE | A shipping charge could not be calculated for the new address |
| 422 | UNSUPPORTED_PROVIDER | The order's carrier configuration does not support this flow |
| 503 | PROVIDER_UNAVAILABLE | The carrier request could not be prepared. Nothing was sent; you can try again |
| 503 | ADDRESS_VALIDATION_UNAVAILABLE | The address validation service is temporarily unavailable. Try again later |
| 503 | LOCAL_PERSISTENCE_FAILED | BELL could not process the request. If the response has a non-null ref, do not call again — look up the order status first |
Example: field that cannot be edited (422)
{
"error": true,
"ref": null,
"code": "VALIDATION_FAILED",
"message": "This field cannot be edited.",
"data": null,
"errors": {
"consignee_name": ["This field cannot be edited."]
},
"warnings": []
}
Example: suggested address (409)
{
"error": true,
"ref": null,
"code": "ADDRESS_RECOMMENDATION_AVAILABLE",
"message": "Địa chỉ có đề xuất cần xác nhận trước khi tiếp tục.",
"data": null,
"suggestions": {
"consignee_city": "San Francisco",
"consignee_postal_code": "94105-1804"
},
"warnings": []
}
suggestions uses the partner field names and is never applied automatically — confirm the values, then call the API again with the values you want.
Charges and payment
- The preflight charge estimate only reads the price list and wallet limit. It creates no transaction, holds no funds and posts no charge.
- The charge is updated only after the carrier accepts the order, i.e. after the
202response has been returned. Receiving202does not mean the charge has changed. - Look up the new charge with
/api/price/get-price/{order_number}after the order reachesLabel. Before that, the returned value is still the charge for the old address. - If the carrier rejects the order, the charge stays as it was before the edit.
- This flow creates no new order, package, goods or payment transaction.
Timeouts and retries
The API no longer waits for the carrier, but it still performs a synchronous address validation step that can take up to ~25 seconds. Set your HTTP timeout to at least 60 seconds.
If the request times out or fails on your side because of a network error, do not call again immediately — the address may already have been saved and the order queued for resubmission. Call /api/order/get-order-info/{order_number} to check the actual status before doing anything else.
| Situation | Can you call again? |
|---|---|
202 success | No. The order is being resubmitted — track the result |
VALIDATION_FAILED, ADDRESS_VALIDATION_FAILED | Yes, after fixing the data |
ADDRESS_RECOMMENDATION_AVAILABLE | Yes, after confirming the suggestion |
ORDER_BUSY, PROVIDER_UNAVAILABLE, ADDRESS_VALIDATION_UNAVAILABLE | Yes, wait a few minutes and try again |
PROVIDER_RESULT_UNKNOWN, LOCAL_PERSISTENCE_FAILED with a ref | No. Look up the order status and contact support with the ref |
| Timeout / no response received | No. Call get-order-info first |
Order returns to Error with a new processing_error | Yes, fix the address based on the reason and call again |
Cancel an order
| Method | POST |
|---|---|
| URL | /api/order/cancel-order |
Request payload
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | order_number | String | BELL waybill code |
Response
{
"error": false,
"ref": null,
"message": "Thành công",
"data": null
}
Print a shipping label
| Method | GET |
|---|---|
| URL | /api/order/print-order/{order_number} |
Path parameter
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | order_number | String | BELL waybill code or your own order number (order_code_partner) |
Response
{
"error": false,
"ref": null,
"message": "Thành công",
"data": {
"orderNumber": "BELL1202103548",
"tracking": "42080521921449037400250000264",
"labelFormat": "PDF",
"labelSize": "4x6",
"label": "",
"labelUrl": "https://storage.bellvn.com/labels/BELL1202103548.pdf",
"receiptUrls": [
"https://storage.bellvn.com/receipts/BELL1202103548_1.pdf"
],
"trackings": [
{
"tracking_code": "42080521921449037400250000264",
"package_no": 1
}
],
"processing_error": null
}
}
Field reference
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | error | boolean | Error flag |
| 2 | ref | String | Reference code |
| 3 | message | String | Message |
| 4 | data.orderNumber | String | Order code that was queried |
| 5 | data.tracking | String | Main tracking number of the order |
| 6 | data.labelFormat | String | Label file format: PDF, GIF, PNG, JPG (or empty if there is no label yet) |
| 7 | data.labelSize | String | Label size (default 4x6) |
| 8 | data.label | String | Base64 label content (currently always empty — use labelUrl instead) |
| 9 | data.labelUrl | String | URL of the label file to download or view online |
| 10 | data.receiptUrls | Array | URLs of waybill receipt files. May be empty [] |
| 11 | data.trackings | Array | Tracking numbers per package (multi-package). Empty [] for single-package orders or when there is no tracking yet |
| 12 | data.processing_error | String | Reason the order failed (status = 11 Error), explained in Vietnamese. null if the order is not in an error state |
receiptUrls and trackings only have values for JingDong service orders (multi-package orders or orders with their own receipts). For other services, both fields are empty arrays.
Look up order information
| Method | GET |
|---|---|
| URL | /api/order/get-order-info/{order_number} |
Path parameter
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | order_number | String | BELL order code (order_code), your own order number (order_code_partner), or internal ID (id) |
Response
{
"status": "success",
"message": "Get order info successfully",
"data": {
"order_code": "BELL240625120345123",
"order_code_partner": "DEV123456789",
"zone": "002",
"status": "Label",
"service": "EPK",
"reason": null,
"processing_error": null,
"tracking_code": "42080521921449037400250000264",
"tracking_label_url": "https://storage.bellvn.com/labels/BELL240625120345123.pdf",
"carrier": "USPS",
"ship_date": "2024-06-25",
"consignee_name": "sarenda mcguire",
"consignee_phone": "0123456789",
"consignee_company": null,
"consignee_country": "US",
"consignee_state": "SD",
"consignee_city": "Wildomar",
"consignee_postal_code": "57718",
"consignee_address": "6105 W Elmwood Dr",
"consignee_address2": null,
"created_date": "2024-06-25 12:03:45"
}
}
Response fields
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | status | String | success or error |
| 2 | message | String | Result message |
| 3 | data.order_code | String | BELL waybill code |
| 4 | data.order_code_partner | String | Your own order number |
| 6 | data.status | String | Order status (see the table below) |
| 7 | data.service | String | Service code |
| 8 | data.reason | String | Reason (e.g. cancellation reason). Null if none |
| 9 | data.processing_error | String | Reason the order failed (status = Error), explained in Vietnamese by BELL. null if the order is not in an error state or the cause could not be determined |
| 10 | data.tracking_code | String | Carrier tracking number |
| 11 | data.tracking_label_url | String | URL of the label file |
| 12 | data.carrier | String | Carrier name |
| 13 | data.ship_date | String | Ship date |
| 14 | data.consignee_name | String | Recipient name |
| 15 | data.consignee_phone | String | Recipient phone number |
| 16 | data.consignee_company | String | Recipient company |
| 17 | data.consignee_country | String | Country code (ISO-2) |
| 18 | data.consignee_state | String | State / province |
| 19 | data.consignee_city | String | City |
| 20 | data.consignee_postal_code | String | Postal code |
| 21 | data.consignee_address | String | Recipient address |
| 22 | data.consignee_address2 | String | Recipient address line 2 |
| 23 | data.created_date | String | Order creation time |
Order statuses
| Value | Description |
|---|---|
Init | Newly created |
Label | Label generated |
In Transit | In transit |
Out For Delivery | Out for delivery |
Delivered | Delivered |
Exception | Exception occurred |
Void | Cancelled |
Request Cancel | Cancellation requested |
Unknown | Unknown |
Draft | Draft |
Common errors
{
"status": "error",
"message": "Order not found"
}
{
"status": "error",
"message": "Order not belong to Customer"
}
Track shipment
To receive updates pushed by BELL to your system, see Shipment journey webhooks. Use the GET API below for lookups, initial history synchronization or reconciliation.
| Method | GET |
|---|---|
| URL | /api/order/tracking/{order_number} |
Path parameter
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | order_number | String | BELL waybill code |
Response
{
"error": false,
"ref": null,
"message": "Thành công",
"data": [
{
"trackingNumber": "BELL2512171654532484",
"code": "12345678900",
"city": "City 0",
"stateCode": "State 0",
"zipCode": "Zip 0",
"countryCode": "Country 0",
"eventDate": "2025-12-18T03:57:04.896549Z",
"eventStatus": "LabelCreated 0",
"note": "Label Created"
},
{
"trackingNumber": "BELL2512171654532484",
"code": "12345678901",
"city": "City 1",
"stateCode": "State 1",
"zipCode": "Zip 1",
"countryCode": "Country 1",
"eventDate": "2025-12-18T03:57:04.896558Z",
"eventStatus": "LabelCreated 1",
"note": "Label Created"
}
]
}
Field reference
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | error | boolean | Error flag (false = success) |
| 2 | ref | String | Request reference code (may be null) |
| 3 | message | String | Result message |
| 4 | data | Array | List of tracking events |
| 5 | data[].trackingNumber | String | Waybill / tracking number |
| 6 | data[].code | String | Event code or internal reference code |
| 7 | data[].city | String | City where the event occurred |
| 8 | data[].stateCode | String | State / province code |
| 9 | data[].zipCode | String | Postal code |
| 10 | data[].countryCode | String | Country code (ISO-2) |
| 11 | data[].eventDate | String | Time of the event (ISO 8601, UTC) |
| 12 | data[].eventStatus | String | Tracking event status / content |
| 13 | data[].note | String | Status note / description |
Get a shipping quote
| Method | POST |
|---|---|
| URL | /api/price/get-price |
Send Authorization: Bearer <token> as described in Authentication. This API quotes one service and one package before order creation. It does not require order_number, create an order or label, check wallet/credit limits, or initiate billing.
Partners must provide one supported service_code and a positive address_id that exists in the system. Warehouse 0 is not accepted for automatic warehouse selection.
The API no longer returns all services or automatically selects the cheapest quote. data contains one quote and no longer includes carrier. Update your request payload and response parser if you use the previous contract.
Request body
{
"address_id": 57,
"service_code": "US_UNIUNI_D",
"consignee_country": "US",
"consignee_state_code": "CA",
"consignee_city": "Los Angeles",
"consignee_postal_code": "90001",
"consignee_address": "123 Example Street",
"unit_type": "cm-g",
"weight": 300,
"length": 20,
"width": 10,
"height": 5
}
The warehouse and address are illustrative; use a real warehouse supported by a service enabled for your account.
| Field | Type | Required | Rules |
|---|---|---|---|
address_id | Integer | Yes | Integer ≥ 1; the warehouse must exist and support the selected service. See the table below |
service_code | String | Yes | Only EPK, LBE6, US_UNIUNI_D, US_UPS_GROUND |
consignee_country | String | Yes | All four services currently support only US; country codes are normalized to uppercase |
consignee_state_code | String | Yes | State code, up to 50 characters |
consignee_city | String | Yes | City, up to 50 characters |
consignee_postal_code | String | Yes | Up to 20 characters; 5 digits or ZIP+4, such as 90001 or 90001-1234. For US_UNIUNI_D, the first 5 digits must be in UniUni's service area |
consignee_address | String | Yes | Recipient address, up to 200 characters |
consignee_address_other | String/null | No | Additional address line, up to 200 characters |
consignee_company | String/null | No | Company name, up to 100 characters |
consignee_email | String/null | No | Valid email address, up to 50 characters |
weight | Number | Yes | Actual weight; finite number ≥ 0.01, in unit_type units |
length, width, height | Number | Yes | Each dimension must be a finite number ≥ 0.01 and no more than 150 cm after conversion |
unit_type | String/null | No | mm-g, cm-g, cm-kg, in-lb; defaults to cm-g when omitted or null |
package_option | String/null | No | Lithium battery code; only supported for EPK and LBE6 in this quote API |
mode | String/null | No | Omitted, null, or simple; advanced is not supported |
Services and origin warehouses
| Service | Supported address_id values |
|---|---|
EPK | No additional allowlist; the warehouse must exist and have an origin country configured |
LBE6 | 73, 75, 80, 101, 113 |
US_UNIUNI_D | 57, 58, 100 |
US_UPS_GROUND | 73, 75, 80, 101, 113, 967 |
Use data[].shop_id from GET /api/order/list-shop (see Warehouse list). The warehouse must exist in the environment you call. BELL must assign an active selling price table for the service to your account; an unassigned public price table does not grant access to quotes.
- Send a single package using top-level
weight,length,width,height.mode=advanced,is_advanced=true, or non-emptypackagesreturns422. Omitpackagesandis_advancedfrom quote requests. US_UNIUNI_IDand codes other than the four services above are not supported.consignee_nameandconsignee_phoneare not required or used; older integrations may still send them, but they are ignored. Order creation validation is unchanged.- Order fields such as
order_number,goods,product_price,label_sender,service_adddo not participate in quote calculation. - For
LBE6,US_UNIUNI_D,US_UPS_GROUND, company, state, city and address lines are automatically normalized to ASCII before validation.
Units and battery shipments
unit_type | Dimensions | Weight |
|---|---|---|
mm-g | mm | grams |
cm-g | cm | grams |
cm-kg | cm | kg |
in-lb | inches | pounds |
The API converts measurements to cm and grams: 1 mm = 0.1 cm, 1 kg = 1000 grams, 1 inch = 2.54 cm, 1 pound = 453.59237 grams. Intermediate weights and dimensions are not rounded.
For battery shipments, send package_option as a string supported by the service:
| Service | Valid package_option values |
|---|---|
EPK | 818, 820 |
LBE6 | 8181, 8182, 8201, 8202 |
US_UNIUNI_DandUS_UPS_GROUNDdo not support battery options in this API.- With
package_option, converted actual weight must be ≤ 11 lbs andlength + 2 × width + 2 × heightmust be < 108 inches. - For
LBE6, codes starting with818map to UBI'sBattery Only; those starting with820map toInside Equipment. Send onlypackage_option, not a separatebatteryPackingfield. - Omit the field or send
nullfor non-battery shipments. The API does not separately add battery, signature, insurance or value-added service fees.
How quotes are calculated
- EPK: billable weight is the greater of actual and dimensional weight (
length_cm × width_cm × height_cm / the service's configured conversion divisor × 1000, in grams). The API uses the matching selling price tier without multiplying the price by weight or calling UBI. - LBE6 / US_UNIUNI_D / US_UPS_GROUND: actual weight selects the price configuration, then the API requests a UBI rate for the selected warehouse and package measurements and applies the configured selling price adjustment.
- The final amount is rounded to two decimal places. No currency conversion is performed;
amountuses the service's configured currency. There is nocurrencyresponse field; confirm the currency with BELL during integration. - A UBI quote is an estimate, not the carrier's final settled charge. For an existing order, use Look up order charges.
- If no price matches, the API returns an error rather than using the nearest tier, choosing the cheapest price or returning a fake successful quote of
0. An actual configured price of0is valid.
Successful response
{
"error": false,
"ref": null,
"message": "Thành công",
"data": [
{
"serviceCode": "US_UNIUNI_D",
"serviceName": "Configured service name",
"amount": 2.87,
"addressId": 57,
"postcode": "92843"
}
]
}
| Field | Type | Description |
|---|---|---|
error | Boolean | false when the quote succeeds |
ref | null | No reference ID is returned for quotes |
message | String | Result message |
data | Array | Exactly one quote, with only the five fields below |
data[].serviceCode | String | Requested service code |
data[].serviceName | String/null | Configured service name |
data[].amount | Number | Rounded selling price, not a string; trailing .00 is not guaranteed |
data[].addressId | Integer | Selected origin warehouse ID |
data[].postcode | String | Origin warehouse postal code, not consignee_postal_code; empty string if the warehouse has no postal code |
The service name, price and postal code are illustrative. The response does not expose carrier, purchase costs or raw carrier data.
Quote error codes
| HTTP | code | Cause / action |
|---|---|---|
| 401 | No standard quote code | Missing, invalid or expired token; check authentication |
| 422 | VALIDATION_FAILED | Invalid payload, warehouse, mode, address, measurements or battery option; correct fields in errors |
| 422 | UNSUPPORTED_PRICE_SERVICE | Service outside the four supported codes; correct service_code |
| 403 | SERVICE_NOT_ENABLED | No active selling price table assigned for the service; contact BELL |
| 404 | PRICE_NOT_FOUND | No matching zone or weight tier |
| 503 | PRICE_CONFIGURATION_INVALID | Missing, duplicate or invalid quote configuration; contact BELL |
| 502 | PROVIDER_RATE_UNAVAILABLE | A valid UBI estimate could not be retrieved; retry later |
Validation error example (422)
{
"error": true,
"ref": null,
"message": "Báo giá chỉ hỗ trợ simple mode, một kiện.",
"data": [],
"code": "VALIDATION_FAILED",
"errors": {
"mode": ["Báo giá chỉ hỗ trợ simple mode, một kiện."]
}
}
Service not enabled example (403)
{
"error": true,
"ref": null,
"message": "Customer chưa được mở dịch vụ báo giá này.",
"data": [],
"code": "SERVICE_NOT_ENABLED"
}
Quote errors return data: []; validation errors also include field-level errors. Token failures intercepted before quoting instead use the authentication format {"status":"error","message":"..."}, not the envelope above.
Look up order charges
This API retrieves recorded charges for an existing order, unlike the pre-order quote API. The GET endpoint contract is unchanged.
| Method | GET |
|---|---|
| URL | /api/price/get-price/{order_number} |
Path parameter
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | order_number | String | BELL waybill code |
Response
{
"error": false,
"message": "Success",
"data": [
{
"order_code": "BELL2512171654532484",
"amount": 15.50,
"type": "MAIN"
}
]
}
Field reference
| No. | Field Name | Data Type | Description |
|---|---|---|---|
| 1 | error | boolean | Error flag (false = success) |
| 2 | message | String | Result message |
| 3 | data | Array | Charge breakdown |
| 4 | data[].order_code | String | Waybill code |
| 5 | data[].amount | Decimal | Charge amount |
| 6 | data[].type | String | Charge type (MAIN: main shipping charge, or another surcharge) |
The API only returns charges for orders in an active invoice. If the order has not been charged yet or has no invoice, an error is returned.