# Tracking API

> Fetch tracking info for your customers. Same data you see in your panel, in your app.

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

Show your customers where their package is. Fetch the same tracking timeline, per-package progress and proofs you see in your panel.

---

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

## Track by tracking number

`GET /tracking/{tracking_number}`

Get the full tracking record for an order using its 4N NextDay tracking number. Merchant tokens can only see their own orders; anything else is reported as not found.

### Path Parameters

- `tracking_number` (string): The order tracking number: `4N` or `SH` followed by 12 digits (e.g. `4N202502030001`). **Required.**

### Response Fields

- `tracking_number` (string): The order tracking number.
- `delivery_state` (string): Current state: `pending`, `picked_up`, `in_transit`, `out_for_delivery`, `partially_delivered`, `delivered`, `closed_with_return`, `failed` or `nulled`.
- `estimated_delivery_date` (string|null): Expected delivery date (ISO 8601).
- `delivery_date` (string|null): Actual delivery date, once delivered.
- `packages_count` (integer): Total number of packages in the order.
- `picked_up_packages_count` (integer): Packages collected from the sender.
- `pending_pickup_packages_count` (integer): Packages not yet collected.
- `delivered_packages_count` (integer): Packages handed to the recipient.
- `failed_packages_count` (integer): Packages whose last delivery attempt failed.
- `returned_packages_count` (integer): Packages returned to the sender.
- `pending_packages_count` (integer): Packages with no delivery outcome yet.
- `timeline` (array): State changes in chronological order, starting with `pending` at creation. Each entry has `state` and `timestamp`; failure entries add `type: "failure"` and `failure_info`.
- `delivery_proof` (object|null): Proof of delivery. Present when `delivery_state` is `delivered` or `partially_delivered` and a proof was recorded; `null` otherwise.
- `pickup_proof` (object|null): Proof captured when the order was collected from the sender. Same shape as `failure_proof` (no recipient fields). `null` until a photographed pickup proof exists.
- `failure_proof` (object|null): Proof captured at the failed delivery attempt — `location`, `notes`, `photos`, `created_at`. **No recipient fields** (it documents the scene, not a signee). Present only when `delivery_state` is `failed` and photos were captured; `null` otherwise.
- `delivery_attempts` (array): One record per visit to the recipient, oldest first. See below.
- `packages` (array): Per-package progress. See below.

### Timeline entry

- `state` (string): The delivery state reached at this point.
- `timestamp` (string): When it happened (ISO 8601).
- `type` (string): Only on failure entries. Always `failure`.
- `failure_info` (object): Only on failure entries. `type` (display label, platform language), `reason` (normally one of `not_home`, `refused`, `wrong_address`, `inaccessible`, `business_closed`, `pending_stock_break`, `no_cargo_ready`, `no_cargo_to_ship`, `customer_unavailable`, `other`) and `notes` (driver notes or `null`).

### Proof blocks

`delivery_proof`, `pickup_proof` and `failure_proof` share these fields. Photo links are signed and valid for **60 minutes**.

- `recipient_name` (string|null): `delivery_proof` only. Who received the package.
- `recipient_rut` (string|null): `delivery_proof` only. Their Chilean ID, if captured.
- `recipient_role` (string|null): `delivery_proof` only. `merchant_staff`, `recipient`, `operator` or `other`.
- `location` (object|null): `latitude` and `longitude` where the proof was recorded.
- `notes` (string|null): Driver notes.
- `photos` (array): Objects with `url` (full size) and `preview_url` (thumbnail).
- `created_at` (string): When the proof was recorded.

### Delivery attempt

Photo links inside attempts are signed and valid for **2 hours**.

