# Uniorder: una API para cada envío

Uniorder es un único conjunto de endpoints con el que una integración cotiza, crea, imprime, sigue y cancela cada envío de la cuenta, sea cual sea la forma en que se realiza el envío. Una sola cotización devuelve la entrega realizada por la propia empresa y, si se solicita, cada servicio de etiquetas de transportista de la cuenta, cada uno con un `rate_id`. El pedido se crea devolviendo el `rate_id` elegido; ningún otro dato de la solicitud selecciona el servicio.

## 1. Qué puede construir

- **Un proceso de pago que ofrece todas las opciones de envío a la vez.** El cliente introduce una dirección, el proceso de pago llama a un endpoint y la página muestra la entrega local junto a UPS, Canada Post y cualquier otro transportista que use la cuenta, cada uno con su precio.
- **Un conector de gestión de pedidos o ERP con una sola ruta de código.** Los pedidos de todos los canales pasan por las mismas llamadas de creación, consulta, etiqueta, seguimiento y cancelación. El conector no necesita una lógica distinta para la entrega local y para las etiquetas de transportista.
- **Procesamiento masivo nocturno.** Hasta 500 envíos se cotizan o se crean en un único trabajo en cola, y los resultados se leen por el id del trabajo.
- **Una pantalla de atención al cliente.** Un agente busca un pedido, reimprime su etiqueta, lee su historial de seguimiento y lo cancela, con las mismas cuatro llamadas para cada pedido.

## 2. Qué hace Uniorder por usted

| Sin Uniorder | Con Uniorder |
|---|---|
| Una API para los pedidos de entrega local y otra para las etiquetas de transportista, cada una con sus propios campos y respuestas | Una sola forma de solicitud (`from_*`, `to_*`, `packages`) y una sola forma de respuesta para cada servicio |
| La integración decide a qué API de transportista llamar | La cotización enumera cada servicio; decide el `rate_id` de la tarifa elegida |
| Endpoints de etiqueta, seguimiento y cancelación separados por servicio | `GET /label`, `GET /tracking` y `POST /cancel` funcionan para cada pedido |
| Creación por lote disponible solo para la entrega local | Cotización por lote y creación por lote para cada servicio, síncronas o en cola |

Uniorder no sustituye a los endpoints existentes; siguen disponibles y sin cambios. Es el punto de entrada recomendado para una nueva integración.

## 3. Mapa de endpoints, en el orden en que los usa una integración

| Paso | Finalidad | REST | GraphQL |
|---|---|---|---|
| 1 | Obtener un token de acceso | `POST /api/v1/user/login` | `userLogin` |
| 2 | Cotizar todos los servicios | `POST /api/v1/uniorder/rate` | `uniorderRate` |
| 3 | Crear el pedido con la tarifa elegida | `POST /api/v1/uniorder` | `uniorderCreate` |
| 4 | Imprimir la etiqueta | `GET /api/v1/uniorder/{orderId}/label` | `uniorderLabel` |
| 5 | Consultar el pedido | `GET /api/v1/uniorder/{orderId}` | `uniorder` |
| 6 | Seguir el pedido | `GET /api/v1/uniorder/{orderId}/tracking` | `uniorderTracking` |
| 7 | Cancelar el pedido | `POST /api/v1/uniorder/{orderId}/cancel` | `uniorderCancel` |
| — | Comprar una etiqueta que no se pudo comprar al crear el pedido | `POST /api/v1/uniorder/{orderId}/label` | `uniorderPurchaseLabel` |
| — | Cotizar o crear hasta 20 filas a la vez | `POST /api/v1/uniorder/rate/batch`, `POST /api/v1/uniorder/batch` | `uniorderRateBatch`, `uniorderCreateBatch` |
| — | Poner en cola hasta 500 filas | `POST /api/v1/uniorder/rate/batch-async`, `POST /api/v1/uniorder/batch-async`, `GET /api/v1/uniorder/jobs/{jobId}` | `uniorderRateBatchAsync`, `uniorderCreateBatchAsync`, `uniorderJob` |

