# 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) - [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 --- #### 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", "is_active": 1 } ], "meta": { "page": 1, "limit": 20, "total": 5, "pages": 1 }, "timestamp": 1703123456 } ``` **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", "is_active": 1 } ``` **Response (201):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate02", "name": "Gate 02", "direction": "out", "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", "is_active": 1 } ``` **Response (200):** ```json { "success": true, "data": { "location_code": "kerkof_01", "gate_code": "gate02", "name": "Gate 02 Updated", "direction": "out", "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 #### 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 --- ## 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 --- ## 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