Saltar al contenido
SendIt está en desarrollo y todavía no opera comercialmente · SendIt is in development and not yet commercially available.
SendItdocs
Español
Esc
navigateopen⌘Jpreview
En esta página

Envíos

El objeto shipment, su ciclo de vida y cómo crear, consultar y cancelar envíos.

El envío (shipment) es la entidad central del API: describe un paquete que viaja de un origen a un destino. Se crea en estado DRAFT, se convierte en guía al comprar una tarifa y avanza por su ciclo de vida hasta la entrega.

El ciclo de vida

DRAFT → LABEL_PURCHASED → READY_FOR_PICKUP → PICKED_UP → IN_TRANSIT → OUT_FOR_DELIVERY → DELIVERED
Estado Significado
DRAFT Recién creado; con cotizaciones, editable y cancelable sin costo
PENDING Enviado, en espera de compra de guía
LABEL_PURCHASED Guía comprada, en espera de recolección
READY_FOR_PICKUP Paquetería notificada para recolectar
PICKED_UP La paquetería recogió el paquete
IN_TRANSIT En camino
OUT_FOR_DELIVERY En reparto de última milla
DELIVERED Entregado
RETURNED Devuelto al remitente
FAILED Entrega fallida
CANCELLED Cancelado

Dos maneras de comprar una guía

POST /v1/shipments sirve a dos flujos. Elige el que corresponda a cada envío:

1. En dos pasos (compara y elige). Crea el envío; la respuesta trae rates[] con las cotizaciones de las paqueterías disponibles. Comparas precio y velocidad, eliges una y compras con su rateId en POST /v1/shipments/:id/label. Ideal cuando el costo o la velocidad deciden en cada envío. Si ya sabes con quién enviar, pasa carrierCode y serviceLevel al crear para acotar las cotizaciones a una categoría.

2. En una llamada. Si ya sabes con qué paquetería y servicio enviar, o solo quieres la más barata, agrega un objeto purchase al crear el envío. La guía llega en la misma respuesta, sin segundo paso. Sirve para automatización y volumen predecible. Ver Compra en una llamada.

En dos pasos En una llamada
Cuándo usarla El precio o la velocidad deciden en cada envío Ya sabes el servicio, o quieres la tarifa más barata
Peticiones Crear → comprar con rateId Una sola: POST /v1/shipments con purchase
Qué regresa rates[] para comparar La guía (label + purchasedRate) lista
Ideal para Tiendas que optimizan por costo Automatización y volumen predecible

Endpoints

Método Ruta Alcance Descripción
POST /v1/shipments shipments:write Crear un envío (devuelve rates[] inline; con purchase, compra la guía en la misma llamada)
GET /v1/shipments shipments:read Listar envíos (paginado y filtrable)
GET /v1/shipments/stats shipments:read Conteo de envíos por estado
GET /v1/shipments/:id shipments:read Detalle con snapshots, guía y eventos
PUT /v1/shipments/:id shipments:write Actualizar un envío en DRAFT
DELETE /v1/shipments/:id shipments:write Cancelar un envío
POST /v1/shipments/:id/return shipments:write Crear un envío de retorno (ruta invertida)

Crea un envío

Para cada rol de dirección (from, to, return) envía exactamente una de dos variantes: el ID de una dirección guardada (fromAddressId) o un objeto inline (fromAddress). Las direcciones inline pueden guardarse en tu directorio con saveToAddressBook: true.

Parámetros del cuerpo

PropType
parcelobjeto

El paquete: length, width y height (cm) y weight (kg). Acepta dimensionUnit (cm | in), weightUnit (kg | g | lb | oz) y description.

Typeobjeto
fromAddressId?string

Origen guardado. Envía exactamente uno de fromAddressId o fromAddress.

Typestring
fromAddress?objeto

Origen inline (ver el objeto dirección). Puede guardarse con saveToAddressBook: true.

Typeobjeto
toAddressId?string

Destino guardado. Envía exactamente uno de toAddressId o toAddress.

Typestring
toAddress?objeto

Destino inline (ver el objeto dirección).

Typeobjeto
returnAddressId?string

Retorno guardado (máximo uno; por defecto se usa el origen).

Typestring
returnAddress?objeto

Retorno inline.

Typeobjeto
purchase?objeto

Compra la guía en la misma llamada: carrierCode + serviceCode (servicio exacto) o strategy: cheapest. Ver Compra en una llamada.

Typeobjeto
externalId?string

Tu propia referencia, como el número de orden o folio. Búscala después con ?externalId=.

Typestring
carrierCode?string

Opcional: acota las cotizaciones a esta paquetería desde la creación (no compra por sí solo).

Typestring
serviceLevel?string

Opcional: acota el servicio (p. ej. standard, express).

Typestring
requestInsurance?boolean

Solicitar seguro sobre el valor declarado. Al comprar una tarifa asegurada se crea una póliza real (ver Seguro y reclamaciones).

