Public API
Public API Documentation
Ready
Core System APIs
-
-
-
-
-
-
-
-
-
Ricoh Machine Control
-
-
-
-
Scan Folder Queue
-
-
-
Camera Recording Control
-
API For Database (CRUD)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Scan Email Destinations
-
-
-
VPS Scan Folders
-
-
Agent PC Settings & Utilities
-
-
-
-
-
Click Test to run API check...
Full API Reference
This includes both the core polling APIs and the new database CRUD endpoints.
# Public API for CRM
Base URL (production):
- `https://agentapi.quanlymay.com`
These endpoints are read-only and return JSON.
Note:
- `POST /api/login` and `POST /api/login/google` now include `workspaceIds` in the returned user object.
- User-to-workspace membership is stored in the `UserWorkspace` table.
- Clients no longer need to send any tenant or scope parameter to the API. Protected endpoints resolve request scope from `X-API-Token`; internal CRUD endpoints fall back to the default server scope when needed.
- Table-backed GET/list responses expose audit fields as `created_at`, `updated_at`, and camelCase aliases `createAt`, `updateAt`.
## 1) Machine list
- Method: `GET`
- Path: `/machinelist/`
- Query params (optional):
- `lan_uid`: filter by LAN id
Example:
```bash
curl -s "https://agentapi.quanlymay.com/machinelist/"
```
Response:
```json
{
"ok": true,
"count": 2,
"machines": [
{
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"mac_id": "00:26:73:7D:78:F9",
"agent_uid": "agent-pc-01",
"printer_name": "Aficio MP 9002",
"ip": "192.168.1.224",
"counter_total": 3653272,
"system_status": "Status OK",
"toner_black": {
"state": "Status OK"
},
"created_at": "2026-03-01T16:00:00+00:00",
"updated_at": "2026-03-02T00:00:00+00:00",
"last_counter_at": "2026-03-02T00:00:00+00:00",
"last_status_at": "2026-03-02T00:00:00+00:00"
}
]
}
```
## 2) Network list
- Method: `GET`
- Path: `/networklist/`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/networklist/"
```
Response:
```json
{
"ok": true,
"count": 1,
"networks": [
{
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"machine_count": 2,
"last_seen_at": "2026-03-02T00:00:00+00:00"
}
]
}
```
## 3) Existing infor list (kept as-is)
- Method: `GET`
- Path: `/api/infor/list`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/api/infor/list"
```
Notes:
- The `counter_data` and `status_data` fields are JSON objects.
- `mac_id` is normalized in `AA:BB:CC:DD:EE:FF` format.
## 4) Get device infor by MAC ID
- Method: `GET`
- Path: `/api/public/device/by-mac`
- Query params (required):
- `mac_id` (or `mac`) – MAC address, any format: `00:26:73:7D:78:F9`, `00-26-73-7D-78-F9`, or `0026737D78F9`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/api/public/device/by-mac?mac_id=0026737D78F9"
```
Notes:
- Success responses always normalize `mac_id` to `AA:BB:CC:DD:EE:FF`.
- Invalid MAC format returns `400`.
- Unknown device returns `404`.
Response:
```json
{
"ok": true,
"mac_id": "00:26:73:7D:78:F9",
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"agent_uid": "agent-pc-01",
"printer_name": "Aficio MP 9002",
"ip": "192.168.1.224",
"counter": {
"total": "3653272"
},
"ok": true,
"mac_id": "00:26:73:7D:78:F9",
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"agent_uid": "agent-pc-01",
"printer_name": "Aficio MP 9002",
"ip": "192.168.1.224",
"counter": {
"total": "3653272"
},
"status": {
"system_status": "Status OK"
},
"last_counter_at": "2026-03-02T00:00:00+00:00",
"last_status_at": "2026-03-02T00:00:00+00:00",
"updated_at": "2026-03-02T00:00:00+00:00"
}
```
## 4b) Query real-time device infor by MAC ID (Force fresh check)
- Method: `GET`
- Path: `/api/public/device/by-mac-now`
- Query params (required):
- `mac_id` (or `mac`) – MAC address, any format: `00:26:73:7D:78:F9`, `00-26-73-7D-78-F9`, or `0026737D78F9`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/api/public/device/by-mac-now?mac_id=0026737D78F9"
```
Notes:
- Unlike `/api/public/device/by-mac` which returns cached database values, this endpoint actively triggers an on-demand SNMP/status query command on the managing Agent.
- The request blocks (long-polls) and waits up to 10 seconds for the Agent response.
- Success responses contain fresh, real-time counters and status.
- Automatically updates the database cache for subsequent passive queries.
- Returns `504` on Agent communication timeout, or `400` if the managing Agent is offline.
Response: Same format as `/api/public/device/by-mac` but with real-time data.
## 5) Check online status by MAC ID
- Method: `GET`
- Path: `/api/public/device/online-status`
- Query params:
- `mac_id` (required) – MAC address, any format: `00:26:73:7D:78:F9`, `00-26-73-7D-78-F9`, or `0026737D78F9`
- `stale_seconds` (optional) – seconds without polling before considered offline (default: `300`)
Example:
```bash
curl -s "https://agentapi.quanlymay.com/api/public/device/online-status?mac_id=00-26-73-7D-78-F9"
```
Response (online):
```json
{
"ok": true,
"mac_id": "00:26:73:7D:78:F9",
"is_online": true,
"printer_name": "Aficio MP 9002",
"ip": "192.168.1.224",
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"last_seen_at": "2026-03-21T05:00:00+00:00",
"seconds_since_seen": 42,
"stale_threshold_seconds": 300,
"online_source": "polling",
"is_online_by_polling": true,
"is_online_by_flag": true
}
```
Response (offline):
```json
{
"ok": true,
"mac_id": "00:26:73:7D:78:F9",
"is_online": false,
"printer_name": "Aficio MP 9002",
"ip": "192.168.1.224",
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"last_seen_at": "2026-03-21T04:00:00+00:00",
"seconds_since_seen": 3642,
"stale_threshold_seconds": 300,
"online_source": "none",
"is_online_by_polling": false,
"is_online_by_flag": false
}
```
**Logic xác định online:**
- `is_online_by_polling = true` nếu `updated_at < 300s` (agent đang chạy và poll data)
- `is_online_by_flag = true` nếu `Printer.is_online = true` (set bởi hệ thống `_refresh_stale_offline`)
- `is_online = is_online_by_polling OR is_online_by_flag`
- `online_source`: `"polling"` | `"printer_flag"` | `"none"`
Response (not found):
```json
{ "ok": false, "error": "Device not found" }
```
## 6) Get all machine infor by LAN UID
- Method: `GET`
- Path: `/api/public/network/by-lan`
- Query params (required):
- `lan_uid`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/api/public/network/by-lan?lan_uid=lanf-33ef2446897e0a57"
```
Response:
```json
{
"ok": true,
"lan_uid": "lanf-33ef2446897e0a57",
"count": 2,
"rows": [
{
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"mac_id": "00:26:73:7D:78:F9",
"agent_uid": "agent-pc-01",
"printer_name": "Aficio MP 9002",
"ip": "192.168.1.224",
"counter": { "total": "3653272" },
"status": { "system_status": "Status OK" },
"last_counter_at": "2026-03-02T00:00:00+00:00",
"last_status_at": "2026-03-02T00:00:00+00:00",
"created_at": "2026-03-01T16:00:00+00:00",
"updated_at": "2026-03-02T00:00:00+00:00"
}
]
}
```
## 6) Get all data (all LANs, all machines)
- Method: `GET`
- Path: `/all/`
- Query params (optional):
- `lan_uid`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/all/"
```
Response:
```json
{
"ok": true,
"count": 4,
"rows": [
{
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"machine_uid": "00:26:73:7D:78:F9",
"mac_id": "00:26:73:7D:78:F9",
"agent_uid": "agent-pc-01",
"printer_name": "Aficio MP 9002",
"ip": "192.168.1.224",
"counter": { "total": "3653272" },
"status": { "system_status": "Status OK" },
"last_counter_at": "2026-03-02T00:00:00+00:00",
"last_status_at": "2026-03-02T00:00:00+00:00",
"created_at": "2026-03-01T16:00:00+00:00",
"updated_at": "2026-03-02T00:00:00+00:00"
}
]
}
```
## 7) Agent machine export for CRM
- Method: `GET`
## 8) Get workspaces linked to a user
- Method: `GET`
- Path: `/api/user/workspaces`
- Query params (required):
- `user_id`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/api/user/workspaces?user_id=7"
```
Response:
```json
{
"ok": true,
"user_id": 7,
"rows": [
{
"id": "ws-1",
"name": "Công ty TNHH Gox Print",
"logo": "🏭",
"color": "#2196F3",
"address": "123 Nguyễn Huệ, Q1, TP.HCM",
"created_at": "2026-03-10"
}
]
}
```
## 9) Get users linked to a workspace
- Method: `GET`
- Path: `/api/workspace/users`
- Query params (required):
- `workspace_id`
Example:
```bash
curl -s "https://agentapi.quanlymay.com/api/workspace/users?workspace_id=ws-1"
```
Response:
```json
{
"ok": true,
"workspace_id": "ws-1",
"rows": [
{
"id": 7,
"lead": "default",
"username": "tech1",
"full_name": "Lê Minh Cường",
"email": "tech1@kythuat.vn",
"type": "tech",
"role": "tech",
"workspaceIds": ["ws-1", "ws-2", "ws-3"]
}
]
}
```
- Path: `/api/public/agent-machines`
- Query params (required):
- `agent_uid`
CRM partners use this endpoint to read the machines that belong to a PC agent (Tier 2). Each machine entry contains the lan/fingerprint metadata that already exists inside `DeviceInfor`, `LanSite`, and the new `NetworkInfo` table, plus counter, status, toner, alert, feature, and lock/unlock history derived from polling.
Example response:
```json
{
"ok": true,
"lead": "default",
"agent_uid": "agent-pc-01",
"agent": {
"hostname": "PC-01",
"local_ip": "192.168.1.10",
"local_mac": "11:22:33:44:55:66"
},
"count": 2,
"machines": [
{
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"lan_name": "Factory A",
"fingerprint_signature": "lead=default|subnet=192.168.1.0/24|gateway_ip=192.168.1.1|gateway_mac=AA:BB:CC:DD:EE:FF",
"network": {
"network_id": "net-001",
"network_name": "Factory A LAN",
"office_name": "Factory Floor",
"real_address": "123 Factory Rd, District 1"
},
"agent_uid": "agent-pc-01",
"printer_name": "Aficio MP 9002",
"mac_id": "00:26:73:7D:78:F9",
"ip": "192.168.1.224",
"counter_total": 3653272,
"counter_summary": {
"copier_bw": 238346,
"printer_bw": 316916,
"fax_bw": 0
},
"status": "Status OK",
"alert": "Energy Saver Mode",
"toner": {
"state": "Status OK"
},
"auto_alert": {
"severity": "warning",
"message": "Black toner is low",
"status": "pending",
"triggered_at": "2026-03-09T01:02:03+00:00",
"resolved_at": ""
},
"features": [
{
"feature": "address_book",
"enabled": true,
"metadata": {},
"last_seen_at": "2026-03-09T00:00:00+00:00"
}
],
"lock_history": [
{
"action": "lock",
"reason": "Service window",
"source": "lead",
"event_at": "2026-03-09T01:05:00+00:00",
"metadata": {}
}
],
"last_counter_at": "2026-03-02T00:00:00+00:00",
"last_status_at": "2026-03-02T00:00:00+00:00",
"updated_at": "2026-03-02T00:00:00+00:00"
}
]
}
```
## 8) Ricoh machine lock/unlock
Use these endpoints to queue a lock or unlock command for a Ricoh device already known by the server. The backend pushes the command to the matching agent through polling, then waits for the agent result.
- List devices first: `GET /api/devices`
- Unlock a Ricoh machine: `POST /api/devices/<mac_id>/unlock`
- Lock a Ricoh machine: `POST /api/devices/<mac_id>/lock`
- Legacy alias kept for compatibility: `PATCH /api/devices/<mac_id>/enable` with body `{ "enabled": true|false }`
Notes:
- Use the `mac_id` value returned by `GET /api/devices`.
- The server accepts `mac_id` in these formats: `AA:BB:CC:DD:EE:FF`, `AA-BB-CC-DD-EE-FF`, or `AABBCCDDEEFF`.
- Success responses normalize `mac_id` to `AA:BB:CC:DD:EE:FF`.
- Success returns `200`.
- Agent-side failure returns `409`.
- Timeout waiting for the agent returns `504`.
Test commands:
```bash
curl -s "https://agentapi.quanlymay.com/api/devices"
```
```bash
curl -s -X POST "https://agentapi.quanlymay.com/api/devices/0026737D78F9/unlock"
```
```bash
curl -s -X POST "https://agentapi.quanlymay.com/api/devices/00-26-73-7D-78-F9/lock"
```
Example success response:
```json
{
"ok": true,
"id": 123,
"mac_id": "00:26:73:7D:78:F9",
"enabled": true,
"action": "unlock",
"changed_at": "2026-04-07T10:15:30+00:00",
"command_id": 456
}
```
## 8A) Device scan-folder assignment
Use this endpoint when the client only knows the Ricoh machine MAC and wants the server to choose the correct Windows agent automatically.
- Discover target machines first: `GET /api/devices`
- Queue scan-folder create/update/delete: `POST /api/devices/<mac_id>/scan-folder`
- Legacy alias kept for compatibility: `POST /api/agents/<agent_id>/ftp-sites`
Notes:
- The server resolves the printer by `mac_id`, reads its `lead + lan_uid`, then picks an agent on the same LAN before queueing the command.
- When multiple same-LAN agents exist, the server prefers an online agent, then the most recently seen one.
- If the body also contains `mac_id`, it must match the path device.
- `create` normally sends `scan_path`; `update` and `delete` normally send `site_name`.
- `scan_path` can be a simple folder name or a full path on the chosen agent.
- If `scan_path` is a simple name, the agent creates the folder under its default scan root.
- `site_name`, `ftp_user`, `ftp_password`, and `port` remain compatibility fields; clients usually do not need them.
- If an explicit `port` is already used by another FTP site, the server returns `409`.
- If no explicit `port` is sent and the default port is busy, the server auto-picks the next free port and returns a `warning`.
- Commands are queued only. The chosen agent applies them on its next polling cycle.
Example create payload:
```json
{
"action": "create",
"scan_path": "scan_002673D3250B"
}
```
Queue create:
```bash
curl -s -X POST "https://agentapi.quanlymay.com/api/devices/00:26:73:D3:25:0B/scan-folder" \
-H "Content-Type: application/json" \
-d "{\"action\":\"create\",\"scan_path\":\"scan_002673D3250B\"}"
```
Queue update:
```bash
curl -s -X POST "https://agentapi.quanlymay.com/api/devices/002673D3250B/scan-folder" \
-H "Content-Type: application/json" \
-d "{\"action\":\"update\",\"site_name\":\"scan_002673D3250B\",\"scan_path\":\"C:/Scans/Ricoh-01\"}"
```
Queue delete:
```bash
curl -s -X POST "https://agentapi.quanlymay.com/api/devices/00-26-73-D3-25-0B/scan-folder" \
-H "Content-Type: application/json" \
-d "{\"action\":\"delete\",\"site_name\":\"scan_002673D3250B\"}"
```
Example success response:
```json
{
"ok": true,
"queued": true,
"command_id": 123,
"status": "pending",
"action": "create",
"lead": "default",
"lan_uid": "lanf-33ef2446897e0a57",
"agent_id": 35,
"agent_uid": "tony",
"agent_local_ip": "192.168.1.10",
"agent_is_online": true,
"mac_id": "00:26:73:D3:25:0B",
"port": 2121,
"scan_path": "scan_002673D3250B",
"site_name": "scan_002673D3250B",
"printer_name": "MP 6503",
"printer_ip": "192.168.1.226",
"printer_agent_uid": "tony",
"warning": ""
}
```
Example validation error when no same-LAN agent exists:
```json
{
"ok": false,
"error": "No agent found on lan_uid lanf-33ef2446897e0a57 for printer 00:26:73:D3:25:0B",
"mac_id": "00:26:73:D3:25:0B",
"lan_uid": "lanf-33ef2446897e0a57",
"available_agents": [
{
"id": 12,
"agent_uid": "agent-pc-01",
"lan_uid": "lanf-deadbeef",
"local_ip": "192.168.5.10",
"is_online": false
}
]
}
```
## 8B) Legacy agent queue alias
If the client already knows the exact host agent, it can still call:
- `POST /api/agents/<agent_id>/ftp-sites`
The server keeps the same `action=create|update|delete` payload shape, still validates `lan_uid` for `create`, and now returns the same enriched response fields as the device-based route above.
## 9) List tasks
- Method: `GET`
- Path: `/api/tasks`
- Query params:
- `agent_uid` (optional)
- `mac_id` or `mac` (optional)
- `status` (optional; one of `backlog`, `selected`, `in-progress`, `review`, `done`, `blocked`)
- `assignee_id` (optional)
Returns the matching tasks. If `X-API-Token` is present, the server scopes the list to the account scope behind that token. Each task links back to `UserAccount` records and reuses the device counters and status tracked inside `DeviceInfor`.
## 10) Create a task
- Method: `POST`
- Path: `/api/tasks`
- Headers:
- `X-API-Token: <token>` (same API token as `/api/polling`)
- Payload:
```json
{
"lan_uid": "lanf-33ef2446897e0a57",
"agent_uid": "agent-pc-01",
"network_id": "net-001",
"mac_id": "00:26:73:7D:78:F9",
"title": "Check paper tray",
"description": "Tray 1 keeps emptying",
"status": "backlog",
"priority": "high",
"reporter_id": 42,
"assignee_id": 7,
"customer_id": 5
}
```
The server derives the task scope from `X-API-Token`, assigns a `task_key`, stores the task in `Task`, and links it to the reporting worker (`reporter_id`), assignee, and customer accounts.
## 11) Update a task
- Method: `PATCH`
- Path: `/api/tasks/<task_id>`
- Headers:
- `X-API-Token: <token>`
- Payload:
- All other fields are optional; only include the fields you want to change.
- Status transitions are limited to Jira-like values (`backlog`, `selected`, `in-progress`, `review`, `done`, `blocked`).
- Provide `status_updated_at` or `completed_at` timestamps when relevant.
- The server resolves request scope from `X-API-Token`.
### Delete a task
- Method: `DELETE`
- Path: `/api/tasks/<task_id>`
- Headers:
- `X-API-Token: <token>`
The server resolves request scope from `X-API-Token`.
Returns:
```json
{
"ok": true,
"id": 123
}
```
All task activity materializes in `Task`, while user assignment metadata lives in `UserAccount` together with workspace/location mappings. The `DeviceFeatureFlag`, `DeviceLockHistory`, and `MachineAlert` tables hold the supplementary machine features mentioned earlier (address book, lock/unlock history, automated alerts).
## 12) Workspaces CRUD
- **List:** `GET /api/workspaces` (Params: `name`, `address`, `date_from`, `date_to`)
- **Create:** `POST /api/workspaces` (Body: `id`, `name`, `logo`, `color`, `address`, `userIds`)
- **Update:** `PATCH /api/workspaces/<ws_id>` (Body: `name`, `logo`, `color`, `address`, `userIds`)
- **Delete:** `DELETE /api/workspaces/<ws_id>`
- Response rows include `userIds`, `userCount`, `locationIds`, and `locationCount`.
## 13) Locations CRUD
- **List:** `GET /api/locations` (Params: `name`, `workspace_id`, `date_from`, `date_to`)
- **Create:** `POST /api/locations` (Body: `id`, `name`, `address`, `room`, `phone`, `machine_count`, `workspace_id`)
- **Update:** `PATCH /api/locations/<loc_id>` (Body: `name`, `address`, `room`, `phone`, `machine_count`, `workspace_id`)
- **Delete:** `DELETE /api/locations/<loc_id>`
- Each location belongs to at most one workspace. `workspace_id` is validated on create/update.
## 14) Materials CRUD
- **List:** `GET /api/materials` (Params: `name`, `repair_id`, `date_from`, `date_to`)
- **Create:** `POST /api/materials` (Body: `id`, `repair_request_id`, `name`, `quantity`, `unit_price`, `total_price`)
- **Update:** `PATCH /api/materials/<mat_id>` (Body: `repair_request_id`, `name`, `quantity`, `unit_price`, `total_price`)
- **Delete:** `DELETE /api/materials/<mat_id>`
## 15) Users CRUD
- **List:** `GET /api/users` (Params: `username`, `fullname`, `type`, `role`, `date_from`, `date_to`)
- **Create:** `POST /api/users` (Body: `username`, `password`, `full_name`, `email`, `phone_number`, `type`, `workspaceIds`, `is_active`, `notes`)
- **Update:** `PATCH /api/users/<user_id>` (Body: same as Create)
- **Delete:** `DELETE /api/users/<user_id>`
- User `type` is normalized to one of `tech` or `support`. Legacy `role` input is still accepted as an alias and normalized to the same two values. Create requests inherit the default server scope when no internal scope is supplied.
## 16) Networks/Companies CRUD
- **List:** `GET /api/networks` (Params: `lan_uid`, `name`, `office`, `date_from`, `date_to`)
- **Create:** `POST /api/networks` (Body: `lan_uid`, `network_id`, `network_name`, `office_name`, `real_address`, `notes`)
- **Update:** `PATCH /api/networks/<net_id>` (Body: same as Create)
- **Delete:** `DELETE /api/networks/<net_id>`
- Create requests inherit the default server scope when no internal scope is supplied.
## UserWorkspace membership
- Membership between users and workspaces is stored in `UserWorkspace`.
- A user can belong to many workspaces, and a workspace can contain many users.
- Login endpoints return `workspaceIds` so the frontend can show the workspace picker.
- The read APIs below are the canonical lookup methods for the current membership model.
## 17) Scan Email Destinations (CRUD)
- **List:** `GET /api/lan-emails`
- Query params (optional):
- `lead`: filter by lead (e.g., `default`)
- `lan_uid`: filter by LAN UID
- `email`: search/filter by email address substring
- `agent_uid`: filter/determine master status
- Response:
```json
{
"ok": true,
"is_master": false,
"rows": [
{
"id": 1,
"lead": "default",
"lan_uid": "lan-1234",
"email": "user@example.com",
"email_number": 2130,
"email_type": "common",
"pc_name": "PC-01",
"ftp_user": "goxprint",
"ftp_password": "goxprint",
"created_at": "2026-06-11 07:00:00"
}
]
}
```
- **Create:** `POST /api/lan-emails`
- Headers: `Content-Type: application/json`
- Body params (required):
- `lead`: lead name
- `lan_uid`: LAN UID
- `email`: email destination address
- Body params (optional):
- `email_type`: type of email (e.g., `common`, default: `common`)
- `pc_name`: target computer name
- Response:
```json
{
"ok": true,
"email": {
"id": 2,
"lead": "default",
"lan_uid": "lan-1234",
"email": "user2@example.com",
"email_number": 2131,
"email_type": "common",
"pc_name": "PC-02"
}
}
```
- **Delete:** `DELETE /api/lan-emails/<email_id>`
- Response:
```json
{
"ok": true,
"id": 2
}
```
## 18) VPS Scan Folders (List/Delete files)
- **List Files:** `GET /api/scans/files`
- Query params (required):
- `lan_uid`: LAN UID of target network
- `email`: email folder to list scans from
- Response:
```json
{
"ok": true,
"rows": [
{
"name": "scan_20260611_070000.pdf",
"size": 1048576,
"mtime": "2026-06-11T00:00:00Z",
"url": "/static/scans/lan-1234/user_example_com/scan_20260611_070000.pdf",
"upload_duration": 4.2,
"upload_completed_at": "2026-06-11T00:00:04Z"
}
]
}
```
- **Delete File:** `DELETE /api/scans/file`
- Query/Body params (required):
- `lan_uid`: LAN UID of target network
- `email`: email folder of the file
- `filename`: filename of the scan file to delete
- Response:
```json
{
"ok": true,
"message": "Successfully deleted file scan_20260611_070000.pdf"
}
```
## 19) Agent Settings & PC Utilities
- **General Settings:** `POST /api/agents/<agent_uid>/settings`
- Headers: `Content-Type: application/json`
- Query/Body params (required):
- `lead`: lead name
- `scan_auto_open_file`: boolean (e.g. `true`)
- `scan_auto_open_dir`: boolean (e.g. `true`)
- Response:
```json
{
"ok": true,
"message": "Settings command queued",
"command_id": 456
}
```
- **Trigger Utility Action:** `POST /api/agents/<agent_uid>/utility/<action>`
- Action path parameter can be one of:
- `devices_and_printers`: open Devices and Printers control panel
- `open_scan_folder`: open the local scan directory in Explorer
- `dxdiag`: run DirectX Diagnostics for hardware info
- `change_ip`: configure IP address settings
- Body params (optional):
- `new_ip`: (required for `change_ip`) new IP address
- `subnet_mask`: (optional for `change_ip`) subnet mask
- `gateway`: (optional for `change_ip`) gateway address
- Response:
```json
{
"ok": true,
"message": "Utility action 'devices_and_printers' queued",
"command_id": 457
}
```
## 20) Camera Recording Control
- **Camera Recording Control:** `POST /api/cameras/record-control` (Aliases: `POST /api/public/camera/control`, `POST /api/public/camera/record`, `POST /api/cameras/record`)
- Headers: `Content-Type: application/json`
- Body params:
- `mac_id` / `mac` / `camera_mac`: MAC address of target camera. Optional if `ip` or `camera_id` is supplied.
- `ip`: (optional fallback) IP address of camera if `mac_id` is not passed.
- `camera_id` / `id`: (optional fallback) Camera ID if `mac_id` is not passed.
- `agent_uid` / `agent`: (optional) ID of managing agent. If omitted, automatically selects an online agent in the same LAN.
- `action`: `'start'`, `'record'`, or `'stop'` (default: `'start'`).
- `duration`: (optional, default: 30) Recording duration limit in seconds (for `'record'`) or minutes (for `'start'`).
- Responses:
- **Success (Start/Record/Stop):**
```json
{
"ok": true,
"message": "Đã bắt đầu ghi hình thành công với giới hạn 30 phút"
}
```
- **Error:**
```json
{
"ok": false,
"error": "Không có Agent trực tuyến nào để thực hiện thao tác"
}
```
## 21) Camera Configurations (CRUD)
- **List LAN Cameras:** `GET /api/public/camera/list` (Aliases: `GET /api/cameras/list`, `GET /api/agents/<agent_uid>/cameras`)
- Query Parameters (optional):
- `lan_uid`: Unique ID of the LAN site.
- `agent_uid`: Unique ID of the Agent node.
- Returns all active and configured cameras within the requested LAN site (`lan_uid`) or Agent (`agent_uid`), deduplicated by MAC address.
- Response:
```json
{
"ok": true,
"cameras": [
{
"id": 3232235778,
"agent_uid": "kythuat02",
"camera_name": "Camera 192.168.1.67 (Camera 192.168.1.108)",
"rtsp_url": "rtsp://admin:Donga2011@192.168.1.67:554/cam/realmonitor?channel=1&subtype=0",
"segment_duration": 60,
"prefix": "rec",
"video_codec": "copy",
"audio_codec": "copy",
"no_audio": true,
"is_recording": false,
"ip": "192.168.1.67, 192.168.1.108",
"mac_address": "24:14:07:43:31:4D",
"manufacturer": "Sigmastar",
"model": "Camera IP",
"is_online": true
}
]
}
```
- **Save/Edit Camera:** `POST /api/agents/<agent_uid>/cameras`
- Headers: `Content-Type: application/json`
- Body params:
- `id`: (optional, for editing) configuration ID
- `camera_name`: (optional, default: "Camera")
- `rtsp_url`: (required) RTSP stream URL
- `segment_duration`: (optional, default: 60)
- `prefix`: (optional, default: "rec")
- `video_codec`: (optional, default: "copy")
- `audio_codec`: (optional, default: "copy")
- `no_audio`: (optional, default: true)
- Response:
```json
{
"ok": true,
"camera_id": 1
}
```
- **Delete Camera:** `POST /api/agents/<agent_uid>/cameras/<int:camera_id>/delete`
- Response:
```json
{
"ok": true
}
```
## 15) Public IP check
- Method: `GET`
- Path: `/api/public/ip/public`
- Description: Get the public IP address of the caller.
- Response:
```json
{
"ok": true,
"public_ip": "115.79.88.99"
}
```
## 16) Workstation (Local) IP check
- Method: `GET`
- Path: `/api/public/ip/workstation`
- Query params (provide at least one):
- `agent_uid` – Agent unique ID
- `lan_uid` – LAN unique ID
- `mac` (or `mac_id`) – MAC address of a printer or device registered under the agent
- Response:
```json
{
"ok": true,
"local_ip": "192.168.1.50",
"hostname": "DESKTOP-TOEFTR1",
"agent_uid": "agent_01",
"lan_uid": "lan_01",
"app_version": "2.0.79",
"is_online": true,
"last_seen_at": "2026-07-15T03:00:00Z"
}
```