- `attempt` (integer): 1-based attempt number.
- `outcome` (string): `success` or `failed`.
- `recipient_name` (string|null): Who received the package, on 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` and `longitude`.
- `notes` (string|null): Driver notes.
- `photos` (array): Objects with `url` and `preview_url`.
- `packages` (array): Per-package outcome: `tracking_number`, `delivered` (boolean) and `failure_reason` (a reason code, or `null` when delivered).
- `created_at` (string): When the attempt was recorded.

### Package

- `tracking_number` (string): Package tracking number: the order's number plus a 3-digit suffix (`4N202502030001-001`).
- `delivery_state` (string): `pending`, `delivered`, `failed` or `returned`.
- `picked_up_at` (string|null): When it was collected from the sender.
- `delivered_at` (string|null): When it was delivered.
- `returned_at` (string|null): When it was returned to the sender.

```bash
curl https://api.4nortes.app/api/v1/tracking/4N202502030001 \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"
```

```js
const response = await fetch(
  'https://api.4nortes.app/api/v1/tracking/4N202502030001',
  {
    headers: {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
    },
  }
)
const data = await response.json()
```

```json
{
  "success": true,
  "message": "Operation successful",
  "data": {
    "tracking_number": "4N202502030001",
    "delivery_state": "delivered",
    "estimated_delivery_date": "2025-02-04T00:00:00.000000Z",
    "delivery_date": "2025-02-05T11:30:00.000000Z",
    "packages_count": 2,
    "picked_up_packages_count": 2,
    "pending_pickup_packages_count": 0,
    "delivered_packages_count": 2,
    "failed_packages_count": 0,
    "returned_packages_count": 0,
    "pending_packages_count": 0,
    "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" }
    ],
    "delivery_proof": {
      "recipient_name": "Jane Doe",
      "recipient_rut": "12345678-9",
      "recipient_role": "recipient",
      "location": { "latitude": -33.4289, "longitude": -70.6093 },
      "notes": null,
      "photos": [
        {
          "url": "https://storage.example.com/proofs/d41d8c.jpg?signature=...",
          "preview_url": "https://storage.example.com/proofs/d41d8c-preview.jpg?signature=..."
        }
      ],
      "created_at": "2025-02-05T11:30:00.000000Z"
    },
    "pickup_proof": {
      "location": { "latitude": -33.4372, "longitude": -70.6506 },
      "notes": null,
      "photos": [
        {
          "url": "https://storage.example.com/proofs/7f3a9b.jpg?signature=...",
          "preview_url": "https://storage.example.com/proofs/7f3a9b-preview.jpg?signature=..."
        }
      ],
      "created_at": "2025-02-03T14:30:00.000000Z"
    },
    "failure_proof": null,
    "delivery_attempts": [
      {
        "attempt": 1,
        "outcome": "failed",
        "recipient_name": null,
        "recipient_rut": null,
        "recipient_role": null,
        "location": { "latitude": -33.4291, "longitude": -70.6090 },
        "notes": "Nobody answered the door",
        "photos": [
          {
            "url": "https://storage.example.com/proofs/0c1e2f.jpg?signature=...",
            "preview_url": "https://storage.example.com/proofs/0c1e2f-preview.jpg?signature=..."
          }
        ],
        "packages": [
          { "tracking_number": "4N202502030001-001", "delivered": false, "failure_reason": "not_home" },
          { "tracking_number": "4N202502030001-002", "delivered": false, "failure_reason": "not_home" }
        ],
        "created_at": "2025-02-04T11:05:00.000000Z"
      },
      {
        "attempt": 2,
        "outcome": "success",
        "recipient_name": "Jane Doe",
        "recipient_rut": "12345678-9",
        "recipient_role": "recipient",
        "location": { "latitude": -33.4289, "longitude": -70.6093 },
        "notes": null,
        "photos": [
          {
            "url": "https://storage.example.com/proofs/d41d8c.jpg?signature=...",
            "preview_url": "https://storage.example.com/proofs/d41d8c-preview.jpg?signature=..."
          }
        ],
        "packages": [
          { "tracking_number": "4N202502030001-001", "delivered": true, "failure_reason": null },
          { "tracking_number": "4N202502030001-002", "delivered": true, "failure_reason": null }
        ],
        "created_at": "2025-02-05T11:30:00.000000Z"
      }
    ],
    "packages": [
      {
        "tracking_number": "4N202502030001-001",
        "delivery_state": "delivered",
        "picked_up_at": "2025-02-03T14:30:00.000000Z",
        "delivered_at": "2025-02-05T11:30:00.000000Z",
        "returned_at": null
      },
      {
        "tracking_number": "4N202502030001-002",
        "delivery_state": "delivered",
        "picked_up_at": "2025-02-03T14:30:00.000000Z",
        "delivered_at": "2025-02-05T11:30:00.000000Z",
        "returned_at": null
      }
    ]
  },
  "timestamp": "2025-02-05T12:00:00.000000Z"
}
```

---

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

## Errors

- `404 Not Found`: No order with that tracking number — or it belongs to another account. Standard error envelope.
- `422 Unprocessable Entity`: The path value doesn't match `4N`/`SH` + 12 digits. Raw validation body (no envelope).

See [Error Handling](https://api-doc.4nortes.app/errors.md) for the two response shapes.

```json
{
  "success": false,
  "message": "Tracking number not found.",
  "errors": null,
  "timestamp": "2025-02-05T12:00:00.000000Z"
}
```

```json
{
  "message": "The tracking number format is invalid.",
  "errors": {
    "tracking_number": ["The tracking number format is invalid."]
  }
}
```