Typeboolean
Defaultfalse
declaredValue?number

Valor declarado en MXN (cobertura del seguro).

Typenumber
metadata?objeto

Pares llave-valor tuyos; se devuelven tal cual.

Typeobjeto
curl -X POST https://api.sendit.mx/v1/shipments \
  -H "X-API-Key: sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "ORD-2026-001",
    "fromAddressId": "clx_direccion_origen",
    "toAddress": {
      "contactName": "María López",
      "contactPhone": "+5213312345678",
      "street": "Av. López Mateos",
      "exteriorNumber": "45",
      "neighborhood": "Jardines del Sol",
      "city": "Zapopan",
      "state": "JAL",
      "postalCode": "45050",
      "country": "MX",
      "saveToAddressBook": false
    },
    "parcel": {
      "length": 30, "width": 20, "height": 15, "weight": 2.5,
      "description": "Electrónicos"
    },
    "requestInsurance": true,
    "declaredValue": 5000,
    "metadata": { "orderId": "shopify-12345" }
  }'

La respuesta 201 trae el envío en DRAFT con sus snapshots de dirección, el historial de eventos y las cotizaciones inline (rates[], ver Cotizaciones):

{
  "success": true,
  "data": {
    "id": "clxq1w2e3r4t5y6u7i8o9p0a",
    "status": "DRAFT",
    "externalId": "ORD-2026-001",
    "fromAddressSnapshot": {
      "contactName": "Bruno Sánchez",
      "city": "Ciudad de México",
      "postalCode": "03100",
      "savedAddressId": "clx_direccion_origen"
    },
    "toAddressSnapshot": {
      "contactName": "María López",
      "city": "Zapopan",
      "postalCode": "45050",
      "savedAddressId": null
    },
    "parcel": { "length": 30, "width": 20, "height": 15, "weight": 2.5 },
    "rates": [
      {
        "id": "DHL_standard_a1b2c3",
        "carrierCode": "DHL",
        "serviceCode": "EXPRESS_WORLDWIDE",
        "serviceLevel": "standard",
        "totalPrice": 326.82,
        "currency": "MXN"
      }
    ],
    "events": [
      { "status": "DRAFT", "description": "Shipment created", "occurredAt": "2026-07-17T12:00:00.000Z" }
    ],
    "createdAt": "2026-07-17T12:00:00.000Z"
  }
}

Compra en una llamada

Agrega un objeto purchase a POST /v1/shipments para crear el envío y comprar la guía en una sola petición. Elige una forma de seleccionar el servicio: carrierCode + serviceCode para el producto exacto, o strategy: "cheapest" para la tarifa más barata. Nunca mandes las dos ni omitas ambas. Con una llave de alcance acotado necesitas labels:write además de shipments:write.

Parámetros de purchase

PropType
carrierCode?string

Paquetería exacta; se usa junto con serviceCode. Excluyente con strategy.

Typestring
serviceCode?string

Código nativo del servicio; se usa junto con carrierCode.

Typestring
strategy?string

cheapest: selecciona automáticamente la tarifa más barata. Excluyente con carrierCode + serviceCode.

Typestring
labelFormat?string

Formato de la guía generada: PDF | ZPL.

Typestring
DefaultPDF
async?boolean

true devuelve labelPurchaseAttempt dentro de la respuesta 201. Consulta su statusUrl.

Typeboolean
Defaultfalse
curl -X POST https://api.sendit.mx/v1/shipments \
  -H "X-API-Key: sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "fromAddressId": "clx_direccion_origen",
    "toAddress": {
      "contactName": "María López",
      "contactPhone": "+5213312345678",
      "street": "Av. López Mateos",
      "exteriorNumber": "45",
      "neighborhood": "Jardines del Sol",
      "city": "Zapopan",
      "state": "JAL",
      "postalCode": "45050",
      "country": "MX"
    },
    "parcel": { "length": 30, "width": 20, "height": 15, "weight": 2.5 },
    "purchase": { "strategy": "cheapest", "labelFormat": "PDF" }
  }'

En éxito, la respuesta 201 incluye además label (con trackingNumber, labelUrl, charged) y purchasedRate:

{
  "success": true,
  "data": {
    "id": "clxq1w2e3r4t5y6u7i8o9p0a",
    "status": "LABEL_PURCHASED",
    "label": {
      "trackingNumber": "1234567890",
      "labelUrl": "https://labels.sendit.mx/clxq1w2e3r4t5y6u7i8o9p0a/1234567890.pdf",
      "charged": "326.82",
      "currency": "MXN"
    },
    "purchasedRate": {
      "id": "DHL_standard_a1b2c3",
      "carrierCode": "DHL",
      "serviceCode": "EXPRESS_WORLDWIDE",
      "serviceLevel": "standard",
      "totalPrice": 326.82
    }
  }
}

Con purchase.async: true, la respuesta externa sigue siendo 201. Recibes labelPurchaseAttempt en lugar de label. Consulta su statusUrl o usa label.purchase.completed. Ver Operaciones asíncronas.

