# API Endpoints Documentation Dokumentasi lengkap semua endpoint yang tersedia di API Btekno. **Base URL**: `https://api.btekno.cloud` --- ## 📋 Daftar Isi - [Public Endpoints](#public-endpoints) - [Authentication](#authentication) - [Retribusi - Ingest](#retribusi---ingest) - [Retribusi - Frontend CRUD](#retribusi---frontend-crud) - [Locations](#locations) - [Gates](#gates) - [Tariffs](#tariffs) - [Streams](#streams) - [Audit Logs](#audit-logs) - [Entry Events](#entry-events) - [Retribusi - Summary](#retribusi---summary) - [Retribusi - Dashboard](#retribusi---dashboard) - [Retribusi - Realtime](#retribusi---realtime) --- ## Public Endpoints ### Health Check **GET** `/health` Health check endpoint untuk monitoring. **Response:** ```json { "status": "ok", "time": 1703123456 } ``` --- ### Documentation **GET** `/` Redirect ke `/docs` (Swagger UI). **GET** `/docs` Swagger UI documentation (public access). **GET** `/docs/openapi.json` OpenAPI 3.0 specification JSON. --- ## Authentication ### Login **POST** `/auth/v1/login` Login untuk mendapatkan JWT token. **Request Body:** ```json { "username": "admin", "password": "password123" } ``` **Response (200):** ```json { "success": true, "data": { "token": "eyJ0eXAiOiJKV1QiLCJhbGc...", "expires_in": 3600, "user": { "id": 1, "username": "admin", "role": "admin" } }, "timestamp": 1703123456 } ``` **Error Responses:** - `401` - Invalid credentials - `403` - User inactive - `422` - Validation error **Authentication:** None (public endpoint) --- ## Retribusi - Ingest ### Ingest Event **POST** `/retribusi/v1/ingest` Endpoint untuk mesin/IoT mengirim event data. **Headers:** ``` X-API-KEY: your-api-key-here Content-Type: application/json ``` **Request Body:** ```json { "timestamp": 1703123456, "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor" } ``` **Response (200):** ```json { "success": true, "data": { "stored": true }, "timestamp": 1703123456 } ``` **Error Responses:** - `401` - Invalid API key - `404` - Location/Gate/Tariff not found - `422` - Validation error **Authentication:** `X-API-KEY` header **Category Values:** - `person_walk` - `motor` - `car` --- ## Retribusi - Frontend CRUD Semua endpoint di bawah ini memerlukan **JWT Authentication**. **Headers:** ``` Authorization: Bearer Content-Type: application/json ``` ### Locations #### List Locations **GET** `/retribusi/v1/frontend/locations` Get list of locations dengan pagination. **Query Parameters:** - `page` (optional, default: 1) - `limit` (optional, default: 20, max: 100) - `location_code` (optional, filter) **Response (200):** ```json { "success": true, "data": [ { "code": "kerkof_01", "name": "Kerkof 01", "type": "kerkof", "is_active": 1 } ], "meta": { "page": 1, "limit": 20, "total": 10, "pages": 1 }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- #### Get Location Detail **GET** `/retribusi/v1/frontend/locations/{code}` Get detail location berdasarkan code. **Response (200):** ```json { "success": true, "data": { "code": "kerkof_01", "name": "Kerkof 01", "type": "kerkof", "is_active": 1 }, "timestamp": 1703123456 } ``` **Error Responses:** - `404` - Location not found - `422` - Invalid location code **Access:** Viewer, Operator, Admin --- #### Create Location **POST** `/retribusi/v1/frontend/locations` Create new location. **Request Body:** ```json { "code": "kerkof_02", "name": "Kerkof 02", "type": "kerkof", "is_active": 1 } ``` **Response (201):** ```json { "success": true, "data": { "code": "kerkof_02", "name": "Kerkof 02", "type": "kerkof", "is_active": 1 }, "timestamp": 1703123456 } ``` **Access:** Operator, Admin --- #### Update Location **PUT** `/retribusi/v1/frontend/locations/{code}` Update location (code tidak bisa diubah). **Request Body:** ```json { "name": "Kerkof 02 Updated", "type": "kerkof", "is_active": 1 } ``` **Response (200):** ```json { "success": true, "data": { "code": "kerkof_02", "name": "Kerkof 02 Updated", "type": "kerkof", "is_active": 1 }, "timestamp": 1703123456 } ``` **Access:** Operator, Admin --- #### Delete Location **DELETE** `/retribusi/v1/frontend/locations/{code}` Soft delete location (set `is_active = 0`). **Response (200):** ```json { "success": true, "data": { "deleted": true }, "timestamp": 1703123456 } ``` **Access:** Admin only --- ### Gates #### List Gates **GET** `/retribusi/v1/frontend/gates` Get list of gates dengan pagination. **Query Parameters:** - `page` (optional, default: 1) - `limit` (optional, default: 20, max: 100) - `location_code` (optional, filter) **Response (200):** ```json { "success": true, "data": [ { "location_code": "kerkof_01", "gate_code": "gate01", "name": "Gate 01", "direction": "in", "camera": "https://example.com/stream.m3u8", "is_active": 1 } ], "meta": { "page": 1, "limit": 20, "total": 5, "pages": 1 }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- #### Get Gate Detail **GET** `/retribusi/v1/frontend/gates/{location_code}/{gate_code}` Get detail gate berdasarkan location_code dan gate_code. **Response (200):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate01", "name": "Gate 01", "direction": "in", "camera": "https://example.com/stream.m3u8", "is_active": 1 }, "timestamp": 1703123456 } ``` **Error Responses:** - `404` - Gate not found - `422` - Invalid location_code or gate_code **Access:** Viewer, Operator, Admin --- #### Create Gate **POST** `/retribusi/v1/frontend/gates` Create new gate. **Request Body:** ```json { "location_code": "kerkof_01", "gate_code": "gate02", "name": "Gate 02", "direction": "out", "camera": "rtsp://192.168.1.100:554/stream1", "is_active": 1 } ``` **Note:** Field `camera` optional, bisa diisi dengan: - HLS URL: `https://example.com/stream.m3u8` - RTSP URL: `rtsp://192.168.1.100:554/stream1` - HTTP URL: `http://192.168.1.100:8080/camera1` - Camera ID: `camera_001` **Response (201):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate02", "name": "Gate 02", "direction": "out", "camera": "rtsp://192.168.1.100:554/stream1", "is_active": 1 }, "timestamp": 1703123456 } ``` **Access:** Operator, Admin --- #### Update Gate **PUT** `/retribusi/v1/frontend/gates/{location_code}/{gate_code}` Update gate (location_code dan gate_code tidak bisa diubah). **Request Body:** ```json { "name": "Gate 02 Updated", "direction": "out", "camera": "http://192.168.1.100:8080/hls/stream.m3u8", "is_active": 1 } ``` **Note:** Field `camera` optional, bisa diisi dengan: - HLS URL: `https://example.com/stream.m3u8` - RTSP URL: `rtsp://192.168.1.100:554/stream1` - HTTP URL: `http://192.168.1.100:8080/camera1` - Camera ID: `camera_001` **Response (200):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate02", "name": "Gate 02 Updated", "direction": "out", "camera": "http://192.168.1.100:8080/hls/stream.m3u8", "is_active": 1 }, "timestamp": 1703123456 } ``` **Access:** Operator, Admin --- #### Delete Gate **DELETE** `/retribusi/v1/frontend/gates/{location_code}/{gate_code}` Soft delete gate (set `is_active = 0`). **Response (200):** ```json { "success": true, "data": { "deleted": true }, "timestamp": 1703123456 } ``` **Access:** Admin only --- ### Tariffs #### List Tariffs **GET** `/retribusi/v1/frontend/tariffs` Get list of tariffs dengan pagination. **Query Parameters:** - `page` (optional, default: 1) - `limit` (optional, default: 20, max: 100) - `location_code` (optional, filter) - `gate_code` (optional, filter) **Response (200):** ```json { "success": true, "data": [ { "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "price": 5000, "location_name": "Kerkof 01", "gate_name": "Gate 01" } ], "meta": { "page": 1, "limit": 20, "total": 10, "pages": 1 }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- #### Get Tariff Detail **GET** `/retribusi/v1/frontend/tariffs/{location_code}/{gate_code}/{category}` Get detail tariff berdasarkan location_code, gate_code, dan category. **Response (200):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "price": 5000 }, "timestamp": 1703123456 } ``` **Error Responses:** - `404` - Tariff not found - `422` - Invalid location_code, gate_code, or category **Access:** Viewer, Operator, Admin --- #### Create Tariff **POST** `/retribusi/v1/frontend/tariffs` Create new tariff. **Request Body:** ```json { "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "price": 5000 } ``` **Response (201):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "price": 5000 }, "timestamp": 1703123456 } ``` **Access:** Operator, Admin --- #### Update Tariff **PUT** `/retribusi/v1/frontend/tariffs/{location_code}/{gate_code}/{category}` Update tariff price (location_code, gate_code, category tidak bisa diubah). **Request Body:** ```json { "price": 6000 } ``` **Response (200):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "price": 6000 }, "timestamp": 1703123456 } ``` **Access:** Operator, Admin --- #### Delete Tariff **DELETE** `/retribusi/v1/frontend/tariffs/{location_code}/{gate_code}/{category}` Hard delete tariff (permanent delete). **Response (200):** ```json { "success": true, "data": { "deleted": true }, "timestamp": 1703123456 } ``` **Access:** Admin only --- ### Streams #### List Streams **GET** `/retribusi/v1/frontend/streams` Get list of streams (alias untuk gates). **Query Parameters:** - `page` (optional, default: 1) - `limit` (optional, default: 20, max: 100) - `location_code` (optional, filter) **Response (200):** ```json { "success": true, "data": [ { "location_code": "kerkof_01", "gate_code": "gate01", "name": "Gate 01", "direction": "in", "is_active": 1 } ], "meta": { "page": 1, "limit": 20, "total": 5, "pages": 1 }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ### Audit Logs #### List Audit Logs **GET** `/retribusi/v1/frontend/audit-logs` Get audit trail history dengan pagination dan filter. **Query Parameters:** - `page` (optional, default: 1) - `limit` (optional, default: 20, max: 100) - `entity` (optional, filter: `locations|gates|tariffs`) - `action` (optional, filter: `create|update|delete`) - `entity_key` (optional, filter by specific entity key) - `start_date` (optional, format: YYYY-MM-DD) - `end_date` (optional, format: YYYY-MM-DD) **Response (200):** ```json { "success": true, "data": [ { "id": 123, "actor_user_id": 1, "actor_username": "admin", "actor_role": "admin", "action": "update", "entity": "locations", "entity_key": "kerkof_01", "before_json": { "code": "kerkof_01", "name": "Kerkof 01", "type": "kerkof", "is_active": 1 }, "after_json": { "code": "kerkof_01", "name": "Kerkof 01 Updated", "type": "kerkof", "is_active": 1 }, "ip_address": "192.168.1.100", "user_agent": "Mozilla/5.0...", "created_at": "2025-01-17 10:30:00" } ], "meta": { "page": 1, "limit": 20, "total": 150, "pages": 8 }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ### Entry Events #### List Entry Events **GET** `/retribusi/v1/frontend/entry-events` Get list of raw entry events (data mentah dari mesin) dengan pagination dan filter. **Query Parameters:** - `page` (optional, default: 1) - `limit` (optional, default: 20, max: 100) - `location_code` (optional, filter) - `gate_code` (optional, filter) - `category` (optional, filter) - `start_date` (optional, format: YYYY-MM-DD) - `end_date` (optional, format: YYYY-MM-DD) **Response (200):** ```json { "success": true, "data": [ { "id": 12345, "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "event_time": "2025-01-17 10:30:00", "source_ip": "192.168.1.100", "created_at": "2025-01-17 10:30:01" } ], "meta": { "page": 1, "limit": 20, "total": 5000, "pages": 250 }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin **Note:** Endpoint ini berguna untuk debugging dan admin untuk melihat raw event data. --- ## Retribusi - Summary Semua endpoint di bawah ini memerlukan **JWT Authentication**. ### Daily Summary #### Trigger Daily Aggregation **POST** `/retribusi/v1/admin/summary/daily` Trigger manual aggregation untuk daily summary. **Request Body:** ```json { "date": "2025-12-16" } ``` **Response (200):** ```json { "success": true, "data": { "rows_processed": 150, "date": "2025-12-16" }, "timestamp": 1703123456 } ``` **Access:** Admin only --- #### Get Daily Summary **GET** `/retribusi/v1/summary/daily` Get daily summary data. **Query Parameters:** - `date` (required, format: YYYY-MM-DD) - `location_code` (optional, filter) - `gate_code` (optional, filter) **Response (200):** ```json { "success": true, "data": [ { "summary_date": "2025-12-16", "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "total_count": 100, "total_amount": 500000 } ], "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ### Hourly Summary #### Get Hourly Summary **GET** `/retribusi/v1/summary/hourly` Get hourly summary data untuk chart (24 jam). **Query Parameters:** - `date` (required, format: YYYY-MM-DD) - `location_code` (optional, filter) - `gate_code` (optional, filter) **Response (200):** ```json { "success": true, "data": { "labels": ["00", "01", "02", ..., "23"], "series": { "total_count": [10, 15, 20, ..., 5], "total_amount": [50000, 75000, 100000, ..., 25000] } }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ## Retribusi - Dashboard Semua endpoint di bawah ini memerlukan **JWT Authentication**. ### Daily Chart **GET** `/retribusi/v1/dashboard/daily` Get daily chart data (line chart). **Query Parameters:** - `start_date` (required, format: YYYY-MM-DD) - `end_date` (required, format: YYYY-MM-DD) - `location_code` (optional, filter) - `gate_code` (optional, filter) **Response (200):** ```json { "success": true, "data": { "labels": ["2025-12-01", "2025-12-02", "2025-12-03"], "series": { "total_count": [100, 150, 200], "total_amount": [500000, 750000, 1000000] } }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ### By Category Chart **GET** `/retribusi/v1/dashboard/by-category` Get chart data grouped by category (bar/donut chart). **Query Parameters:** - `date` (required, format: YYYY-MM-DD) - `location_code` (optional, filter) - `gate_code` (optional, filter) **Response (200):** ```json { "success": true, "data": { "labels": ["person_walk", "motor", "car"], "series": { "total_count": [50, 100, 30], "total_amount": [100000, 500000, 300000] } }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ### Summary Statistics **GET** `/retribusi/v1/dashboard/summary` Get summary statistics (stat cards). **Query Parameters:** - `date` (required, format: YYYY-MM-DD) - `location_code` (optional, filter) **Response (200):** ```json { "success": true, "data": { "total_count_today": 180, "total_amount_today": 900000, "by_gate": [ { "gate_code": "gate01", "total_count": 100, "total_amount": 500000 } ], "by_category": [ { "category": "motor", "total_count": 100, "total_amount": 500000 } ] }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ## Retribusi - Realtime Semua endpoint di bawah ini memerlukan **JWT Authentication**. ### SSE Stream **GET** `/retribusi/v1/realtime/stream` Server-Sent Events (SSE) stream untuk real-time updates. **Headers:** ``` Authorization: Bearer Accept: text/event-stream ``` **Query Parameters:** - `last_id` (optional, untuk resume dari event tertentu) - `location_code` (optional, filter) **Response (200):** ``` Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive id: 12345 event: ingest data: {"location_code":"kerkof_01","gate_code":"gate01","category":"motor","event_time":1703123456} id: 12346 event: ingest data: {"location_code":"kerkof_01","gate_code":"gate01","category":"car","event_time":1703123457} ... ``` **Access:** Viewer, Operator, Admin **Note:** Connection akan tetap terbuka dan mengirim event setiap ada ingest baru. Ping dikirim setiap 10 detik jika tidak ada data. --- ### Snapshot **GET** `/retribusi/v1/realtime/snapshot` Get snapshot data untuk real-time dashboard cards. **Query Parameters:** - `date` (optional, default: today, format: YYYY-MM-DD) - `location_code` (optional, filter) **Response (200):** ```json { "success": true, "data": { "total_count_today": 180, "total_amount_today": 900000, "by_gate": [ { "gate_code": "gate01", "total_count": 100, "total_amount": 500000 } ], "by_category": [ { "category": "motor", "total_count": 100, "total_amount": 500000 } ] }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin --- ### Realtime Events #### List Realtime Events **GET** `/retribusi/v1/realtime/events` Get list of realtime events (history events untuk SSE) dengan pagination dan filter. **Query Parameters:** - `page` (optional, default: 1) - `limit` (optional, default: 20, max: 100) - `location_code` (optional, filter) - `gate_code` (optional, filter) - `category` (optional, filter) - `start_date` (optional, format: YYYY-MM-DD) - `end_date` (optional, format: YYYY-MM-DD) **Response (200):** ```json { "success": true, "data": [ { "id": 12345, "location_code": "kerkof_01", "gate_code": "gate01", "category": "motor", "event_time": 1703123456, "total_count_delta": 1, "created_at": "2025-01-17 10:30:01" } ], "meta": { "page": 1, "limit": 20, "total": 1000, "pages": 50 }, "timestamp": 1703123456 } ``` **Access:** Viewer, Operator, Admin **Note:** `event_time` adalah Unix timestamp (integer). Endpoint ini untuk melihat history events yang sudah masuk ke realtime_events table. --- ## Error Responses Semua endpoint mengembalikan error dalam format konsisten: ### 401 Unauthorized ```json { "error": "unauthorized", "message": "Invalid or expired token" } ``` ### 403 Forbidden ```json { "error": "forbidden", "message": "Insufficient permissions" } ``` ### 404 Not Found ```json { "error": "not_found", "message": "Resource not found" } ``` ### 409 Conflict ```json { "error": "conflict", "message": "Resource already exists" } ``` ### 422 Validation Error ```json { "error": "validation_error", "fields": { "location_code": "Field is required", "price": "Must be >= 0" } } ``` ### 500 Server Error ```json { "error": "server_error", "message": "Internal server error" } ``` --- ## Authentication & Authorization ### JWT Token - **Algorithm:** HS256 - **TTL:** Configurable via `JWT_TTL_SECONDS` (default: 3600 seconds) - **Header:** `Authorization: Bearer ` ### Roles - **viewer** - Read-only access - **operator** - Read + Write (POST, PUT) - **admin** - Full access (Read + Write + Delete) ### API Key (Ingest) - **Header:** `X-API-KEY: ` - **Config:** `RETRIBUSI_API_KEY` in `.env` --- ## Rate Limiting Saat ini tidak ada rate limiting. Untuk production, pertimbangkan untuk menambahkan rate limiting middleware. --- ## Pagination Semua list endpoints mendukung pagination: - `page` - Page number (default: 1) - `limit` - Items per page (default: 20, max: 100) Response selalu include `meta` object dengan: - `page` - Current page - `limit` - Items per page - `total` - Total items - `pages` - Total pages --- ## Timestamps Semua response include `timestamp` field (Unix timestamp). Untuk date parameters, gunakan format: `YYYY-MM-DD` Untuk datetime, gunakan format: `YYYY-MM-DD HH:MM:SS` atau Unix timestamp (integer). --- ## Support Untuk pertanyaan atau issue, silakan hubungi tim development. **Last Updated:** 2025-12-17