API de cotizaciones de envío
POST /v1/fulfillment/shipping/quotes estima el envío internacional a partir de un destino, peso, dimensiones, atributos de artículos y servicios.
Utilice el atajo de paquete único cuando el fraccionamiento en almacén sea desconocido. Utilice packages[] cuando ya conozca el número de paquetes y las medidas de cada uno.
Este endpoint usa la misma terminología de canal, región, servicio, facturación y dinero que la API de canales de envío.
https://api.hiobuy.com/v1/fulfillment/shipping/quotesLas cotizaciones son presupuestos, no precios bloqueados. El pesaje en almacén, la medición de volumen, el reembalaje y la revisión de servicios pueden cambiar el importe final. Una cotización
COMPLETEsigue sin ser un bloqueo de precio.
Alias de compatibilidad: POST /v1/fulfillment/shipments/freight/estimate (mismo comportamiento). Prefiera /v1/fulfillment/shipping/quotes.
Dinero y unidades {#units}
- Todos los valores monetarios
amountson enteros en fen de CNY.5000significa ¥50.00. - El
monetary_unitde primer nivel es siempreCNY_minor. - Los objetos monetarios usan
{ "amount": 5000, "currency": "CNY" }. - El peso de la solicitud es siempre
KG. Las dimensiones de paquete son siempreCM. - Identifique canales y servicios por code estable, no por
name.
Encabezados de solicitud {#request}
POST /v1/fulfillment/shipping/quotes
Authorization: Bearer hio_live_xxx
Content-Type: application/json
Language: enEl idioma de visualización es únicamente el encabezado Language. Valores admitidos: en (predeterminado), en-US, zh, zh-CN, zh-TW, cn, hk. No coloque language en el cuerpo JSON ni en la cadena de consulta.
Dos formatos de solicitud {#request-formats}
No combine los campos del atajo de paquete único (
weight_kg,length_cm,width_cm,height_cm) conpackages[]. La API rechaza una solicitud mixta con400 VALIDATION_ERROR.
Formato A — atajo de paquete único {#format-a}
Utilice este formato cuando conozca el destino y el peso total estimado, pero no cómo el almacén fraccionará las cajas.
Solicitud mínima:
{
"destination": {
"country_code": "KR"
},
"weight_kg": 2.05
}Paquete único con dimensiones:
{
"destination": {
"country_code": "KR",
"postal_code": "04524"
},
"weight_kg": 2.05,
"length_cm": 30,
"width_cm": 20,
"height_cm": 10,
"attributes": []
}weight_kges el peso estimado del envío enKG.length_cm,width_cmyheight_cmestán enCM. Proporcione los tres u omita los tres.- Prefiera este formato cuando el fraccionamiento final en almacén sea desconocido. Dimensiones faltantes pueden producir
PARTIALoREVIEW_REQUIREDen canales volumétricos.
Formato B — paquetes conocidos {#format-b}
Utilice packages[] cuando ya conozca el número de cajas y los datos de cada caja.
Paquete declarado único:
{
"destination": {
"country_code": "KR"
},
"packages": [
{
"reference": "box-1",
"weight": {
"value": 2.05,
"unit": "KG"
},
"dimensions": {
"length": 30,
"width": 20,
"height": 10,
"unit": "CM"
}
}
]
}Varios paquetes:
{
"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"
}
}
]
}Reglas de packages[] (OpenAPI):
- 1–200 paquetes.
- Un elemento es un paquete único explícito. Varios elementos constituyen un envío multi-paquete explícito.
referencees opcional, pero debe ser único dentro de la solicitud.packages[].weightes obligatorio.packages[].weight.unitdebe serKG.packages[].dimensionses opcional. Cuando está presente,length,width,heightyunitson todos obligatorios.unitdebe serCM.
El Gateway mapea el formato A a un paquete de almacén único. El formato B se valida y se mapea tal como se declaró. El fraccionamiento final y las dimensiones medidas se determinan todavía después del procesamiento en almacén.
Campos de solicitud compartidos {#shared-fields}
Ambos formatos pueden incluir lo siguiente.
destination
| Campo | Obligatorio | Descripción |
|---|---|---|
country_code | Sí | ISO 3166-1 alpha-2, dos letras |
subdivision_code | No | Por ejemplo US-CA. El prefijo de país debe coincidir con country_code. |
region | No | Nombre de visualización de la subdivisión. Un subdivision_code válido tiene prioridad. |
postal_code | No | Cadena. Conserve los ceros iniciales. |
declared_value
{
"amount": 5000,
"currency": "CNY"
}Valor declarado a nivel de envío en fen de CNY enteros. Omita el campo cuando sea desconocido. No envíe 0 para significar desconocido.
attributes
Array de códigos de atributos de artículo estables de GET /v1/fulfillment/shipments/item-attributes. Cadenas únicas, máximo 100.
Las cotizaciones se limitan primero a los canales aplicables a destination.country_code; se omiten los canales de otros países. Cada canal restante evalúa por separado los atributos declarados. Un atributo no admitido produce UNAVAILABLE con ATTRIBUTE_NOT_SUPPORTED, mientras que otros canales aplicables aún pueden devolver cotizaciones.
services
{
"services": {
"channel": [
{
"code": "LINE_INSURANCE",
"quantity": 1
}
],
"outbound": [
{
"code": "VACUUM_PACKING",
"quantity": 1
}
],
"inbound": [
{
"code": "PHOTO",
"quantity": 3
}
]
}
}| Grupo | Significado |
|---|---|
channel | Servicios de valor añadido del canal. Los códigos provienen de canales value_added_services[].code. |
outbound | Procesamiento de salida en almacén |
inbound | Procesamiento de entrada en almacén |
Cada elemento exige code y quantity. OpenAPI: quantity es un entero de 1 a 999. Los códigos deben ser únicos dentro de cada grupo.
channel_codes
Códigos de canal estables que restringen el conjunto de cotizaciones. Omita para cotizar todos los canales candidatos. No envíe un array vacío. Un código desconocido o inaccesible devuelve 400 INVALID_SHIPPING_CHANNEL para toda la solicitud.
Ejemplo de solicitud completa {#complete-request}
Formato A con destino, dimensiones, valor declarado, atributos, servicios y un filtro de canal:
{
"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"
]
}Respuesta {#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."
}Campos de primer nivel
| Campo | Descripción |
|---|---|
success | true en una estimación correcta, incluso cuando algunas cotizaciones son UNAVAILABLE. |
estimate_type | DECLARED — basado en datos declarados, no en medición de almacén. |
completeness | Qué tan completa es la entrada. No es una garantía de entregabilidad. |
warnings | Advertencias a nivel de envío. |
quotes | Resultados por canal: completos primero, luego parciales/revisión, luego no disponibles. |
monetary_unit | Siempre CNY_minor. |
generated_at | Marca de tiempo de la respuesta. |
request_id | Correlacione con x-request-id. |
disclaimer | Descargo de responsabilidad legible de la estimación. |
completeness
| Campo | Significado |
|---|---|
level | Cobertura global de la entrada, por ejemplo HIGH. |
destination | Qué tan completo está el destino (COMPLETE cuando el país está presente). |
package_dimensions | Si se proporcionaron longitud/anchura/altura. |
package_split | DECLARED cuando envió packages[]; el atajo supone un paquete único. |
declared_value | PROVIDED u omitido/desconocido. |
attributes | Si se declararon atributos de artículo. |
services | REQUESTED_ONLY cuando envió servicios seleccionados; el almacén aún puede añadir más. |
Estado de la cotización {#quote-status}
No decida solo a partir de available. Lea siempre quote_status.
quote_status | available | total | Significado |
|---|---|---|---|
COMPLETE | true | Objeto monetario | Presupuesto completo que puede mostrar. Sigue sin estar bloqueado. |
PARTIAL | true | generalmente null | Faltan entradas. Lea las advertencias y añada dimensiones, código postal o valor declarado. |
REVIEW_REQUIRED | true | generalmente null | Se requiere medición de almacén o revisión humana. |
UNAVAILABLE | false | null | Este canal no puede cotizar. La solicitud HTTP igualmente se completó. |
totales el presupuesto completo cuando se conoce.known_totales la suma de los cargos que se pueden calcular ahora.- Cuando
totalesnull, no trateknown_totalcomo una cotización completa. unavailable_reason.codees para el manejo programático.unavailable_reason.messagees solo para visualización.
UNAVAILABLE en una cotización no es un error HTTP.
Objeto de cotización {#quotes}
| Campo | Descripción |
|---|---|
channel | { code, name, description, tags, capabilities } — misma identidad que el catálogo de canales. |
channel.capabilities.delivery_methods | Array opcional y compatible de códigos estables de entrega: DOOR_DELIVERY, PICKUP, POST_OFFICE_PICKUP. Puede aparecer con cualquier estado; los clientes existentes pueden ignorarlo. |
available | Si este canal produjo una estimación usable. Combínelo con quote_status. |
quote_status | COMPLETE · PARTIAL · REVIEW_REQUIRED · UNAVAILABLE. |
unavailable_reason | { code, message } o null. |
attribute_evaluation | Resultado opcional por canal: SUPPORTED, UNSUPPORTED o NOT_DECLARED, con listas de atributos declarados, aceptados y no admitidos. |
matched_region | { code, name, match_type } o null. |
packages | Paquetes declarados o mapeados más pesos por paquete. |
weights | actual / volumetric / chargeable a nivel de envío, en KG. |
billing_quantity | Cantidad que el canal factura realmente, con unidad KG, M3 o KG_PER_M3. |
pricing_selector | Cómo esa cantidad alcanzó una banda de tarifa (type, value, unit, scope). |
transit_time | Tiempo de tránsito de referencia. |
charges | Grupos de cargos. Véase más abajo. |
display_charges | Filas opcionales solo para UI. Si included_in_total es false, no las sume a total. |
total / known_total | Presupuesto completo vs cargos actualmente conocidos. |
warnings | Advertencias a nivel de canal. |
service_adjustment_possible | true si el procesamiento en almacén aún puede cambiar servicios y cargos. |
Pesos y cantidad facturable {#weights}
actual— peso declarado o medido.volumetric— peso volumétrico.chargeable— peso usado para el flete.billing_quantity— cantidad que el canal factura (puede ser peso, volumen o densidad).pricing_selector— cómo esa cantidad seleccionó una fila tarifaria. Los tipos coinciden con el catálogoWEIGHT/VOLUME/DENSITY.- Los canales multi-paquete pueden redondear por caja y luego sumar. Cuando
pricing_selector.scopeesPER_PACKAGE, lea cadapackages[].pricing_selector.
Cargos {#charges}
| Grupo | Significado |
|---|---|
base_freight | Flete internacional base |
channel_services | Servicios de canal obligatorios y seleccionados por el desarrollador |
channel_rules | Sobredimensión, sobrepeso, valor declarado y otras reglas de ruta |
outbound_services | Servicios de valor añadido durante la preparación y salida del envío |
inbound_services | Servicios de valor añadido durante la recepción y entrada en almacén |
adjustments | Ajustes |
Servicios de entrada y salida
inbound_services y outbound_services corresponden a etapas distintas de los servicios de valor añadido del almacén. Los servicios de entrada se realizan durante la recepción, como fotos del paquete, grabación de vídeo o inspección. Los servicios de salida se realizan al preparar el envío, como refuerzo, envasado al vacío o embalaje con marco/caja de madera. Ambas etapas pueden aplicarse y cobrarse legítimamente al mismo paquete o envío.
Los importes de la cotización son solo estimaciones; solicitar una cotización no crea un cargo de servicio adicional. Los cargos definitivos dependen de los servicios realmente realizados y de su precio y liquidación finales. Use ambos grupos para estimar el coste total y concilie cada estimación con su cargo real correspondiente. No vuelva a sumar la estimación de un servicio individual sobre su cargo final.
Ejemplo en unidades menores de CNY: fotos del paquete 50 fen (¥0,50), inspección 100 fen (¥1,00), envasado al vacío 150 fen (¥1,50) y refuerzo del paquete 200 fen (¥2,00). Los cuatro pueden coexistir porque son servicios distintos realizados en etapas diferentes.
Cada grupo suele incluir:
| Campo | Significado |
|---|---|
amount | Total del grupo en fen, o null si es desconocido |
currency | CNY |
known_amount | Suma de los elementos que se pueden calcular ahora |
calculation_status | Por ejemplo CALCULATED |
items | Líneas de detalle |
channel_rules también puede incluir aggregation y cap. Use el subtotal del grupo: no lo sustituya sumando las líneas de reglas en bruto.
SUM_ALL suma todos los cargos coincidentes. HIGHEST_ONLY selecciona solo el mayor tras ordenar por importe descendente; las reglas con el mismo precio son equivalentes. cap=null significa sin límite. reason es solo para mostrar. Para lógica use condition_match, matched_conditions, rule_type, charge_mode, charge_value, outcome y missing_fields. Las reglas PENDING_INPUT, REVIEW_REQUIRED y otras no calculadas se excluyen del total conocido. amount=null no es cero y un charge_value configurado no es un cargo real.
Ítems de cargo
| Campo | Significado |
|---|---|
code | Código estable de cargo o servicio |
name | Nombre de visualización localizado |
category | Categoría de cargo |
source | MANDATORY · DEVELOPER_SELECTED · RULE_ENGINE |
pricing_basis | Cómo se tarifó la línea |
quantity | Cantidad facturable de la línea |
unit_price | Precio unitario en fen cuando se conoce |
amount | Total de la línea. null significa desconocido, no cero. |
estimated | Si la línea sigue siendo una estimación |
calculation_status | Estado del cálculo |
affected_by_packages | Si el número/tamaño de paquetes puede cambiar esta línea |
included_in_total | Si es false, no la sume de nuevo a total |
display_charges puede existir solo para la presentación frontend.
Motivos de no disponibilidad {#unavailable-reasons}
Valores comunes de unavailable_reason.code:
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_ERRORLos canales no disponibles permanecen en quotes[] para que pueda explicar por qué se rechazaron.
Terminología del catálogo {#catalog-terminology}
| Canales de envío | Esta API | Significado |
|---|---|---|
items[].code/name/description/tags | quotes[].channel.* | El mismo canal |
regions[].code/name/match_type | quotes[].matched_region.* | Región seleccionada por la cotización |
value_added_services[].code | services.channel[].code y code del cargo | Código de servicio estable |
billing.quantity_unit | billing_quantity.unit / pricing_selector.unit | Unidad de facturación |
rule_summary.aggregation | charges.channel_rules.aggregation | Agregación de reglas |
rule_summary.cap | charges.channel_rules.cap | Límite tras la agregación; null significa sin límite |
Atributos de artículo {#item-attributes}
GET /v1/fulfillment/shipments/item-attributes devuelve los códigos attributes[] válidos para las cotizaciones.
Errores {#errors}
| HTTP | error.code | Cuándo |
|---|---|---|
| 400 | VALIDATION_ERROR | Estructura inválida, formatos de solicitud mixtos, unidades KG/CM, códigos duplicados o channel_codes vacío |
| 400 | INVALID_SHIPPING_CHANNEL | Un valor de channel_codes solicitado es desconocido o no visible para la app |
| 401 | INVALID_API_KEY | Token Bearer ausente o inválido |
| 403 | FULFILLMENT_MODE_NOT_SUPPORTED | La app usa self fulfillment |
| 403 | WAREHOUSE_AUTH_INVALID | La autorización del almacén falta, fue rechazada o expiró |
| 422 | INVALID_COUNTRY_CODE | El país de destino es inválido |
| 422 | INVALID_ITEM_ATTRIBUTE | Un código de atributo es inválido |
| 502 | WAREHOUSE_UPSTREAM_ERROR | El servicio del almacén falló; reintente con backoff |
No hay un código público distinto AUTHENTICATION_ERROR, WAREHOUSE_AUTH_REQUIRED ni WAREHOUSE_SERVICE_UNAVAILABLE. Use 401 INVALID_API_KEY, 403 WAREHOUSE_AUTH_INVALID y 502 WAREHOUSE_UPSTREAM_ERROR.
Un canal que no puede transportar este envío es una cotización UNAVAILABLE, no un fallo HTTP.
Relacionado {#related}
- Canales de envío — capacidades, cobertura y códigos de servicio
- Envíos — crear un envío después de que los paquetes estén en entrada
- Resumen de fulfillment
Get Support
Need integration help? Contact Developer Support at support@hiobuy.com · Response within 1–2 business days