# API Reference


Source: https://docs.fleetwork.vn/sdk/tracking-api

> Tham chiếu đầy đủ tất cả endpoints của hệ thống GPS Tracking dành cho đối tác tích hợp.

---

## Base URLs

| Group            | Base URL                                                 |
| ---------------- | -------------------------------------------------------- |
| **GPS Tracking** | `https://live.fleetwork.vn/api/v1/gps-tracking`          |
| **Dashboard**    | `https://live.fleetwork.vn/api/v1/dashboard/gps-manager` |
| **Báo cáo**      | `https://live.fleetwork.vn/api/v1/reports`               |

---

## Authentication

Tất cả endpoints xác thực qua **request header**:

```http
X-API-Key: <your_api_key>
```

Sai hoặc thiếu key → `401 Unauthorized`:

```json
{
  "error": "UNAUTHORIZED",
  "message": "Missing or invalid X-API-Key",
  "code": 401
}
```

### Luồng xác thực Dashboard API

Các endpoint thuộc nhóm **Dashboard** đi qua Fleetwork.Web gateway:

```
Client
  → X-API-Key → Fleetwork.Web (live.fleetwork.vn)
      → tự inject X-Account-Id + X-Internal-Token
      → YARP proxy → AnalyticsApi (internal)
```

> Client **chỉ cần gửi `X-API-Key`**. Không cần và không nên gửi `X-Account-Id` hay `X-Internal-Token` từ phía client.

## Quy ước thời gian

Tất cả query params và field liên quan thời gian trong tài liệu này dùng **Unix timestamp (milliseconds)**, bao gồm:

- `timestamp`
- `date`
- `from`
- `to`
- `startTime`
- `endTime`
- `generatedAt`
- `lastSeenAt`

Quy ước áp dụng:

- với dữ liệu theo ngày, `date` là timestamp mốc đầu ngày theo timezone hệ thống
- với khoảng thời gian lọc, `from` và `to` là timestamp Unix ms
- các field hiển thị như `formatted` vẫn là chuỗi để phục vụ UI

---

## Mục lục

**PHẦN 1 — GPS TRACKING**