El envío siempre se crea primero: si la compra falla, te queda un DRAFT que puedes terminar por el flujo de dos pasos.

Código Cuándo Cómo resolverlo
402 INSUFFICIENT_BALANCE El monedero no cubre la guía El envío queda en DRAFT; fondea el monedero y compra con POST /v1/shipments/:id/label
422 ONE_CALL_BUY_RATES_PENDING Las paqueterías tardaron (>8 s) en cotizar Sondea ratesPollUrl y compra con el rateId
422 ONE_CALL_BUY_NO_MATCHING_RATE Ninguna tarifa coincidió con tu selección Elige una de details.availableRates
400 INVALID_INPUT purchase traía ambas formas de selección, o ninguna Envía carrierCode + serviceCode o strategy, no las dos

Manda varias cajas juntas

Un envío lleva un parcel. Para despachar varias cajas en la misma operación, crea un envío por caja y agrúpalos en un lote. Compras todas las guías en una llamada y generas un solo manifiesto para la paquetería.

Las direcciones se congelan en snapshots

Al crear el envío, cada dirección se copia a un snapshot inmutable (fromAddressSnapshot, toAddressSnapshot, returnAddressSnapshot). Editar o borrar después la dirección guardada jamás altera los envíos históricos.

Siempre lee la dirección desde el snapshot, no desde el ID:

// Correcto: el snapshot es el valor canónico
const origin = shipment.fromAddressSnapshot;

// Incorrecto: la dirección guardada pudo cambiar o borrarse
const origin = await getAddress(shipment.fromAddressId);

Dentro del snapshot, savedAddressId indica de qué entrada del directorio provino (null si fue una dirección de un solo uso).

Lista y filtra

GET /v1/shipments?status=IN_TRANSIT&carrierCode=DHL&limit=50
Filtro Coincidencia
status Exacta (DRAFT, IN_TRANSIT, …)
statuses Varios estados a la vez (repite el parámetro o sepáralos por coma)
carrierCode Exacta (DHL, FEDEX, ESTAFETA, …)
trackingNumber Parcial (contiene)
externalId Exacta
search Texto libre sobre número de rastreo y externalId
createdFrom / createdTo Rango de fechas de creación (ISO; from inclusivo, to exclusivo)

La paginación por cursor y los operadores avanzados están en Paginación y filtros. Para tableros, GET /v1/shipments/stats devuelve el conteo por estado en una sola llamada.

Consulta el detalle

GET /v1/shipments/:id devuelve el envío completo: snapshots, parcel, resumen de la guía (si existe) y el historial de eventos:

{
  "success": true,
  "data": {
    "id": "clxq1w2e3r4t5y6u7i8o9p0a",
    "status": "IN_TRANSIT",
    "trackingNumber": "TEST-DHL-A1B2C3D4",
    "publicTrackingToken": "2f68c611-30af-4a86-9cde-793413af5f65",
    "trackingUrl": "https://app.sendit.mx/track/2f68c611-30af-4a86-9cde-793413af5f65",
    "events": [
      { "status": "IN_TRANSIT", "description": "Package in transit", "occurredAt": "2026-07-17T09:12:00.000Z" },
      { "status": "PICKED_UP", "description": "Package picked up", "occurredAt": "2026-07-17T08:03:00.000Z" }
    ]
  }
}

Actualiza un borrador

Solo los envíos en DRAFT se pueden editar; después quedan bloqueados:

curl -X PUT https://api.sendit.mx/v1/shipments/{id} \
  -H "X-API-Key: sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{ "externalId": "ORD-2026-001-v2" }'

Un envío que ya avanzó devuelve 409 SHIPMENT_ALREADY_PROCESSED.

Cancela un envío

curl -X DELETE https://api.sendit.mx/v1/shipments/{id} \
  -H "X-API-Key: sk_test_..."

No se puede cancelar en DELIVERED, RETURNED, FAILED ni si ya está CANCELLED. Si el envío ya tiene guía comprada y quieres el reembolso, cancela primero la guía. Ver Cancelaciones y reembolsos.

Crea un envío de retorno

POST /v1/shipments/:id/return crea un nuevo envío en DRAFT con la ruta invertida, ligado al original vía returnForShipmentId:

  • from = el destino original (donde está el paquete ahora)
  • to = la dirección de retorno original, o en su defecto el origen
  • parcel = copiado del original (puedes sobreescribirlo si se reempaca)

La respuesta es idéntica a la de POST /v1/shipments, con rates[] inline. La guía de retorno se compra por el flujo normal. Reglas:

  • El original debe tener guía (los DRAFT, PENDING, CANCELLED y RETURNED se rechazan).
  • Un retorno activo por envío. Cancelar el retorno libera el cupo.
  • No hay retornos de retornos.
  • En modo de prueba funciona de punta a punta.

¿Te ha resultado útil esta página?