# Tracking

> Get the same tracking info you see in your panel. Show customers where their package is, right in your app.

Source: https://api-doc.4nortes.app/tracking

Get the same tracking information you see in your panel. Fetch it to show customers in your app or to update your order management system.

<a id="tracking-numbers"></a>

## Tracking Numbers

Every order is assigned a unique tracking number when created:

```
4N YYYYMMDD NNNN
```

- `4N` — prefix for regular orders. `SH` is used for orders that start from a blank label scanned by a driver in the field ("shell" orders) and keep that prefix for life.
- `YYYYMMDD` — the creation date.
- `NNNN` — a 4-digit code, randomly assigned. Don't infer volume or sequence from it.

For example, `4N202512310001` is an order created on 2025-12-31. Tracking numbers are always 14 characters: the two-letter prefix plus 12 digits. The tracking endpoint rejects any other shape with a `422`.

Each package inside an order has its own tracking number, built from the order's: `4N202512310001-001`, `4N202512310001-002`, and so on.

<a id="delivery-states"></a>

## Delivery states

The `delivery_state` field is the order's current position in the flow. These are the possible values:

- `pending`
- `picked_up`
- `in_transit`
- `out_for_delivery`
- `partially_delivered`
- `delivered`
- `closed_with_return`
- `failed`
- `nulled`

| State                 | Meaning                                                     |
| --------------------- | ----------------------------------------------------------- |
| `pending`             | Created, waiting for pickup                                 |
| `picked_up`           | Collected from the sender                                   |
| `in_transit`          | Moving through the network                                  |
| `out_for_delivery`    | On a vehicle heading to the recipient                       |
| `partially_delivered` | Some packages were delivered, others not yet                |
| `delivered`           | All packages delivered — final                              |
| `closed_with_return`  | Closed; undelivered packages returned to the sender — final |
| `failed`              | The last delivery attempt failed; a new attempt may follow  |
| `nulled`              | Cancelled — final                                           |