- [1.1 Gửi dữ liệu vị trí](#11-post-gps-tracking)
- [1.2 Gửi hàng loạt (bulk)](#12-post-gps-trackingbulk)
- [1.3 Danh sách users có GPS](#13-get-gps-trackingusers)
- [1.4 Vị trí mới nhất theo User](#14-get-latestusers)
- [1.5 Vị trí mới nhất theo Vehicle](#15-get-latestvehicles)
- [1.6 Vị trí mới nhất theo Device](#16-get-latestdevices)
- [1.7 Lịch sử di chuyển](#17-get-gps-trackinghistory)
- [1.8 Lộ trình theo mã kiện hàng](#18-get-gps-trackingpackagesroute)

**PHẦN 2 — DASHBOARD**

- [2.1 KPI Overview](#21-get-summary--kpi-overview)
- [2.2 Bảng chi tiết users](#22-get-users)
- [2.3 Heatmap giờ hoạt động](#23-get-activity-heatmap)
- [2.4 Biểu đồ nhiên liệu](#24-get-fuel-tracking)
- [2.5 Chi phí theo tháng](#25-get-monthly-costs)
- [2.6 All-in-One (Composite)](#26-get---all-in-one)

**PHẦN 3 — BÁO CÁO**

- [3.1 Hành trình — Tổng hợp](#31-get-reportstripsummary)
- [3.2 Hành trình — Chi tiết](#32-get-reportstripdetail)
- [3.3 Nhiên liệu — Tổng hợp](#33-get-reportsfuelsummary)
- [3.4 Nhiên liệu — Chi tiết](#34-get-reportsfueldetail)
- [3.5 Thời gian hoạt động](#35-get-reportsactivity-time)

---

# PHẦN 1 — GPS TRACKING

## 1.1 `POST /gps-tracking`

Ghi tọa độ GPS. **Body luôn là một mảng** điểm GPS — gửi 1 điểm thì mảng có 1 phần tử.

```http
POST https://live.fleetwork.vn/api/v1/gps-tracking
Content-Type: application/json
X-API-Key: <key>
```

### Alias — 3 đường dẫn, cùng một hành vi

| Đường dẫn                                    | Ghi chú                                      |
| -------------------------------------------- | -------------------------------------------- |
| `POST /api/v1/gps-tracking`                  | Đường dẫn khuyến nghị.                       |
| `POST /api/v1/gps-tracking/bulk`             | Alias, xem [1.2](#12-post-gps-trackingbulk). |
| `POST /api/v1/accounts/{accountId}/waypoints` | Alias cũ, giữ cho client đã tích hợp.        |

> Ba route trỏ về **cùng một handler**: cùng payload, cùng validation, cùng response. Chọn route nào cũng được; account vẫn suy ra từ `X-API-Key` chứ không lấy từ `{accountId}` trên URL.

### Request Body

| Field       | Required | Type    | Mô tả                                         |
| ----------- | -------- | ------- | --------------------------------------------- |
| `lat`       | **Yes**  | Double  | Vĩ độ (`-90` đến `90`).                       |
| `lng`       | **Yes**  | Double  | Kinh độ (`-180` đến `180`).                   |
| `time`      | No       | Long    | Unix timestamp (ms) lúc ghi nhận vị trí.      |
| `speed`     | No       | Float   | Vận tốc (km/h).                               |
| `heading`   | No       | Integer | Hướng di chuyển (0–360°).                     |
| `metaData`  | No       | Object  | Thông tin bổ sung của thiết bị/app.           |
| `packages`  | No       | String[] | Danh sách mã kiện hàng mà điểm GPS này đang chở. Xem bên dưới. |
| `data`      | No       | String  | Base64. Đặt `null` cho tracking thông thường. |
| `vehicleId` | No\*     | String  | ID xe.                                        |
| `userId`    | No\*     | String  | ID nhân viên.                                 |
| `deviceId`  | No\*     | String  | ID thiết bị.                                  |

> **Bắt buộc:** phải có ít nhất **1** trong 3: `vehicleId`, `userId`, `deviceId`.

### `packages` — gắn mã kiện hàng vào điểm GPS

Field **tuỳ chọn** duy nhất được thêm cho luồng giao hàng. Không gửi thì endpoint chạy y hệt như trước, không phát sinh gì thêm.

- **Là mảng** vì một toạ độ có thể thuộc **nhiều đơn cùng lúc** (xe chở nhiều đơn trên một chuyến).
- Mỗi cặp (điểm × mã) thành **một bản ghi lộ trình** — đây là dữ liệu mà [1.8](#18-get-gps-trackingpackagesroute) đọc ra.
- Mã được **trim** khoảng trắng, **bỏ chuỗi rỗng**, và **khử trùng** trong cùng một điểm trước khi lưu.
- Mã do phía client tự khai (ví dụ `"#231"`), hệ thống không đối chiếu với danh mục đơn hàng nào.

Ví dụ ngay dưới đây tạo ra **3 bản ghi**: `#231` có 2 điểm, `#3333` có 1 điểm.

**Quy tắc tính `statusCode` tự động:**

```
speed > 0               → statusCode = 1  (Moving)
speed = 0               → statusCode = 2  (Stopping)
không có GPS > 15 phút  → statusCode = 0  (LostGps)
```

### Ví dụ Request

```json
[
  {
    "lat": 21.0285,
    "lng": 105.8542,
    "time": 1713235100000,
    "speed": 45.2,
    "heading": 90,
    "metaData": { "appVersion": "1.2.0", "battery": 82, "speedSource": "gps" },
    "packages": ["#231", "#3333"],
    "data": null,
    "userId": "user-001",
    "vehicleId": "vehicle-abc"
  },
  {
    "lat": 21.029,
    "lng": 105.855,
    "time": 1713235160000,
    "speed": 38.0,
    "heading": 92,
    "packages": ["#231"],
    "userId": "user-001",
    "vehicleId": "vehicle-abc"
  }
]
```

> Điểm 1 chở 2 đơn, điểm 2 chỉ còn `#231` (đơn `#3333` đã giao) ⇒ `#231` có 2 điểm lộ trình, `#3333` có 1.

### Response `200 OK`

```json
{}
```

> Response body trả về rỗng. Không có `id`, `address`, `createdAt` trong response push GPS.

---

## 1.2 `POST /gps-tracking/bulk`

Gửi nhiều tọa độ cùng lúc — dùng khi thiết bị vừa mất mạng và cần đẩy lại dữ liệu.

```http
POST https://live.fleetwork.vn/api/v1/gps-tracking/bulk
Content-Type: application/json
X-API-Key: <key>
```

**Request Body:** Array các object, mỗi object cùng cấu trúc WaypointMSG với 1.1 — bao gồm cả field `packages`.

```json
[
  {
    "lat": 21.0285,
    "lng": 105.8542,
    "time": 1713235000000,
    "speed": 40.0,
    "userId": "user-001"
  },
  {
    "lat": 21.029,
    "lng": 105.855,
    "time": 1713235060000,
    "speed": 45.2,
    "userId": "user-001"
  },
  {
    "lat": 21.0295,
    "lng": 105.856,
    "time": 1713235120000,
    "speed": 50.0,
    "userId": "user-001"
  }
]
```

---

## 1.3 `GET /gps-tracking/users`

Lấy danh sách tất cả user có GPS kèm `lastLocation` của từng user.

```http
GET https://live.fleetwork.vn/api/v1/gps-tracking/users
    ?PageNumber=1&PageSize=20
X-API-Key: <key>
```

### Query Parameters

| Parameter        | Type     | Required | Default | Mô tả                                                                              |
| ---------------- | -------- | -------- | ------- | --------------------------------------------------------------------------------- |
| `userIds`        | String[] | No       | —       | Lọc theo danh sách user id. Chỉ trả về các user trong danh sách. Bỏ trống = tất cả. |
| `Status`         | Int      | No       | —       | `1` (moving) / `2` (stopping) / `0` (signal_lost).                                 |
| `UpdatedFrom`    | Int64    | No       | —       | Lọc user cập nhật từ mốc này (Unix ms).                                            |
| `UpdatedTo`      | Int64    | No       | —       | Lọc user cập nhật đến mốc này (Unix ms).                                           |
| `PageNumber`     | Int32    | No       | 1       | Trang hiện tại.                                                                    |
| `PageSize`       | Int32    | No       | 20      | Số bản ghi / trang.                                                                |
| `SortDescending` | Boolean  | No       | true    | `true` = mới nhất lên đầu.                                                         |

> **`userIds` — định dạng nhiều giá trị:** lặp lại key cho mỗi id, **không** dùng dấu ngoặc `[]` và **không** ghép bằng dấu phẩy:
>
> ```http
> GET /gps-tracking/users?userIds=user-001&userIds=user-002
> ```
>
> Các dạng `userIds[]=a&userIds[]=b` hoặc `userIds=a,b` sẽ bị bỏ qua (API trả về toàn bộ user).

### Response `200 OK`

```json
{
  "users": [
    {
      "userId": "user-001",
      "deviceId": "device-xyz",
      "vehicleId": "vehicle-abc",
      "statusCode": 1,
      "status": "moving",
      "lastLocation": {
        "lat": 21.0285,
        "lng": 105.8542,
        "time": 1713235100000,
        "speed": 45.2,
        "heading": 90,
        "userId": "user-001",
        "vehicleId": "vehicle-abc",
        "deviceId": "device-xyz",
        "data": null,
        "metadata": {}
      }
    }
  ],
  "totalCount": 10,
  "page": 1,
  "pageSize": 20,
  "totalPages": 1
}
```

| Field                   | Type    | Mô tả                                                  |
| ----------------------- | ------- | ------------------------------------------------------ |
| `statusCode`            | Integer | `0` = LostGps, `1` = Moving, `2` = Stopping.           |
| `status`                | String  | Nhãn trạng thái (`moving`, `stopping`, `signal_lost`). |
| `lastLocation.time`     | Long    | Unix ms lúc ghi nhận vị trí cuối.                      |
| `lastLocation.metadata` | Object  | Thông tin bổ sung từ thiết bị/app.                     |

---

## 1.4 `GET /latest/users/{userId}`

Lấy vị trí mới nhất của một nhân viên theo ID.

```http
GET https://live.fleetwork.vn/api/v1/gps-tracking/latest/users/{userId}
X-API-Key: <key>
```

**Response:** `200 OK` — WaypointMSG của user (xem bảng field bên dưới).

---

## 1.5 `GET /latest/vehicles/{vehicleId}`

Lấy vị trí mới nhất của một xe theo ID.

```http
GET https://live.fleetwork.vn/api/v1/gps-tracking/latest/vehicles/{vehicleId}
X-API-Key: <key>
```

**Response:** `200 OK` hoặc `404 Not Found` tuỳ dữ liệu.

---

## 1.6 `GET /latest/devices/{deviceId}`

Lấy vị trí mới nhất của một thiết bị theo ID.

```http
GET https://live.fleetwork.vn/api/v1/gps-tracking/latest/devices/{deviceId}
X-API-Key: <key>
```

**Response:** Luôn `200 OK` kể cả device không tồn tại (DeterministicGuid cache key).

---

### Response WaypointMSG (1.4 / 1.5 / 1.6)

| Field       | Type    | Mô tả                            |
| ----------- | ------- | -------------------------------- |
| `lat`       | Double  | Vĩ độ.                           |
| `lng`       | Double  | Kinh độ.                         |
| `time`      | Long    | Thời điểm ghi nhận (Unix ms).    |
| `speed`     | Float   | Vận tốc (km/h).                  |
| `heading`   | Integer | Hướng di chuyển (0–360).         |
| `userId`    | String  | ID nhân viên (nếu có).           |
| `vehicleId` | String  | ID xe (nếu có).                  |
| `deviceId`  | String  | ID thiết bị (nếu có).            |
| `data`      | String  | Base64 dữ liệu thêm (hoặc null). |
| `metadata`  | Object  | Thông tin bổ sung từ thiết bị.   |

> Các field `id`, `address`, `altitude`, `accuracy`, `createdAt` **không còn** trong response.

---

## 1.7 `GET /gps-tracking/history`

Truy vấn lịch sử tọa độ GPS trong một khoảng thời gian. Kết quả phân trang.

```http
GET https://live.fleetwork.vn/api/v1/gps-tracking/history
    ?UserId=user-001
    &FromTime=1713225600000
    &ToTime=1713311999000
    &PageNumber=1
    &PageSize=100
    &SortDescending=false
X-API-Key: <key>
```

### Query Parameters

| Parameter        | Type    | Required | Default | Mô tả                          |
| ---------------- | ------- | -------- | ------- | ------------------------------ |
| `DeviceId`       | String  | No       | —       | Lọc theo ID thiết bị.          |
| `VehicleId`      | String  | No       | —       | Lọc theo ID xe.                |
| `UserId`         | String  | No       | —       | Lọc theo ID nhân viên.         |
| `FromTime`       | Int64   | No       | —       | Thời gian bắt đầu (Unix ms).   |
| `ToTime`         | Int64   | No       | —       | Thời gian kết thúc (Unix ms).  |
| `Status`         | Int     | No       | —       | `1` (moving) / `2` (stopping). |
| `PageNumber`     | Int32   | No       | 1       | Trang hiện tại.                |
| `PageSize`       | Int32   | No       | 100     | Số bản ghi / trang.            |
| `SortDescending` | Boolean | No       | true    | `true` = mới nhất lên đầu.     |

### Response `200 OK`

```json
{
  "trackingData": [
    {
      "lat": 21.0285,
      "lng": 105.8542,
      "time": 1713225600000,
      "speed": 40.0,
      "heading": 90,
      "userId": "user-001",
      "vehicleId": "vehicle-abc",
      "deviceId": "device-xyz",
      "statusCode": 1,
      "data": null,
      "metadata": {}
    }
  ],
  "totalCount": 240,
  "page": 1,
  "pageSize": 100,
  "totalPages": 3,
  "hasNextPage": true,
  "hasPreviousPage": false
}
```

| Field                       | Type    | Mô tả                                              |
| --------------------------- | ------- | -------------------------------------------------- |
| `trackingData[].time`       | Long    | Unix ms lúc ghi nhận vị trí (thay `timestamp`).    |
| `trackingData[].statusCode` | Integer | `0` = LostGps, `1` = Moving, `2` = Stopping (int). |

> Các field `altitude`, `accuracy`, `address`, `createdAt` **không còn** trong response.

---

## 1.8 `GET /gps-tracking/packages/route`

Đọc lộ trình đã đi của kiện hàng, theo **mã kiện** mà client đã gắn ở `packages` khi push GPS ([1.1](#11-post-gps-tracking)).

```http
GET https://live.fleetwork.vn/api/v1/gps-tracking/packages/route
    ?codes=%23231,%233333
X-API-Key: <key>
```

### Query Parameters

| Parameter   | Type   | Required | Default | Mô tả                                                                     |
| ----------- | ------ | -------- | ------- | ------------------------------------------------------------------------- |
| `codes`     | String | No       | —       | Danh sách mã kiện, phân tách bằng dấu phẩy: `#231,#3333`. Ký tự `#` **phải** encode thành `%23`. |
| `from`      | String | No       | —       | Mốc bắt đầu, ISO 8601 (`2026-08-11T00:00:00Z`).                           |
| `to`        | String | No       | —       | Mốc kết thúc, ISO 8601. Khoảng lọc là `[from, to)`.                       |
| `maxPoints` | Int    | No       | 2000    | Trần số điểm trả về **cho mỗi kiện**. Tối đa `20000`.                     |

> Riêng endpoint này dùng **ISO 8601** cho `from`/`to` (các endpoint khác trong tài liệu dùng Unix ms). Trong response, `time` vẫn là Unix ms.

**Hai hành vi khác nhau đáng lưu ý:**

| Cách gọi                      | Phạm vi thời gian                                                                    |
| ----------------------------- | ------------------------------------------------------------------------------------ |
| **Có `codes`**                | **Không giới hạn thời gian.** Đây là đường chính: khách đưa mã kiện, không cần biết ngày. |
| Không `codes`, không `from`/`to` | Tự giới hạn **7 ngày gần nhất** — trả về mọi kiện đã di chuyển trong khoảng đó, tránh quét toàn bộ lịch sử. |

Giá trị thực sự áp dụng luôn được echo lại ở `appliedFrom` / `appliedTo`, nên client phân biệt được câu trả lời đầy đủ với câu trả lời đã bị giới hạn mà không phải đoán.

### Response `200 OK`

```json
{
  "packages": [
    {
      "packageCode": "#231",
      "from": "2026-08-11T02:38:20",
      "to": "2026-08-11T02:39:20",
      "pointCount": 2,
      "returnedPointCount": 2,
      "carriers": [
        {
          "id": "3f2a91c4-8d7e-5b16-9c03-1a4e7f2b6d85",
          "carrierKind": 0,
          "userId": "user-001",
          "vehicleId": "vehicle-abc",
          "deviceId": null,
          "from": "2026-08-11T02:38:20",
          "to": "2026-08-11T02:39:20"
        }
      ],
      "points": [
        {
          "time": 1713235100000,
          "timeStamp": "2026-08-11T02:38:20",
          "lat": 21.0285,
          "lng": 105.8542,
          "speed": 45.2,
          "heading": 90,
          "status": 1
        },
        {
          "time": 1713235160000,
          "timeStamp": "2026-08-11T02:39:20",
          "lat": 21.029,
          "lng": 105.855,
          "speed": 38.0,
          "heading": 92,
          "status": 1
        }
      ],
      "polyline": null,
      "distanceM": null,
      "source": "live"
    },
    {
      "packageCode": "#3333",
      "from": "2026-08-11T02:38:20",
      "to": "2026-08-11T02:38:20",
      "pointCount": 1,
      "returnedPointCount": 1,
      "carriers": ["..."],
      "points": ["..."],
      "source": "live"
    }
  ],
  "total": 2,
  "appliedFrom": null,
  "appliedTo": null,
  "maxPointsPerPackage": 2000
}
```

### Response Fields

| Field                          | Type    | Mô tả                                                                     |
| ------------------------------ | ------- | ------------------------------------------------------------------------- |
| `total`                        | Integer | Số kiện trả về.                                                           |
| `appliedFrom` / `appliedTo`    | String  | Khoảng thời gian **thực sự** đã áp dụng (`null` = không giới hạn).        |
| `maxPointsPerPackage`          | Integer | Trần điểm đã áp dụng sau khi kẹp về `[1, 20000]`.                         |
| `packages[].pointCount`        | Integer | Số điểm **đã ghi nhận** cho kiện.                                          |
| `packages[].returnedPointCount`| Integer | Số điểm **trong response** — nhỏ hơn `pointCount` khi bị giảm mẫu.        |
| `packages[].carriers[]`        | Array   | Các chặng vận chuyển, mỗi chặng một người/xe chở. Xem bên dưới.           |
| `packages[].points[].time`     | Long    | Unix ms — dùng field này khi cần chính xác múi giờ.                        |
| `packages[].points[].timeStamp`| String  | ISO 8601 theo giờ **UTC**, không kèm hậu tố offset.                       |
| `packages[].points[].status`   | Integer | `0` = LostGps, `1` = Moving, `2` = Stopping.                              |
| `packages[].source`            | String  | `live` = còn dữ liệu từng điểm · `archive` = lộ trình cũ đã nén.          |
| `packages[].polyline`          | String  | Chỉ có khi `source = "archive"`: lộ trình đã nén thành polyline, `points[]` rỗng. |
| `packages[].distanceM`         | Long    | Quãng đường (mét), chỉ có với `source = "archive"`.                        |

### `carriers[]` — đơn sang tay giữa đường

`carriers` **tách theo chặng**, không lặp lại người chở trên từng điểm. Một đơn được bàn giao giữa đường (shipper → xe tải) sẽ có **nhiều phần tử**, nói rõ ai chở đoạn nào — trong khi `points[]` vẫn là một lộ trình liền mạch theo thứ tự thời gian:

```json
"carriers": [
  { "id": "…", "carrierKind": 0, "userId": "shipper-01", "from": "2026-08-11T02:38:20", "to": "2026-08-11T03:10:00" },
  { "id": "…", "carrierKind": 1, "vehicleId": "truck-77",  "from": "2026-08-11T03:12:40", "to": "2026-08-11T05:02:15" }
]
```

| `carrierKind` | Loại    |
| ------------- | ------- |
| `0`           | User    |
| `1`           | Vehicle |
| `4`           | Account |

### Khi vượt trần điểm

Hệ thống **giảm mẫu đều** chứ không cắt đuôi — giữ cả điểm đầu và điểm cuối. Cắt đuôi sẽ khiến gói hàng trông như dừng giữa chừng; giảm mẫu giữ đúng hình dạng lộ trình từ đầu đến cuối. So `returnedPointCount` với `pointCount` để biết dữ liệu đã bị giảm mẫu hay chưa; cần đầy đủ thì nâng `maxPoints` (tối đa `20000`).

### Xác thực

Chỉ cần `X-API-Key`. Account được suy ra từ key — client **không** gửi account id.

### Mã lỗi

| Status | `error`                | Nguyên nhân                                                          |
| ------ | ---------------------- | -------------------------------------------------------------------- |
| `401`  | `UNAUTHORIZED`         | Thiếu / sai `X-API-Key`, không xác định được account.                |
| `400`  | `INVALID_RANGE`        | `to` không lớn hơn `from`.                                            |
| `403`  | `route_scope_missing`  | Key chưa được cấp scope cho route này ở gateway.                     |
| `500`  | `PACKAGE_ROUTE_FAILED` | Lỗi khi đọc lộ trình — liên hệ Fleetwork support.                    |

---

# PHẦN 2 — DASHBOARD

Base path: `https://live.fleetwork.vn/api/v1/dashboard/gps-manager`

| Vùng               | Endpoint                | Mô tả                                               |
| ------------------ | ----------------------- | --------------------------------------------------- |
| 4 KPI Cards        | `GET /summary`          | Active nhân viên, km, thời gian, chi phí nhiên liệu |
| Bảng users         | `GET /users`            | Per-user: km, thời gian, nhiên liệu, trạng thái     |
| Heatmap            | `GET /activity-heatmap` | Day × Hour grid                                     |
| Biểu đồ nhiên liệu | `GET /fuel-tracking`    | km vs lít theo ngày/tuần/tháng                      |
| Chi phí tháng      | `GET /monthly-costs`    | Stacked bar: nhiên liệu, bảo dưỡng, bảo hiểm        |
| All-in-One         | `GET /`                 | Tất cả trong 1 request                              |

---

## 2.1 `GET /summary` — KPI Overview

```http
GET https://live.fleetwork.vn/api/v1/dashboard/gps-manager/summary?date=1713225600000
X-API-Key: <key>
```

### Query Parameters

| Parameter | Type  | Required | Default                     | Mô tả                 |
| --------- | ----- | -------- | --------------------------- | --------------------- |
| `date`    | Int64 | No       | Đầu ngày hiện tại (Unix ms) | Mốc ngày cần lấy KPI. |

### Response `200 OK`

```json
{
  "date": 1713225600000,
  "activeEmployees": { "active": 4, "total": 25 },
  "totalDistance": { "value": 936.9, "unit": "km" },
  "totalTravelTime": { "totalSeconds": 111000, "formatted": "30 giờ 50 phút" },
  "totalFuelCost": {
    "value": 900000,
    "currency": "VND",
    "formatted": "900,000 VND"
  },
  "generatedAt": 1713235200000
}
```

| Field                       | Type    | Mô tả                                            |
| --------------------------- | ------- | ------------------------------------------------ |
| `date`                      | Int64   | Timestamp đầu ngày đang tổng hợp (Unix ms).      |
| `activeEmployees.active`    | Integer | Số nhân viên có GPS point trong 5 phút gần nhất. |
| `activeEmployees.total`     | Integer | Tổng nhân viên trong account.                    |
| `totalDistance.value`       | Double  | Tổng km đi trong ngày.                           |
| `totalTravelTime.formatted` | String  | Ví dụ: `"30 giờ 50 phút"`.                       |
| `totalFuelCost.formatted`   | String  | Ví dụ: `"900,000 VND"`.                          |

---

## 2.2 `GET /users`

```http
GET https://live.fleetwork.vn/api/v1/dashboard/gps-manager/users
  ?date=1713225600000&page=1&pageSize=20
X-API-Key: <key>
```

### Query Parameters

| Parameter  | Type   | Required | Default                     | Mô tả                                 |
| ---------- | ------ | -------- | --------------------------- | ------------------------------------- |
| `date`     | Int64  | No       | Đầu ngày hiện tại (Unix ms) | Mốc ngày cần thống kê.                |
| `status`   | String | No       | all                         | `moving` / `stopped` / `signal_lost`. |
| `page`     | Int    | No       | 1                           | Trang.                                |
| `pageSize` | Int    | No       | 20                          | Bản ghi / trang.                      |

### Response `200 OK`

```json
{
  "date": 1713225600000,
  "summary": { "total": 7, "moving": 4, "stopped": 2, "signalLost": 1 },
  "users": [
    {
      "userId": "user-001",
      "distance": { "value": 165.0, "unit": "km" },
      "travelTime": { "totalSeconds": 30600, "formatted": "8h 30m" },
      "fuel": {
        "consumedLiters": 11.6,
        "costVnd": 266000,
        "costFormatted": "266,000 VND",
        "efficiencyKmPerL": 14.2
      },
      "statusCode": 1,
      "status": "moving",
      "lastLocation": {
        "lat": 21.0285,
        "lng": 105.8542,
        "speed": 45.2,
        "time": 1713235100000
      },
      "lastSeenAt": 1713235100000
    }
  ],
  "pagination": { "page": 1, "pageSize": 20, "totalItems": 7, "totalPages": 1 }
}
```

**Trạng thái user:**

| `statusCode` | `status`      | Điều kiện                             |
| ------------ | ------------- | ------------------------------------- |
| `1`          | `moving`      | `speed > 0` và `lastSeenAt < 15 phút` |
| `2`          | `stopping`    | `speed = 0` và `lastSeenAt < 15 phút` |
| `0`          | `signal_lost` | `(now - lastSeenAt) > 15 phút`        |

---

## 2.3 `GET /activity-heatmap`

```http
GET https://live.fleetwork.vn/api/v1/dashboard/gps-manager/activity-heatmap
  ?from=1712793600000&to=1713484799000&metric=distance
X-API-Key: <key>
```

### Query Parameters

| Parameter | Type   | Required | Default                        | Mô tả                                       |
| --------- | ------ | -------- | ------------------------------ | ------------------------------------------- |
| `from`    | Int64  | No       | 14 ngày trước (Unix ms)        | Thời gian bắt đầu khoảng lọc.               |
| `to`      | Int64  | No       | Hiện tại / cuối ngày (Unix ms) | Thời gian kết thúc khoảng lọc.              |
| `metric`  | String | No       | `distance`                     | `distance` (km) / `points` (số GPS points). |
| `userId`  | String | No       | —                              | Lọc nhân viên cụ thể.                       |

### Response `200 OK`

```json
{
  "from": 1712793600000,
  "to": 1713484799000,
  "metric": "distance",
  "cells": [
    {
      "dayOfWeek": "Thu",
      "date": 1713312000000,
      "hour": 16,
      "value": 60.0,
      "label": "60 km"
    }
  ],
  "maxValue": 124.5,
  "minValue": 0.0,
  "totalCells": 168
}
```

| Field               | Type    | Mô tả                                       |
| ------------------- | ------- | ------------------------------------------- |
| `from`              | Int64   | Timestamp bắt đầu range (Unix ms).          |
| `to`                | Int64   | Timestamp kết thúc range (Unix ms).         |
| `cells[].dayOfWeek` | String  | Tên ngày trong tuần (Mon, Tue, ..., Sun).   |
| `cells[].date`      | Int64   | Timestamp đầu ngày của ô heatmap (Unix ms). |
| `cells[].hour`      | Integer | Giờ trong ngày (0–23).                      |
| `cells[].value`     | Double  | Giá trị km hoặc số GPS points.              |
| `cells[].label`     | String  | Nhãn hiển thị, ví dụ: `"60 km"`.            |
| `maxValue`          | Double  | Giá trị lớn nhất (dùng tính màu gradient).  |
| `totalCells`        | Integer | Tổng số ô (số ngày × 24).                   |

---

## 2.4 `GET /fuel-tracking`

```http
GET https://live.fleetwork.vn/api/v1/dashboard/gps-manager/fuel-tracking
    ?from=1704067200000&to=1735689599000&groupBy=week
X-API-Key: <key>
```

### Query Parameters

| Parameter | Type   | Required | Default                    | Mô tả                                |
| --------- | ------ | -------- | -------------------------- | ------------------------------------ |
| `from`    | Int64  | No       | Đầu kỳ hiện tại (Unix ms)  | Thời gian bắt đầu khoảng phân tích.  |
| `to`      | Int64  | No       | Cuối kỳ hiện tại (Unix ms) | Thời gian kết thúc khoảng phân tích. |
| `groupBy` | String | No       | `week`                     | `day` / `week` / `month`.            |
| `userId`  | String | No       | —                          | Lọc nhân viên.                       |

### Response `200 OK`

```json
{
  "from": 1704067200000,
  "to": 1735689599000,
  "groupBy": "week",
  "fuelEfficiency": { "value": 16.8, "unit": "km/L", "trend": "improving" },
  "series": [
    {
      "period": "2026-W01",
      "label": "T1/W1",
      "distanceKm": 820.5,
      "fuelLiters": 48.8,
      "efficiencyKmPerL": 16.8
    }
  ],
  "totals": {
    "totalDistanceKm": 18420.0,
    "totalFuelLiters": 1096.4,
    "avgEfficiencyKmPerL": 16.8
  }
}
```

| Field                  | Mô tả                                                            |
| ---------------------- | ---------------------------------------------------------------- |
| `from`                 | Timestamp bắt đầu khoảng dữ liệu (Unix ms).                      |
| `to`                   | Timestamp kết thúc khoảng dữ liệu (Unix ms).                     |
| `fuelEfficiency.trend` | `improving` / `declining` / `stable`.                            |
| `series[].period`      | `"2026-W01"` (tuần), `"2026-01"` (tháng), `"2026-04-01"` (ngày). |

---

## 2.5 `GET /monthly-costs`

```http
GET https://live.fleetwork.vn/api/v1/dashboard/gps-manager/monthly-costs
    ?from=1704067200000&to=1735689599000&currency=VND
X-API-Key: <key>
```

### Query Parameters

| Parameter  | Type   | Required | Default                     | Mô tả                               |
| ---------- | ------ | -------- | --------------------------- | ----------------------------------- |
| `from`     | Int64  | No       | Đầu năm hiện tại (Unix ms)  | Thời gian bắt đầu khoảng tổng hợp.  |
| `to`       | Int64  | No       | Cuối năm hiện tại (Unix ms) | Thời gian kết thúc khoảng tổng hợp. |
| `currency` | String | No       | `VND`                       | Đơn vị tiền tệ trả về.              |

### Response `200 OK`

```json
{
  "from": 1704067200000,
  "to": 1735689599000,
  "currency": "VND",
  "months": [
    {
      "month": 3,
      "label": "Mar",
      "costs": {
        "fuel": 1200000,
        "maintenance": 7000000,
        "insurance": 3000000,
        "other": 1000000,
        "total": 12200000
      }
    }
  ],
  "totals": {
    "fuel": 14400000,
    "maintenance": 28000000,
    "insurance": 12000000,
    "other": 4000000,
    "grandTotal": 58400000
  },
  "categories": [
    { "key": "fuel", "label": "Nhiên liệu", "color": "#4F8EF7" },
    { "key": "maintenance", "label": "Bảo dưỡng", "color": "#F7C24F" },
    { "key": "insurance", "label": "Bảo hiểm", "color": "#4FF79B" },
    { "key": "other", "label": "Khác", "color": "#F74F4F" }
  ]
}
```

| Field                  | Type   | Mô tả                                         |
| ---------------------- | ------ | --------------------------------------------- |
| `from`                 | Int64  | Timestamp bắt đầu khoảng tổng hợp (Unix ms).  |
| `to`                   | Int64  | Timestamp kết thúc khoảng tổng hợp (Unix ms). |
| `months[].month`       | Int    | Tháng trong năm (`1..12`).                    |
| `months[].label`       | String | Nhãn hiển thị tháng.                          |
| `months[].costs.total` | Long   | Tổng chi phí của tháng.                       |
| `totals.grandTotal`    | Long   | Tổng chi phí toàn bộ khoảng `from..to`.       |

---

## 2.6 `GET /` — All-in-One

Load toàn bộ Dashboard trong 1 request.

```http
GET https://live.fleetwork.vn/api/v1/dashboard/gps-manager?date=1713225600000
X-API-Key: <key>
```

```json
{
  "summary": { "...": "response của 2.1" },
  "users": {
    "summary": { "total": 7, "moving": 4, "stopped": 2, "signalLost": 1 },
    "topUsers": []
  },
  "heatmap": { "...": "response của 2.3, 14 ngày gần nhất" },
  "fuelTracking": { "...": "response của 2.4, groupBy=week" },
  "monthlyCosts": { "...": "response của 2.5" }
}
```

---

# PHẦN 3 — BÁO CÁO

Base path: `https://live.fleetwork.vn/api/v1/reports`

**Tất cả báo cáo hỗ trợ xuất file** — thêm `?format=excel` hoặc `?format=csv`.

---

## 3.1 `GET /reports/trip/summary`

Báo cáo hành trình tổng hợp theo nhân viên.

```http
GET https://live.fleetwork.vn/api/v1/reports/trip/summary
  ?from=1711929600000&to=1712793599000&page=1&pageSize=20
X-API-Key: <key>
```

### Query Parameters

| Parameter  | Type    | Required | Default         | Mô tả                                                 |
| ---------- | ------- | -------- | --------------- | ----------------------------------------------------- |
| `from`     | Int64   | **Yes**  | —               | Thời gian bắt đầu (Unix ms).                          |
| `to`       | Int64   | **Yes**  | —               | Thời gian kết thúc (Unix ms).                         |
| `userId`   | String  | No       | —               | Lọc nhân viên.                                        |
| `groupId`  | UUID    | No       | —               | Lọc nhóm.                                             |
| `page`     | Int     | No       | 1               | Trang.                                                |
| `pageSize` | Int     | No       | 20 (max 200)    | Bản ghi / trang.                                      |
| `sortBy`   | String  | No       | `totalDistance` | `totalDistance` / `travelTime` / `maxSpeed` / `name`. |
| `sortDesc` | Boolean | No       | true            | Giảm dần.                                             |
| `format`   | String  | No       | —               | `excel` / `csv`.                                      |

### Response `200 OK`

```json
{
  "reportType": "trip_summary",
  "from": 1711929600000,
  "to": 1712793599000,
  "employees": [
    {
      "userId": "user-001",
      "name": "Nguyễn Văn A",
      "groupName": "Đội A",
      "totalDistanceKm": 1245.8,
      "travelTime": { "totalSeconds": 152100, "formatted": "42 giờ 30 phút" },
      "stopTime": { "totalSeconds": 29700, "formatted": "8 giờ 15 phút" },
      "maxSpeedKmh": 95.0,
      "minSpeedKmh": 15.0,
      "tripDays": 10
    }
  ],
  "pagination": { "page": 1, "pageSize": 20, "totalItems": 4, "totalPages": 1 },
  "generatedAt": 1713235200000
}
```

---

## 3.2 `GET /reports/trip/detail`

1 hàng = 1 nhân viên × 1 ngày.

```http
GET https://live.fleetwork.vn/api/v1/reports/trip/detail
  ?from=1711929600000&to=1712793599000&page=1&pageSize=50
X-API-Key: <key>
```

Query parameters tương tự 3.1 (`pageSize` max 500).

### Response `200 OK`

```json
{
  "reportType": "trip_detail",
  "trips": [
    {
      "date": 1712707200000,
      "userId": "user-001",
      "name": "Nguyễn Văn A",
      "startTime": 1712734200000,
      "endTime": 1712771100000,
      "distanceKm": 156.8,
      "travelTime": { "totalSeconds": 30600, "formatted": "8 giờ 30 phút" },
      "stopTime": { "totalSeconds": 6300, "formatted": "1 giờ 45 phút" },
      "maxSpeedKmh": 95.0,
      "minSpeedKmh": 15.0,
      "startLocation": {
        "lat": 21.0285,
        "lng": 105.8542,
        "address": "Hà Nội, Hoàn Kiếm"
      },
      "endLocation": {
        "lat": 21.035,
        "lng": 105.8,
        "address": "Hà Nội, Cầu Giấy"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "totalItems": 38,
    "totalPages": 1
  },
  "generatedAt": 1713235200000
}
```

---

| Field               | Type  | Mô tả                                     |
| ------------------- | ----- | ----------------------------------------- |
| `trips[].date`      | Int64 | Timestamp đầu ngày của bản ghi (Unix ms). |
| `trips[].startTime` | Int64 | Timestamp bắt đầu hành trình (Unix ms).   |
| `trips[].endTime`   | Int64 | Timestamp kết thúc hành trình (Unix ms).  |

## 3.3 `GET /reports/fuel/summary`

```http
GET https://live.fleetwork.vn/api/v1/reports/fuel/summary
    ?from=1711929600000&to=1712793599000&page=1&pageSize=20
X-API-Key: <key>
```

### Query Parameters

| Parameter  | Type    | Required | Default      | Mô tả                                                          |
| ---------- | ------- | -------- | ------------ | -------------------------------------------------------------- |
| `from`     | Int64   | **Yes**  | —            | Thời gian bắt đầu (Unix ms).                                   |
| `to`       | Int64   | **Yes**  | —            | Thời gian kết thúc (Unix ms).                                  |
| `userId`   | String  | No       | —            | Lọc nhân viên.                                                 |
| `groupId`  | UUID    | No       | —            | Lọc nhóm.                                                      |
| `page`     | Int     | No       | 1            | Trang.                                                         |
| `pageSize` | Int     | No       | 20 (max 200) | Bản ghi / trang.                                               |
| `sortBy`   | String  | No       | `distanceKm` | `distanceKm` / `fuelStandardLiters` / `totalCostVnd` / `name`. |
| `sortDesc` | Boolean | No       | true         | Giảm dần.                                                      |
| `format`   | String  | No       | —            | `excel` / `csv`.                                               |

### Response `200 OK`

```json
{
  "reportType": "fuel_summary",
  "fuelConfig": { "efficiencyLper100km": 7.0, "pricePerLiterVnd": 23000 },
  "employees": [
    {
      "userId": "user-001",
      "name": "Nguyễn Văn A",
      "groupName": "Đội A",
      "distanceKm": 1245.8,
      "travelTime": { "totalSeconds": 152100, "formatted": "42 giờ 30 phút" },
      "fuelStandardLiters": 87.2,
      "totalCostVnd": 2005600,
      "totalCostFormatted": "2.005.600 VNĐ"
    }
  ],
  "totals": {
    "distanceKm": 4446.2,
    "fuelStandardLiters": 311.2,
    "totalCostVnd": 7157600,
    "totalCostFormatted": "7.157.600 VNĐ"
  },
  "pagination": { "page": 1, "pageSize": 20, "totalItems": 4, "totalPages": 1 },
  "generatedAt": 1713235200000
}
```

> Định mức = `distanceKm × efficiencyLper100km / 100` | Chi phí = `fuelStandardLiters × pricePerLiterVnd`

---

## 3.4 `GET /reports/fuel/detail`

1 hàng = 1 nhân viên × 1 ngày.

```http
GET https://live.fleetwork.vn/api/v1/reports/fuel/detail
  ?from=1711929600000&to=1712793599000&page=1&pageSize=50
X-API-Key: <key>
```

### Query Parameters

| Parameter  | Type    | Required | Default      | Mô tả                                                          |
| ---------- | ------- | -------- | ------------ | -------------------------------------------------------------- |
| `from`     | Int64   | **Yes**  | —            | Thời gian bắt đầu (Unix ms).                                   |
| `to`       | Int64   | **Yes**  | —            | Thời gian kết thúc (Unix ms).                                  |
| `userId`   | String  | No       | —            | Lọc nhân viên.                                                 |
| `groupId`  | UUID    | No       | —            | Lọc nhóm.                                                      |
| `page`     | Int     | No       | 1            | Trang.                                                         |
| `pageSize` | Int     | No       | 50 (max 500) | Bản ghi / trang.                                               |
| `sortBy`   | String  | No       | `date`       | `date` / `distanceKm` / `fuelStandardLiters` / `totalCostVnd`. |
| `sortDesc` | Boolean | No       | true         | Giảm dần.                                                      |
| `format`   | String  | No       | —            | `excel` / `csv`.                                               |

### Response `200 OK`

```json
{
  "reportType": "fuel_detail",
  "trips": [
    {
      "date": 1712707200000,
      "userId": "user-001",
      "name": "Nguyễn Văn A",
      "startTime": 1712734200000,
      "endTime": 1712771100000,
      "distanceKm": 156.8,
      "travelTime": { "totalSeconds": 30600, "formatted": "8 giờ 30 phút" },
      "fuelStandardLiters": 11.0,
      "fuelPricePerLiterVnd": 23000,
      "totalCostVnd": 253000,
      "totalCostFormatted": "253.000 VNĐ"
    }
  ],
  "totals": {
    "distanceKm": 746.5,
    "fuelStandardLiters": 52.3,
    "totalCostVnd": 1202900,
    "totalCostFormatted": "1.202.900 VNĐ"
  },
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "totalItems": 38,
    "totalPages": 1
  },
  "generatedAt": 1713235200000
}
```

| Field               | Type  | Mô tả                                     |
| ------------------- | ----- | ----------------------------------------- |
| `trips[].date`      | Int64 | Timestamp đầu ngày của bản ghi (Unix ms). |
| `trips[].startTime` | Int64 | Timestamp bắt đầu hành trình (Unix ms).   |
| `trips[].endTime`   | Int64 | Timestamp kết thúc hành trình (Unix ms).  |

---

## 3.5 `GET /reports/activity-time`

Mỗi hàng = 1 ngày × 1 giờ, thống kê toàn đội.

```http
GET https://live.fleetwork.vn/api/v1/reports/activity-time
  ?from=1711929600000&to=1712793599000
X-API-Key: <key>
```

### Query Parameters

| Parameter  | Type   | Required | Default         |
| ---------- | ------ | -------- | --------------- |
| `from`     | Int64  | **Yes**  | —               |
| `to`       | Int64  | **Yes**  | —               |
| `page`     | Int    | No       | 1               |
| `pageSize` | Int    | No       | 50 (max 500)    |
| `format`   | String | No       | `excel` / `csv` |

### Response `200 OK`

```json
{
  "reportType": "activity_time",
  "totalEmployees": 20,
  "rows": [
    {
      "date": 1712707200000,
      "hour": 7,
      "activeEmployeeCount": 3,
      "inactiveEmployeeCount": 17,
      "totalDistanceKm": 45.2
    },
    {
      "date": 1712707200000,
      "hour": 8,
      "activeEmployeeCount": 8,
      "inactiveEmployeeCount": 12,
      "totalDistanceKm": 112.8
    }
  ],
  "totals": { "totalDistanceKm": 2461.1 },
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "totalItems": 18,
    "totalPages": 1
  },
  "generatedAt": 1713235200000
}
```

| Field         | Type  | Mô tả                                           |
| ------------- | ----- | ----------------------------------------------- |
| `rows[].date` | Int64 | Timestamp đầu ngày của dòng thống kê (Unix ms). |
| `rows[].hour` | Int32 | Giờ trong ngày, từ `0` đến `23`.                |

---

## Error Responses

| Status                      | Mô tả                                     |
| --------------------------- | ----------------------------------------- |
| `400 Bad Request`           | Tham số không hợp lệ.                     |
| `401 Unauthorized`          | API key thiếu hoặc sai.                   |
| `403 Forbidden`             | Không có quyền truy cập resource.         |
| `404 Not Found`             | Resource không tồn tại.                   |
| `429 Too Many Requests`     | Vượt giới hạn rate limit.                 |
| `500 Internal Server Error` | Lỗi hệ thống — liên hệ Fleetwork support. |

```json
{ "error": "BAD_REQUEST", "message": "...", "code": 400 }
```
