# El objeto entrega

Todas las llamadas de entregas (crear, consultar, listar, corregir, cancelar, simular) responden con este mismo objeto. Siempre trae **todos** los campos: los que no aplican vienen en `null`.

## Ciclo de vida

<figure class="diagram" role="img" aria-label="Ciclo de vida: CREATED pasa a ASSIGNED, luego a OUT_FOR_DELIVERY y termina en DELIVERED o FAILED. FAILED puede reintentarse y vuelve a CREATED, o a ASSIGNED si el reintento ya tiene repartidor. CREATED y ASSIGNED pueden cancelarse.">
<div class="diagram-scroll">
<svg viewBox="0 0 780 330" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
<defs><marker id="flecha" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path class="arrowhead" d="M0 0 10 5 0 10z"/></marker></defs>
<path class="edge" d="M150 64H206" marker-end="url(#flecha)"/>
<path class="edge" d="M346 64H402" marker-end="url(#flecha)"/>
<path class="edge" d="M582 64H630" marker-end="url(#flecha)"/>
<path class="edge" d="M492 88V196" marker-end="url(#flecha)"/>
<path class="edge muted" d="M110 88 C110 150 230 160 260 196" marker-end="url(#flecha)"/>
<path class="edge muted" d="M276 88V196" marker-end="url(#flecha)"/>
<path class="edge retry" d="M492 244V296H60V92" marker-end="url(#flecha)"/>
<text class="event" x="178" y="28" text-anchor="middle">assigned</text>
<text class="event" x="374" y="28" text-anchor="middle">out_for_delivery</text>
<text class="event" x="606" y="28" text-anchor="middle">delivered</text>
<text class="event" x="502" y="146">failed</text>
<text class="event" x="286" y="146">cancelled</text>
<text class="event" x="276" y="318" text-anchor="middle">retry_scheduled · intento + 1</text>
<g class="node"><rect x="20" y="40" width="130" height="48" rx="12"/><text x="85" y="69" text-anchor="middle">CREATED</text></g>
<g class="node"><rect x="206" y="40" width="140" height="48" rx="12"/><text x="276" y="69" text-anchor="middle">ASSIGNED</text></g>
<g class="node"><rect x="402" y="40" width="180" height="48" rx="12"/><text x="492" y="69" text-anchor="middle">OUT_FOR_DELIVERY</text></g>
<g class="node done"><rect x="630" y="40" width="130" height="48" rx="12"/><text x="695" y="69" text-anchor="middle">DELIVERED</text></g>
<g class="node fail"><rect x="412" y="196" width="160" height="48" rx="12"/><text x="492" y="225" text-anchor="middle">FAILED</text></g>
<g class="node end"><rect x="206" y="196" width="140" height="48" rx="12"/><text x="276" y="225" text-anchor="middle">CANCELLED</text></g>
</svg>
</div>
<figcaption>Cada flecha emite su evento (<code>delivery.…</code>) y también <code>delivery.status_changed</code>. Retirar el paquete (<code>delivery.picked_up</code>), guardar el comprobante (<code>delivery.proof_added</code>) y rendir el cobro (<code>delivery.collection_settled</code>) no cambian el estado.</figcaption>
</figure>

| Estado | Significa | Qué podés hacer |
| --- | --- | --- |
| `CREATED` | Cargada, sin repartidor. | Corregir o cancelar. |
| `ASSIGNED` | Asignada a un repartidor. | Corregir o cancelar. |
| `OUT_FOR_DELIVERY` | El repartidor salió a entregar. Si la inició por error, puede volver a `ASSIGNED` (mismo repartidor e intento). | Consultar y seguir. |
| `DELIVERED` | Entregada. Si había cobro, `collectedAmount`, `collectionMethod`, `collectedAt` y `collectedCurrency` dicen lo declarado. | Pedir el [comprobante](comprobantes-y-etiquetas.html). |
| `FAILED` | No se pudo entregar (`failureReasonCode`). | Esperar el reintento: vuelve a `CREATED` (o a `ASSIGNED`, si el reintento ya tiene repartidor) con `attemptNumber` + 1. |
| `CANCELLED` | Cancelada. | Nada: es final. |