See [Orders](https://api-doc.4nortes.app/orders.md) for how an order moves between these states.

<a id="timeline-events"></a>

## Timeline events

You get the same timeline you see when clicking an order in your panel — one entry per state change, oldest first. The first entry is always `pending` at the order's creation time.

| Field          | Type     | Description                                                    |
| -------------- | -------- | -------------------------------------------------------------- |
| `state`        | string   | The delivery state reached at this point                       |
| `timestamp`    | datetime | When it happened                                               |
| `type`         | string   | Only on failure entries: always `failure`                      |
| `failure_info` | object   | Only on failure entries: `type`, `reason`, `notes` (see below) |

A failed delivery attempt produces **two** entries at (almost) the same time: a `failed` entry carrying `type: "failure"` and `failure_info` with the details, and the resulting `failed` state change. Use the one with `failure_info` to tell your customer what happened.

### Failure info

| Field    | Type         | Description                                                                                           |
| -------- | ------------ | ----------------------------------------------------------------------------------------------------- |
| `type`   | string\|null | A human-readable label for what failed (delivery or pickup), in the platform's language. Display only |
| `reason` | string       | The failure reason. Normally one of the codes below; branch on this field                             |
| `notes`  | string\|null | Free-text notes from the driver                                                                       |

Reason codes: `not_home`, `refused`, `wrong_address`, `inaccessible`, `business_closed`, `pending_stock_break`, `no_cargo_ready`, `no_cargo_to_ship`, `customer_unavailable`, `other`.

### Example Timeline

```json
{
  "timeline": [
    { "state": "pending", "timestamp": "2025-02-03T10:00:00.000000Z" },
    { "state": "picked_up", "timestamp": "2025-02-03T14:30:00.000000Z" },
    { "state": "in_transit", "timestamp": "2025-02-03T18:00:00.000000Z" },
    { "state": "out_for_delivery", "timestamp": "2025-02-04T08:00:00.000000Z" },
    {
      "state": "failed",
      "timestamp": "2025-02-04T11:05:00.000000Z",
      "type": "failure",
      "failure_info": {
        "type": "Entrega fallida",
        "reason": "not_home",
        "notes": "Nobody answered the door"
      }
    },
    { "state": "failed", "timestamp": "2025-02-04T11:05:01.000000Z" },
    { "state": "out_for_delivery", "timestamp": "2025-02-05T08:30:00.000000Z" },
    { "state": "delivered", "timestamp": "2025-02-05T11:30:00.000000Z" }
  ]
}
```

<a id="package-progress"></a>

## Package progress

Orders with several packages can be delivered in parts. The tracking response gives you both the counters and the per-package detail:

| Counter                         | Meaning                               |
| ------------------------------- | ------------------------------------- |
| `packages_count`                | Total packages in the order           |
| `picked_up_packages_count`      | Packages collected from the sender    |
| `pending_pickup_packages_count` | Packages not yet collected            |
| `delivered_packages_count`      | Packages handed to the recipient      |
| `failed_packages_count`         | Packages whose last attempt failed    |
| `returned_packages_count`       | Packages returned to the sender       |
| `pending_packages_count`        | Packages with no delivery outcome yet |

Each entry in `packages[]` has:

| Field             | Type           | Description                                    |
| ----------------- | -------------- | ---------------------------------------------- |
| `tracking_number` | string         | Package tracking number (`{order}-NNN`)        |
| `delivery_state`  | string         | `pending`, `delivered`, `failed` or `returned` |
| `picked_up_at`    | datetime\|null | When it was collected                          |
| `delivered_at`    | datetime\|null | When it was delivered                          |
| `returned_at`     | datetime\|null | When it was returned to the sender             |

<a id="delivery-attempts"></a>

## Delivery attempts

`delivery_attempts[]` lists every visit to the recipient, in order, with its outcome and evidence — the same cards you see in the panel. Each attempt has:

| Field            | Type         | Description                                                                                      |
| ---------------- | ------------ | ------------------------------------------------------------------------------------------------ |
| `attempt`        | integer      | 1 for the first visit, 2 for the second, …                                                       |
| `outcome`        | string       | `success` or `failed`                                                                            |
| `recipient_name` | string\|null | Who received the package (successful attempts)                                                   |
| `recipient_rut`  | string\|null | Their Chilean ID, if captured                                                                    |
| `recipient_role` | string\|null | `merchant_staff`, `recipient`, `operator` or `other`                                             |
| `location`       | object\|null | `latitude` / `longitude` where the attempt was recorded                                          |
| `notes`          | string\|null | Driver notes                                                                                     |
| `photos`         | array        | `url` and `preview_url` pairs, signed links valid for **2 hours**                                |
| `packages`       | array        | Per-package outcome: `tracking_number`, `delivered` (boolean), `failure_reason` (code or `null`) |
| `created_at`     | datetime     | When the attempt was recorded                                                                    |

<a id="delivery-proof"></a>

## Proofs

Three evidence blocks share the same core shape — `location`, `notes`, `photos[]` (`url` + `preview_url`) and `created_at` — and differ in when they're present:

| Block            | Present when                                                                             | Extra fields                                        |
| ---------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------- |
| `delivery_proof` | `delivery_state` is `delivered` or `partially_delivered` and the driver recorded a proof | `recipient_name`, `recipient_rut`, `recipient_role` |
| `pickup_proof`   | A photographed pickup proof exists (from pickup onwards). `null` before that             | —                                                   |
| `failure_proof`  | `delivery_state` is `failed` and the failed attempt has photos. `null` otherwise         | — (it documents the scene, not a signee)            |

`recipient_role` is one of `merchant_staff`, `recipient`, `operator` or `other`.

### Location Object

```json
{
  "location": {
    "latitude": -33.4489,
    "longitude": -70.6693
  }
}
```

### Photos Array

```json
{
  "photos": [
    {
      "url": "https://storage.example.com/proofs/abc123.jpg?signature=...",
      "preview_url": "https://storage.example.com/proofs/abc123-preview.jpg?signature=..."
    }
  ]
}
```

Photo links in the proof blocks are signed and valid for **60 minutes**; the ones inside `delivery_attempts[]` last 2 hours. Fetch the tracking again to get fresh links — don't store them.

> **Note:**
>
> Delivery photos are kept for 90 days. Download any you need for your records before then.

<a id="order-events"></a>

## Full event history

The tracking timeline shows delivery states only. For the complete step-by-step history of an order — every change, who made it and when — use [`GET /orders/{reference}/events`](https://api-doc.4nortes.app/api/orders.md#order-events). It accepts either the tracking number or your own `external_reference`, so you can look an order up without storing our number first.

<a id="updates"></a>

## Get updates automatically

Instead of checking your panel or calling the API repeatedly, set up [webhooks](https://api-doc.4nortes.app/webhooks.md) to get notified when orders move.

### When to use what

| Approach         | Best for                                                   |
| ---------------- | ---------------------------------------------------------- |
| **Webhooks**     | Updating your system automatically when status changes     |
| **Tracking API** | Showing customers their order status on demand             |
| **Order events** | Audits, support tooling, reconciling with your own records |

See the [Webhooks documentation](https://api-doc.4nortes.app/webhooks.md) for setup.
