Shipping quotes API
POST /v1/fulfillment/shipping/quotes estimates international shipping from a destination, weight, dimensions, item attributes, and services.
Use the single-package shorthand when the warehouse split is unknown. Use packages[] when the package count and per-package measurements are already known.
This endpoint uses the same channel, region, service, billing, and money terminology as the Shipping channels API.
https://api.hiobuy.com/v1/fulfillment/shipping/quotesQuotes are budgets, not locked prices. Warehouse weighing, volume measurement, re-packing, and service review can change the final amount. A
COMPLETEquote is still not a price lock.
Compatibility alias: POST /v1/fulfillment/shipments/freight/estimate (same behavior). Prefer /v1/fulfillment/shipping/quotes.
Money and units {#units}
- All money
amountvalues are integer CNY fen.5000means ¥50.00. - Top-level
monetary_unitis alwaysCNY_minor. - Money objects use
{ "amount": 5000, "currency": "CNY" }. - Request weight is always
KG. Package dimensions are alwaysCM. - Identify channels and services by stable code, not
name.
Request headers {#request}
POST /v1/fulfillment/shipping/quotes
Authorization: Bearer hio_live_xxx
Content-Type: application/json
Language: enDisplay language is the Language header only. Supported values: en (default), en-US, zh, zh-CN, zh-TW, cn, hk. Do not put language in the JSON body or query string.
Two request formats {#request-formats}
Do not combine the single-package shorthand fields (
weight_kg,length_cm,width_cm,height_cm) withpackages[]. The API rejects a mixed request with400 VALIDATION_ERROR.
Format A — single-package shorthand {#format-a}
Use this when you know the destination and estimated total weight, but not how the warehouse will split boxes.
Minimal request:
{
"destination": {
"country_code": "KR"
},
"weight_kg": 2.05
}Single package with dimensions:
{
"destination": {
"country_code": "KR",
"postal_code": "04524"
},
"weight_kg": 2.05,
"length_cm": 30,
"width_cm": 20,
"height_cm": 10,
"attributes": []
}weight_kgis the estimated shipment weight inKG.length_cm,width_cm, andheight_cmareCM. Provide all three or omit all three.- Prefer this format when the final warehouse split is unknown. Missing dimensions may produce
PARTIALorREVIEW_REQUIREDfor volumetric channels.
Format B — known packages {#format-b}
Use packages[] when you already know the box count and each box’s data.
Single declared package:
{
"destination": {
"country_code": "KR"
},
"packages": [
{
"reference": "box-1",
"weight": {
"value": 2.05,
"unit": "KG"
},
"dimensions": {
"length": 30,
"width": 20,
"height": 10,
"unit": "CM"
}
}
]
}Multiple packages:
{
"destination": {
"country_code": "KR",
"postal_code": "04524"
},
"packages": [
{
"reference": "box-1",
"weight": {
"value": 1.25,
"unit": "KG"
},
"dimensions": {
"length": 30,
"width": 20,
"height": 10,
"unit": "CM"
}
},
{
"reference": "box-2",
"weight": {
"value": 0.8,
"unit": "KG"
},
"dimensions": {
"length": 20,
"width": 15,
"height": 8,
"unit": "CM"
}
}
]
}packages[] rules (OpenAPI):
- 1–200 packages.
- One element is an explicit single package. Multiple elements are an explicit multi-package shipment.
referenceis optional, but must be unique within the request.packages[].weightis required.packages[].weight.unitmust beKG.packages[].dimensionsis optional. When present,length,width,height, andunitare all required.unitmust beCM.
The Gateway maps format A to a single warehouse package. Format B is validated and mapped as declared. Final split and measured dimensions are still determined after warehouse processing.
Shared request fields {#shared-fields}
Both formats may include the following.
destination
| Field | Required | Description |
|---|---|---|
country_code | Yes | ISO 3166-1 alpha-2, two letters |
subdivision_code | No | For example US-CA. The country prefix must match country_code. |
region | No | Subdivision display name. A valid subdivision_code takes precedence. |
postal_code | No | String. Keep leading zeroes. |
declared_value
{
"amount": 5000,
"currency": "CNY"
}Shipment-level declared value in integer CNY fen. Omit the field when unknown. Do not send 0 to mean unknown.
attributes
Array of stable item-attribute codes from GET /v1/fulfillment/shipments/item-attributes. Unique strings, max 100.
Quotes are first limited to channels applicable to destination.country_code; channels for other countries are omitted. Each remaining channel evaluates the declared attributes independently. Unsupported attributes produce UNAVAILABLE with ATTRIBUTE_NOT_SUPPORTED, while other applicable channels may still return quotes.
services
{
"services": {
"channel": [
{
"code": "LINE_INSURANCE",
"quantity": 1
}
],
"outbound": [
{
"code": "VACUUM_PACKING",
"quantity": 1
}
],
"inbound": [
{
"code": "PHOTO",
"quantity": 3
}
]
}
}| Group | Meaning |
|---|---|
channel | Channel value-added services. Codes come from channels value_added_services[].code. |
outbound | Outbound warehouse processing |
inbound | Inbound warehouse processing |
Each item requires code and quantity. OpenAPI: quantity is an integer from 1 to 999. Codes must be unique within each group.
channel_codes
Stable channel codes that restrict the quote set. Omit to quote all candidate channels. Do not send an empty array. An unknown or inaccessible code returns 400 INVALID_SHIPPING_CHANNEL for the whole request.
Complete request example {#complete-request}
Format A with destination, dimensions, declared value, attributes, services, and a channel filter:
{
"destination": {
"country_code": "US",
"subdivision_code": "US-CA",
"region": "California",
"postal_code": "90001"
},
"weight_kg": 2.05,
"length_cm": 30,
"width_cm": 20,
"height_cm": 10,
"declared_value": {
"amount": 5000,
"currency": "CNY"
},
"attributes": [
"GENERAL_CARGO"
],
"services": {
"channel": [
{
"code": "LINE_INSURANCE",
"quantity": 1
}
],
"outbound": [
{
"code": "VACUUM_PACKING",
"quantity": 1
}
],
"inbound": [
{
"code": "PHOTO",
"quantity": 3
}
]
},
"channel_codes": [
"US-SEA"
]
}Response {#response}
{
"success": true,
"estimate_type": "DECLARED",
"completeness": {
"level": "HIGH",
"destination": "COMPLETE",
"package_dimensions": "COMPLETE",
"package_split": "DECLARED",
"declared_value": "PROVIDED",
"attributes": "DECLARED",
"services": "REQUESTED_ONLY"
},
"warnings": [
{
"code": "WAREHOUSE_REVIEW_REQUIRED",
"message": "Final charges require warehouse measurement, packing and service review.",
"affects": [
"FINAL_CHARGES"
]
}
],
"quotes": [
{
"channel": {
"code": "US-SEA",
"name": "US Ocean",
"description": "General cargo line",
"tags": [
"GREAT_VALUE"
],
"capabilities": {
"delivery_methods": [
"DOOR_DELIVERY",
"PICKUP"
]
}
},
"available": true,
"quote_status": "COMPLETE",
"unavailable_reason": null,
"attribute_evaluation": {
"status": "SUPPORTED",
"declared_attributes": [
{
"code": "GENERAL",
"name": "General cargo"
}
],
"accepted_attributes": [
{
"code": "GENERAL",
"name": "General cargo"
}
],
"unsupported_attributes": []
},
"matched_region": {
"code": "US-WEST",
"name": "US West",
"match_type": "COUNTRY_REGION"
},
"packages": [
{
"reference": "box-1",
"weight": {
"value": 2.05,
"unit": "KG"
},
"dimensions": {
"length": 30,
"width": 20,
"height": 10,
"unit": "CM"
},
"weights": {
"actual": {
"value": 2.05,
"unit": "KG"
},
"volumetric": {
"value": 1,
"unit": "KG"
},
"chargeable": {
"value": 2.5,
"unit": "KG"
}
}
}
],
"weights": {
"actual": {
"value": 2.05,
"unit": "KG"
},
"volumetric": {
"value": 1,
"unit": "KG"
},
"chargeable": {
"value": 2.5,
"unit": "KG"
}
},
"billing_quantity": {
"value": 2.5,
"unit": "KG"
},
"pricing_selector": {
"type": "WEIGHT",
"value": 2.5,
"unit": "KG",
"scope": "SHIPMENT"
},
"transit_time": {
"text": "15-18 business days",
"min_business_days": 15,
"max_business_days": 18
},
"charges": {
"base_freight": {
"amount": 10000,
"currency": "CNY"
},
"channel_services": {
"amount": 550,
"currency": "CNY",
"known_amount": 550,
"calculation_status": "CALCULATED",
"items": []
},
"channel_rules": {
"amount": 0,
"currency": "CNY",
"known_amount": 0,
"calculation_status": "CALCULATED",
"items": [],
"aggregation": "SUM_ALL",
"cap": null
},
"outbound_services": {
"amount": 800,
"currency": "CNY",
"known_amount": 800,
"calculation_status": "CALCULATED",
"items": []
},
"inbound_services": {
"amount": 300,
"currency": "CNY",
"known_amount": 300,
"calculation_status": "CALCULATED",
"items": []
},
"adjustments": {
"amount": 0,
"currency": "CNY",
"known_amount": 0,
"calculation_status": "CALCULATED",
"items": []
}
},
"display_charges": {
"addition": {
"amount": 0,
"currency": "CNY",
"included_in_total": false
},
"floor": {
"amount": 0,
"currency": "CNY",
"included_in_total": false
}
},
"total": {
"amount": 11650,
"currency": "CNY"
},
"known_total": {
"amount": 11650,
"currency": "CNY"
},
"warnings": [],
"service_adjustment_possible": true
}
],
"monetary_unit": "CNY_minor",
"generated_at": "2026-09-07T08:00:00Z",
"request_id": "req_xxx",
"disclaimer": "Estimated charges based on declared data."
}Top-level fields
| Field | Description |
|---|---|
success | true on a successful estimate, including when some quotes are UNAVAILABLE. |
estimate_type | DECLARED — based on declared data, not warehouse measurement. |
completeness | How complete the input is. Not a deliverability guarantee. |
warnings | Shipment-level warnings. |
quotes | Per-channel results: complete first, then partial/review, then unavailable. |
monetary_unit | Always CNY_minor. |
generated_at | Response timestamp. |
request_id | Correlate with x-request-id. |
disclaimer | Human-readable estimate disclaimer. |
completeness
| Field | Meaning |
|---|---|
level | Overall input coverage, for example HIGH. |
destination | How complete the destination is (COMPLETE when country is present). |
package_dimensions | Whether length/width/height were provided. |
package_split | DECLARED when you sent packages[]; shorthand is an assumed single package. |
declared_value | PROVIDED or omitted/unknown. |
attributes | Whether item attributes were declared. |
services | REQUESTED_ONLY when you sent selected services; warehouse may still add more. |
Quote status {#quote-status}
Do not decide from available alone. Always read quote_status.
quote_status | available | total | Meaning |
|---|---|---|---|
COMPLETE | true | Money object | Full budget you may display. Still not locked. |
PARTIAL | true | usually null | Missing inputs. Read warnings and add dimensions, postal code, or declared value. |
REVIEW_REQUIRED | true | usually null | Warehouse measurement or human review is required. |
UNAVAILABLE | false | null | This channel cannot quote. The HTTP request still succeeded. |
totalis the complete budget when known.known_totalis the sum of fees that can be calculated now.- When
totalisnull, do not treatknown_totalas a complete quote. unavailable_reason.codeis for programmatic handling.unavailable_reason.messageis display-only.
UNAVAILABLE on one quote is not an HTTP error.
Quote object {#quotes}
| Field | Description |
|---|---|
channel | { code, name, description, tags, capabilities } — same identity as the channel catalog. |
channel.capabilities.delivery_methods | Optional additive array of stable last-mile codes: DOOR_DELIVERY, PICKUP, POST_OFFICE_PICKUP. It may be present for every quote status. Existing clients may ignore it. |
available | Whether this channel produced a usable estimate. Pair with quote_status. |
quote_status | COMPLETE · PARTIAL · REVIEW_REQUIRED · UNAVAILABLE. |
unavailable_reason | { code, message } or null. |
attribute_evaluation | Optional per-channel result: SUPPORTED, UNSUPPORTED, or NOT_DECLARED, with declared, accepted, and unsupported attribute lists. |
matched_region | { code, name, match_type } or null. |
packages | Declared or mapped packages plus per-package weights. |
weights | Shipment-level actual / volumetric / chargeable in KG. |
billing_quantity | Quantity the channel actually bills, with unit KG, M3, or KG_PER_M3. |
pricing_selector | How that quantity hit a rate-card band (type, value, unit, scope). |
transit_time | Reference transit time. |
charges | Fee groups. See below. |
display_charges | Optional UI-only rows. If included_in_total is false, do not add them to total. |
total / known_total | Complete budget vs currently known fees. |
warnings | Channel-level warnings. |
service_adjustment_possible | true if warehouse processing may still change services and fees. |
Weights and billing quantity {#weights}
actual— declared or measured weight.volumetric— dimensional weight.chargeable— weight used for freight.billing_quantity— quantity the channel bills (may be weight, volume, or density).pricing_selector— how that quantity selected a rate row. Types match catalogWEIGHT/VOLUME/DENSITY.- Multi-package channels may round per box and then sum. When
pricing_selector.scopeisPER_PACKAGE, read eachpackages[].pricing_selector.
Charges {#charges}
| Group | Meaning |
|---|---|
base_freight | Base international freight |
channel_services | Mandatory and developer-selected channel services |
channel_rules | Oversize, overweight, declared-value, and other route rules |
outbound_services | Value-added services performed during shipment preparation / outbound processing |
inbound_services | Value-added services performed during warehouse receiving / inbound processing |
adjustments | Adjustments |
Inbound vs outbound services
Inbound and outbound services represent different stages of warehouse value-added services. Inbound services are performed during warehouse receiving, while outbound services are performed during shipment preparation. Both groups may legitimately appear in the same quote and may both be charged when different services are performed at each stage.
The amounts shown in a shipping quote are estimates of the applicable services only. Requesting a quote does not create or settle a service charge. Final charges are determined by the services actually performed and their final package/shipment service pricing and settlement.
Use both groups when estimating total fulfillment cost, then reconcile each estimated service against its corresponding final actual charge. Do not add a quote estimate again on top of the final charge for the same individual service.
Illustrative quote amounts (all amounts use CNY minor units):
| Stage | Example service | Estimated amount |
|---|---|---|
| Inbound | Package photos | 50 fen (¥0.50) |
| Inbound | Inspection | 100 fen (¥1.00) |
| Outbound | Vacuum packing | 150 fen (¥1.50) |
| Outbound | Package reinforcement | 200 fen (¥2.00) |
All four estimates may coexist because they are different services performed at different fulfillment stages. Other inbound examples include video recording and receiving-related services requested by the customer; other outbound examples include wooden-frame / wooden-crate packing and outbound handling services.
Each group typically includes:
| Field | Meaning |
|---|---|
amount | Group total in fen, or null if unknown |
currency | CNY |
known_amount | Sum of items that can be calculated now |
calculation_status | For example CALCULATED |
items | Line items |
channel_rules may also include aggregation and cap. Use the group subtotal — do not replace it by summing raw rule items.
aggregation=SUM_ALL accumulates every matched charge. HIGHEST_ONLY sorts by amount and selects only the highest; equal-priced rules are equivalent. cap limits the aggregated subtotal, and null means uncapped.
Charge items
| Field | Meaning |
|---|---|
code | Stable fee or service code |
name | Localized display name |
category | Fee category |
source | MANDATORY · DEVELOPER_SELECTED · RULE_ENGINE |
pricing_basis | How the line was priced |
quantity | Billable quantity for the line |
unit_price | Unit price in fen when known |
amount | Line total. null means unknown, not zero. |
estimated | Whether the line is still an estimate |
calculation_status | Calculation state |
affected_by_packages | Whether package count/size can change this line |
included_in_total | If false, do not add it again to total |
reason | Display-only explanation; do not use it for program logic |
condition_match | ALL requires every condition; ANY requires at least one |
matched_conditions | Conditions with shipment actual_value and configured threshold_value |
rule_type | Per-shipment, per-box, weight-based, order/dispatch restriction, or advanced rule |
charge_mode / charge_value | Fixed, percentage, or formula configuration and potential rate |
outcome | Evaluated result, for example CHARGE |
missing_fields | Inputs required when the rule cannot yet be evaluated |
display_charges may exist only for frontend presentation.
PENDING_INPUT, REVIEW_REQUIRED, and other uncalculated rules are excluded from the known total. amount=null is not zero, and a configured charge_value is not an incurred charge.
Unavailable reasons {#unavailable-reasons}
Common unavailable_reason.code values:
OUTSIDE_PRICING_RANGE
POSTAL_CODE_REQUIRED
POSTAL_CODE_NOT_SUPPORTED
DESTINATION_NOT_SUPPORTED
ATTRIBUTE_NOT_SUPPORTED
SERVICE_NOT_SUPPORTED
DIMENSIONS_REQUIRED
DECLARED_VALUE_REQUIRED
MULTI_PACKAGE_NOT_SUPPORTED
PRICING_MODE_UNSUPPORTED
CHANNEL_RULE_REJECTED
CHANNEL_PRICING_ERRORUnavailable channels stay in quotes[] so you can explain why they were rejected.
Catalog terminology {#catalog-terminology}
| Shipping channels | This API | Meaning |
|---|---|---|
items[].code/name/description/tags | quotes[].channel.* | Same channel |
items[].capabilities.delivery_methods | quotes[].channel.capabilities.delivery_methods | Same stable last-mile delivery-method codes |
regions[].code/name/match_type | quotes[].matched_region.* | Region selected by the quote |
value_added_services[].code | services.channel[].code and charge code | Stable service code |
billing.quantity_unit | billing_quantity.unit / pricing_selector.unit | Billing unit |
rule_summary.aggregation | charges.channel_rules.aggregation | Rule aggregation |
rule_summary.cap | charges.channel_rules.cap | Cap after aggregation; null means uncapped |
Item attributes {#item-attributes}
GET /v1/fulfillment/shipments/item-attributes returns the valid attributes[] codes for quotes.
Errors {#errors}
| HTTP | error.code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid structure, mixed request formats, KG/CM units, duplicate codes, or empty channel_codes |
| 400 | INVALID_SHIPPING_CHANNEL | A requested channel_codes value is unknown or not visible to the app |
| 401 | INVALID_API_KEY | Missing or invalid Bearer token |
| 403 | FULFILLMENT_MODE_NOT_SUPPORTED | App uses self fulfillment |
| 403 | WAREHOUSE_AUTH_INVALID | Warehouse authorization is missing, rejected, or expired |
| 422 | INVALID_COUNTRY_CODE | Destination country is invalid |
| 422 | INVALID_ITEM_ATTRIBUTE | An attribute code is invalid |
| 502 | WAREHOUSE_UPSTREAM_ERROR | Warehouse service failed; retry with backoff |
There is no distinct public AUTHENTICATION_ERROR, WAREHOUSE_AUTH_REQUIRED, or WAREHOUSE_SERVICE_UNAVAILABLE code. Use 401 INVALID_API_KEY, 403 WAREHOUSE_AUTH_INVALID, and 502 WAREHOUSE_UPSTREAM_ERROR.
A channel that cannot carry this shipment is an UNAVAILABLE quote, not an HTTP failure.
Related {#related}
- Shipping channels — capabilities, coverage, and service codes
- Shipments — create a shipment after parcels are inbound
- Fulfillment overview
Get Support
Need integration help? Contact Developer Support at support@hiobuy.com · Response within 1–2 business days