> [!TIP]
> Para probar el recorrido completo sin repartidores, usá una clave de prueba y el [simulador](entorno-de-prueba.html#simular-el-recorrido): cada paso dispara los mismos webhooks que en producción.

## Campos

Generada del contrato del servidor: siempre coincide con lo que responde la API.

| Campo | Tipo | Qué es |
| --- | --- | --- |
| `id` | texto o `null` | Id de la entrega (UUID). Usalo en las demás llamadas. |
| `status` | `CREATED` · `ASSIGNED` · `OUT_FOR_DELIVERY` · `DELIVERED` · `FAILED` · `CANCELLED` o `null` | Estado actual. Ver [el ciclo de vida](#ciclo-de-vida). |
| `externalReference` | texto o `null` | El número de tu pedido, si lo mandaste. Único por empresa y comercio. |
| `customerName` | texto o `null` | Nombre del destinatario. |
| `address` | texto o `null` | Dirección de entrega, tal como se cargó (Ruta no la geocodifica). |
| `recipientPhone` | texto o `null` | Teléfono del destinatario en formato E.164 (`+59899123456`). |
| `recipientEmail` | texto o `null` | Email del destinatario para los avisos por email (sin espacios, en minúsculas), o `null`. |
| `instructions` | texto o `null` | Indicaciones para el repartidor. |
| `trackingUrl` | texto o `null` | Página pública de seguimiento para compartir con el destinatario. |
| `scheduledDate` | texto o `null` | Día programado (`YYYY-MM-DD`), si la entrega se programó. |
| `windowStart` | texto o `null` | Inicio de la franja horaria (`HH:mm`). |
| `windowEnd` | texto o `null` | Fin de la franja horaria (`HH:mm`). |
| `scheduleTimeZone` | texto o `null` | Zona horaria de la programación, por ejemplo `America/Montevideo`. |
| `amountToCollect` | texto o `null` | Monto a cobrar contra entrega, como texto decimal (`"1250.00"`), en la moneda de `currency`. |
| `shipperId` | texto o `null` | Comercio al que pertenece la entrega. |
| `zoneId` | texto o `null` | Zona asignada. |
| `destinationLat` | número o `null` | Latitud del punto marcado a mano (grados decimales). |
| `destinationLng` | número o `null` | Longitud del punto marcado a mano (grados decimales). |
| `attemptNumber` | entero | Número de intento: empieza en 1 y sube con cada reintento. |
| `failureReasonCode` | texto o `null` | Motivo del último fallo: `RECIPIENT_ABSENT`, `ADDRESS_NOT_FOUND`, `ACCESS_UNAVAILABLE`, `RECIPIENT_REFUSED`, `UNSAFE_LOCATION`, `PACKAGE_ISSUE` u `OTHER`. |
| `createdAt` | fecha y hora ISO 8601 o `null` | Cuándo se creó. |
| `startedAt` | fecha y hora ISO 8601 o `null` | Cuándo salió a entregar. |
| `deliveredAt` | fecha y hora ISO 8601 o `null` | Cuándo se entregó. |
| `failedAt` | fecha y hora ISO 8601 o `null` | Cuándo falló el último intento. |
| `cancelledAt` | fecha y hora ISO 8601 o `null` | Cuándo se canceló. |
| `collectedAmount` | texto o `null` | Monto que el repartidor declaró cobrado al entregar, en la moneda de `collectedCurrency`. |
| `collectionMethod` | `CASH` · `TRANSFER` · `OTHER` o `null` | Cómo se cobró. |
| `collectedAt` | fecha y hora ISO 8601 o `null` | Cuándo se declaró el cobro. |
| `settledAt` | fecha y hora ISO 8601 o `null` | Cuándo el courier le rindió el cobro al comercio. |
| `test` | booleano | `true` si la entrega es del [entorno de prueba](entorno-de-prueba.html). |
| `quotaHeld` | booleano | `true` mientras la entrega está retenida porque llegó con el cupo mensual del courier completo: sigue en `CREATED`, sin repartidor, y sale sola cuando hay cupo. |
| `quotaHeldAt` | fecha y hora ISO 8601 o `null` | Desde cuándo está retenida, o `null`. |
| `currency` | `UYU` · `USD` o `null` | Moneda del cobro: `UYU` (pesos) o `USD` (dólares); `null` sin monto a cobrar. Los montos de la entrega están en esa moneda y Ruta nunca convierte. |
| `collectedCurrency` | `UYU` · `USD` o `null` | Moneda de `collectedAmount`: `UYU` o `USD`; `null` hasta que se cobre. Es distinta de `currency` cuando el repartidor cobró en la otra moneda (por ejemplo, una entrega en dólares pagada en pesos); Ruta no convierte. |

## Ejemplo

```json
{
  "id": "6f1c1d0e-2f5e-4bb5-9c1a-1c1b1f0e9a77",
  "status": "DELIVERED",
  "externalReference": "1042",
  "customerName": "Ana Pérez",
  "address": "Av. Italia 1234, apto 5",
  "recipientPhone": "+59899123456",
  "recipientEmail": "ana@ejemplo.com",
  "instructions": "Timbre 5",
  "trackingUrl": "https://<tu-courier>/track/0b6d…",
  "scheduledDate": null, "windowStart": null, "windowEnd": null, "scheduleTimeZone": null,
  "amountToCollect": "1250.00",
  "shipperId": null, "zoneId": null,
  "destinationLat": null, "destinationLng": null,
  "attemptNumber": 1, "failureReasonCode": null,
  "createdAt": "2026-10-10T15:04:05.123456Z",
  "startedAt": "2026-10-10T16:10:00Z",
  "deliveredAt": "2026-10-10T16:42:13Z",
  "failedAt": null, "cancelledAt": null,
  "collectedAmount": "1250.00", "collectionMethod": "CASH",
  "collectedAt": "2026-10-10T16:42:13Z", "settledAt": null,
  "test": false,
  "quotaHeld": false, "quotaHeldAt": null,
  "currency": "UYU",
  "collectedCurrency": "UYU"
}
```

Si el destinatario pagó en la otra moneda (por ejemplo, una entrega de `"currency": "USD"` pagada en pesos), `collectedCurrency` es `"UYU"` y `collectedAmount` está en pesos. Ruta nunca convierte: comparalo con `amountToCollect` sólo si las dos monedas coinciden.

La API nunca muestra la identidad ni la ubicación del repartidor, ni notas internas del courier.
