Preparación y Envío

Los endpoints de fulfillment cubren la ruta desde un pedido aceptado hasta un paquete listo para salir: preparación, selección de paquetería, generación de guías y actualización de rastreo. Existen dos familias:

  • Por pedido (/orders/{orderId}/prepare, /orders/{orderId}/fulfill): pensados para flujos interactivos, un pedido a la vez.
  • En lote (/orders/fulfillment/*): pensados para procesar muchos pedidos en una sola llamada, típicamente desde el dashboard del comerciante o una tarea programada.

Envelope de respuesta

Toda respuesta exitosa de recurso único sigue { "data": {...} }; las listas usan { "data": [...], "meta": { "pagination": {...} } }. Los errores siempre son { "error": { "code", "message", "details?" } } con códigos namespace:snake_case (ej. orders:cannot_fulfill).

Para los eventos posteriores al envío (marcar como entregado, subir evidencia de entrega, adjuntar guía externa) consulta Entrega y Envíos.

Marcar un pedido como en preparación

Marca un pedido como en preparación. Requiere indicar qué ítems y en qué cantidad se están preparando en este lote.

orderIdstringrequired

ID del pedido

itemsarrayrequired

Ítems a preparar en este lote. Mínimo 1 elemento.

shippingInfoobject

Información de envío a asociar a esta preparación (opcional).

{
  "items": [{ "itemIndex": 0, "quantityToPrepare": 2 }]
}
200
{
  "data": {
    "_id": "65f3a1b2c4d5e6f7a8b9c0d1",
    "orderStatus": "preparing",
    "_links": {
      "fulfill": "/orders/65f3a1b2c4d5e6f7a8b9c0d1/fulfill",
      "cancel": "/orders/65f3a1b2c4d5e6f7a8b9c0d1/cancel"
    }
  }
}

Permiso requerido: orders:fulfill

Forma de items

Cada elemento identifica un ítem del pedido por su posición, no por SKU:

CampoTipoRequeridoDescripción
itemIndexnumberÍndice (≥0) del ítem dentro del arreglo items del pedido.
quantityToPreparenumberCantidad a preparar de ese ítem (> 0).

Forma de shippingInfo (opcional)

CampoTipoRequeridoDescripción
carrierstringSí, si envías shippingInfoCódigo de paquetería.
trackingNumberstringSí, si envías shippingInfoNúmero de rastreo.
trackingUrlstringNoURL de rastreo pública.
customCarrierNamestringSí, si carrier es "other"Nombre de la paquetería cuando no está en el catálogo.
shippingCostobjectNo{ amount: number, currency: string }.
providerstringNoProveedor de envío que originó la cotización.

items es obligatorio

A diferencia de lo que sugiere el nombre, items no es opcional: se requiere al menos un elemento con itemIndex y quantityToPrepare. Enviar el arreglo vacío o solo una lista de SKUs es rechazado.

curl -X POST https://api.fenicia.io/orders/65f3a1b2c4d5e6f7a8b9c0d1/prepare \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"itemIndex":0,"quantityToPrepare":2}]}'

Procesar (fulfill) un pedido

Marca los ítems indicados como procesados. No acepta información de rastreo directamente.

orderIdstringrequired

ID del pedido

itemsstring[]required

Identificadores de los ítems a marcar como procesados. Mínimo 1 elemento.

{
  "items": ["65f3a1b2c4d5e6f7a8b9c0e0"]
}
204Sin cuerpo
(sin contenido)

Permiso requerido: orders:fulfill

204 No Content — sin cuerpo

Este endpoint responde 204 No Content, siempre sin cuerpo. No incluye trackingNumber ni carrier en el request ni en la respuesta — esos campos no existen en este endpoint. Para adjuntar rastreo usa POST /orders/fulfillment/tracking o POST /orders/{orderId}/attach-shipment.

curl -X POST https://api.fenicia.io/orders/65f3a1b2c4d5e6f7a8b9c0d1/fulfill \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"items":["65f3a1b2c4d5e6f7a8b9c0e0"]}'

Capacidades de fulfillment (lote)

Devuelve, para cada pedido, qué acciones de fulfillment están disponibles y cuáles bloqueadas.

orderIdsstring[]required

IDs de pedido a inspeccionar. Mínimo 1.

credentialsobject

Mapa channelId → credenciales, para uso orquestado. Normalmente no lo necesitas si llamas desde fuera de Fenicia.

{
  "orderIds": ["65f3a1b2c4d5e6f7a8b9c0d1"]
}
200
{
  "data": {
    "capabilities": {
      "65f3a1b2c4d5e6f7a8b9c0d1": {
        "canDownloadLabel": true,
        "labelBlockedReason": null,
        "canUpdateTracking": true,
        "canMarkAsShipped": false,
        "fulfillmentType": "carrier",
        "channelType": "shopify"
      }
    },
    "orderCount": 1
  }
}

Permiso requerido: orders:read

Más campos de los mostrados

Cada objeto de capacidades trae más banderas además de las mostradas arriba (en total, alrededor de 13 campos por pedido). Los que se listan aquí están confirmados; para el listado exhaustivo consulta con soporte antes de depender de un campo no documentado.

curl -X POST https://api.fenicia.io/orders/fulfillment/capabilities \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"orderIds":["65f3a1b2c4d5e6f7a8b9c0d1"]}'

Generar guías de envío (lote)

Genera guías de envío para uno o varios pedidos en una sola llamada.

orderIdsstring[]required

IDs de pedido

credentialsobject

Mapa channelId → credenciales, para uso orquestado.

optionsobject

Opciones de generación, anidadas (ver abajo).

{
  "orderIds": ["65f3a1b2c4d5e6f7a8b9c0d1"],
  "options": { "format": "pdf", "size": "medium" }
}
200
{
  "data": {
    "labels": [ { "...": "..." } ],
    "summary": { "total": 2, "success": 2, "failed": 0, "skipped": 0 }
  }
}

Permiso requerido: orders:fulfill

Forma de options (opcional)

CampoTipoDescripción
format'pdf' | 'zpl' | 'png'Formato de salida de la guía.
size'small' | 'medium' | 'large'Tamaño de la guía (relevante para impresoras térmicas).

options va anidado

format y size viajan dentro de options, no en la raíz del body: { "orderIds": [...], "options": { "format": "zpl", "size": "small" } }.

Shape de labels[] no auditado a detalle

summary está confirmado tal como se muestra. El shape exacto de cada elemento de labels[] (y del campo opcional mergedLabel) no fue auditado campo por campo contra el código fuente — confirma el contrato exacto con soporte antes de depender de un campo específico dentro de cada resultado.

curl -X POST https://api.fenicia.io/orders/fulfillment/labels \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"orderIds":["65f3a1b2c4d5e6f7a8b9c0d1"],"options":{"format":"pdf","size":"medium"}}'

Actualizar rastreo (lote)

Adjunta o actualiza información de rastreo para uno o varios pedidos en una sola llamada.

updatesarrayrequired

Arreglo de actualizaciones, una por pedido (ver forma abajo).

credentialsobject

Mapa channelId → credenciales, para uso orquestado.

{
  "updates": [
    { "orderId": "65f3a1b2c4d5e6f7a8b9c0d1", "tracking": { "trackingNumber": "1Z999AA10123456784", "carrierId": "fedex" } }
  ]
}
200
{
  "data": {
    "updated": 1,
    "failed": 0,
    "results": [
      { "orderId": "65f3a1b2c4d5e6f7a8b9c0d1", "success": true }
    ]
  }
}

Permiso requerido: orders:fulfill

Forma de cada elemento de updates

{
  "orderId": "65f3a1b2c4d5e6f7a8b9c0d1",
  "tracking": {
    "trackingNumber": "1Z999AA10123456784",
    "carrierId": "fedex",
    "carrierName": "FedEx",
    "trackingUrl": "https://fedex.com/track?n=1Z999AA10123456784"
  }
}

Dentro de tracking, solo trackingNumber es requerido; carrierId, carrierName y trackingUrl son opcionales.

Este endpoint procesa un lote, no un pedido

El body no es { "orderId", "trackingNumber", "carrier" } a nivel raíz — es un arreglo updates[], y la información de rastreo va anidada bajo la clave tracking. El campo carrier (a secas) no existe en este endpoint.

curl -X POST https://api.fenicia.io/orders/fulfillment/tracking \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"updates":[{"orderId":"65f3a1b2c4d5e6f7a8b9c0d1","tracking":{"trackingNumber":"1Z999AA10123456784","carrierId":"fedex"}}]}'

Marcar como enviados (lote)

Marca múltiples pedidos como enviados en una sola llamada.

orderIdsstring[]required

IDs de pedido a marcar como enviados

credentialsobject

Mapa channelId → credenciales, para uso orquestado.

shipmentInfoobject

Información de envío a aplicar a todos los pedidos del lote (ver forma abajo).

{
  "orderIds": ["65f3a1b2c4d5e6f7a8b9c0d1"],
  "shipmentInfo": { "notifyCustomer": true }
}
200
{
  "data": {
    "shipped": 2,
    "failed": 0,
    "results": [
      { "orderId": "65f3a1b2c4d5e6f7a8b9c0d1", "success": true, "markedAsShipped": true, "customerNotified": true },
      { "orderId": "65f3a1b2c4d5e6f7a8b9c0d2", "success": true, "trackingUpdated": true }
    ]
  }
}

Permiso requerido: orders:fulfill

Forma de shipmentInfo (opcional)

CampoTipoDescripción
trackingobjectInformación de rastreo a aplicar.
shippedAtstring (ISO)Fecha/hora de envío a registrar.
notifyCustomerbooleanSi se envía notificación al cliente.

No existe carrier en la raíz

El campo carrier a nivel raíz del body no existe. La información de paquetería, si aplica, va dentro de shipmentInfo.tracking.

curl -X POST https://api.fenicia.io/orders/fulfillment/ship \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"orderIds":["65f3a1b2c4d5e6f7a8b9c0d1"],"shipmentInfo":{"notifyCustomer":true}}'

Paqueterías disponibles

Devuelve las paqueterías disponibles, según cómo la consultes: por pedidos, por canal, o el catálogo por defecto.

orderIdsstring[]

IDs de pedido — si los envías, la respuesta se resuelve por pedido.

channelTypestring

Tipo de canal — si lo envías (sin orderIds), la respuesta se resuelve por canal.

credentialsobject

Mapa channelId → credenciales, para uso orquestado.

{
  "orderIds": ["65f3a1b2c4d5e6f7a8b9c0d1"]
}
200
{
  "data": {
    "mode": "by-orders"
  }
}

Permiso requerido: orders:read

Respuesta discriminada por modo

Todos los parámetros son opcionales. La forma exacta de la respuesta depende del modo resuelto (by-orders, by-channel o default) y no fue auditada campo por campo para cada variante — el campo mode sí está confirmado como discriminador. Antes de parsear la respuesta programáticamente, valida el shape de cada modo con soporte.

curl -X POST https://api.fenicia.io/orders/fulfillment/carriers \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{"orderIds":["65f3a1b2c4d5e6f7a8b9c0d1"]}'

Errores

CódigoStatusDescripción
orders:not_found404No existe un pedido con ese ID en tu tenant.
orders:cannot_fulfill422El pedido no está en un estado desde el que se pueda procesar.
orders:items_required400El body no incluye ítems (o el arreglo está vacío).
orders:shipping_info_required400Falta información de envío requerida para la operación.
orders:shipping_label_not_available422No hay guía disponible para generar/descargar en el estado actual.
orders:channel_unsupported422El canal del pedido no soporta esta operación de fulfillment.
sync:integration_error / channels:sync_failed502Falla al comunicarse con la integración del canal (paquetería/marketplace).
sync:rate_limited429Límite de tasa alcanzado contra el proveedor externo.
auth:permission_denied403La API key no tiene el permiso requerido.
auth:invalid_token401La API key es inválida o fue revocada.
validation:invalid_input400El body no cumple el schema esperado.

Formato de error

Todo error se devuelve envuelto: { "error": { "code": "orders:cannot_fulfill", "message": "...", "details": {...} } }. Los códigos siguen siempre namespace:snake_case.

Siguientes pasos