[Manual REST](/api/documentation#/paths/v1-uniorder-rate/post) · [Manual GraphQL](/api/graphql/documentation#/orders/uniorderRate)

Las solicitudes, respuestas y verificaciones paso a paso se encuentran en la guía **Cotización y pedido en un solo flujo**.

## 4. Ejemplo: un proceso de pago que ofrece todas las opciones

Una floristería de Montreal vende en línea. En el proceso de pago cotiza el paquete una sola vez, incluyendo los transportistas de etiquetas:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/rate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "quote_labels": true,
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

La respuesta enumera una tarifa `self_delivery` y una tarifa `label_service` por cada servicio de transportista. El proceso de pago las muestra como opciones; el cliente elige la entrega local en el mismo día. El pedido se crea con el `rate_id` de esa tarifa y las mismas direcciones y paquetes:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: checkout-10045" \
  -d '{
    "rate_id": "eyJpdiI6Ik1rT2Z...",
    "ref": "WEB-10045",
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

La respuesta devuelve el `id` del pedido y sus números de seguimiento. La tienda imprime la etiqueta con `GET /api/v1/uniorder/{orderId}/label` y muestra el historial de `GET /api/v1/uniorder/{orderId}/tracking` en la página del pedido del cliente.

## 5. Ejemplo: un ERP que envía cada noche

Un ERP exporta los pedidos del día a las 22:00. Envía las direcciones a `POST /api/v1/uniorder/rate/batch-async`, consulta el trabajo hasta que `status` es `done`, selecciona una tarifa para cada fila según sus propias reglas y envía las filas elegidas a `POST /api/v1/uniorder/batch-async`. Cada resultado lleva la `reference` de la fila, de modo que el ERP asocia cada resultado con su propia línea de pedido. Un `rate_id` es válido durante 30 minutos, por lo que el trabajo de creación se envía poco después de que finalice el trabajo de cotización.

## 6. Ejemplo: una pantalla de atención al cliente

Ante la llamada de un cliente, la pantalla del agente llama a `GET /api/v1/uniorder/{orderId}` para el estado y las direcciones, a `GET /api/v1/uniorder/{orderId}/tracking` para el historial y la prueba de entrega, y a `POST /api/v1/uniorder/{orderId}/cancel` cuando el cliente cancela. Las mismas llamadas se aplican a un pedido de entrega y a un pedido de etiqueta; el campo `type` los distingue.

```bash
curl https://YOUR_HOST/api/v1/uniorder/123456/tracking \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

El mismo pedido mediante GraphQL ([Manual GraphQL](/api/graphql/documentation#/orders/uniorder)):

```graphql
query {
  uniorder(order_id: 123456)
}
```

## 7. Reglas que deben tenerse en cuenta en el diseño

- **El `rate_id` decide el servicio.** Es válido durante 30 minutos y solo para la cuenta que solicitó la cotización.
- **El pedido se tarifica en el momento de su creación.** `shipping_price` es el precio cobrado; `quoted_price` es el precio de la cotización. Ambos pueden diferir.
- **Un pedido de etiqueta nunca se pierde.** Cuando la etiqueta no se puede comprar al crear el pedido, el pedido se conserva y la respuesta es `LABEL_PURCHASE_FAILED` con el `id` del pedido; la etiqueta se compra más tarde con `POST /api/v1/uniorder/{orderId}/label`.
- **Los reintentos son seguros.** Envíe una cabecera `Idempotency-Key` en cada llamada de creación; la cancelación de un pedido ya cancelado devuelve `already_cancelled` `true`.
- **Los estados son uniformes.** Un pedido de entrega informa `pending`, `in_transit`, `out_for_delivery`, `delivered`, `exception` o `cancelled`; un pedido de etiqueta informa `label_pending`, `label_purchased` o `cancelled`, y el seguimiento de su transportista indica dónde se encuentra el paquete.

## 8. Lista de verificación para la puesta en producción

- [ ] La cuenta tiene permiso de API y el token se almacena en el servidor, no en un navegador.
- [ ] Las cotizaciones se solicitan con las direcciones completas del remitente y del destinatario.
- [ ] Los pedidos se crean dentro de los 30 minutos siguientes a la cotización, con un `Idempotency-Key`.
- [ ] `LABEL_PURCHASE_FAILED` se gestiona comprando la etiqueta más tarde, nunca creando el pedido de nuevo.
- [ ] El seguimiento se lee de `GET /api/v1/uniorder/{orderId}/tracking` o se recibe mediante webhooks.
- [ ] La cancelación gestiona `ORDER_STATUS_NOT_CANCELLABLE` y `ORDER_CANCEL_REFUSED` dejando el pedido como está.
