API Reference Complete REST API endpoint reference.
All routes are under /api. Protected routes require Authorization: Bearer <JWT>. Admin routes additionally require role == "admin". Super-admin routes require a role: "superadmin" JWT.
Method Path Body Returns POST /login{username, password}{access_token, refresh_token}POST /register{username, email?, password, nik(16), nama_ktp, nama_samaran, nomor_hp, role?, preferences?}{access_token, refresh_token}POST /refresh{refresh_token}{access_token}POST /auth/check-username{username}{available, message}GET /send-verification-code?phone=— sends WhatsApp reset link POST /update-password{code, new_password(min6)}{message}POST /superadmin/login{username, password}{access_token, message}
Method Path Description POST /logoutClears stored refresh token GET /profileReturns user (NIK decrypted) PUT /profileEdit profile (multipart: nik, nama_ktp, nama_samaran, nomor_hp, email, password, preferences, role_ids, image) GET /users/:idRoles for the authenticated user POST /caregiver/patientsBody {patient_id} — caller must have pendamping role GET /caregiver/patientsList the caller's patients GET /patient/caregiversList the caller's caregivers GET /patientsAll users with pasien role GET /caregiversAll users with pendamping role DELETE /caregiver/patients/:idRemove a patient link
Method Path Body Notes GET /roles— List all roles POST /roles{name, is_editable}GET /roles/:id— PUT /roles/:id{name, is_editable?}Non-editable roles rejected DELETE /roles/:id— Non-editable roles rejected POST /admins/:id/roles{role_ids:[]}ON CONFLICT DO NOTHINGGET /users/:id/roles— Roles for a user DELETE /admins/:id/roles{role_ids:[]}
Method Path Body POST /kategori{nama_kategori}GET /kategori— PUT /kategori/:id— DELETE /kategori/:id—
Method Path Query Notes GET /faskes/nearbylat, lng, radius?(m, default 5000)Haversine + Google Places fallback; in-memory grid cache (4h, 1km hit radius) GET /faskes/— {results, status}GET /faskes/locationlat, lngDB Haversine within 50 km GET /faskes/searchkeywordLIKE on nama GET /faskes/:id— :id is Google place_id
Admin: POST/PUT/DELETE /faskes[/:id].
Public read uses OptionalJWT (populates is_liked/is_saved if logged in).
Method Path Auth Description GET /forum/categoriesoptional List categories GET /forum/threads?category_id=optional List threads GET /forum/threads/:idoptional Thread detail + nested replies + counts POST /forum/threadsJWT Create (multipart: category_id, title, content, use_pseudonym, image) → fires WhatsApp broadcast PUT /forum/threads/:idJWT Edit (multipart) DELETE /forum/threads/:idJWT Owner only POST /forum/threads/:id/replyJWT Reply (multipart: content, use_pseudonym, parent_id?, image) → notifies thread creator POST /forum/threads/:id/likeJWT Toggle → {is_liked} POST /forum/threads/:id/saveJWT Toggle → {is_saved} DELETE /forum/replies/:idJWT Owner only POST /forum/replies/:id/likeJWT Toggle POST /forum/replies/:id/saveJWT Toggle PUT /forum/replies/:idJWT Edit (multipart)
Method Path Auth Description GET /articles/— Scrape Kemenkes live → [{title, url, is_saved}] GET /articles/detail?url=— Scrape full article by URL GET /articles/all— DB-backed list GET /articles/search?title=— GET /articles/url?url=— GET /articles/:id— Numeric DB ID POST /articles/saveJWT Body {url, title} → {is_saved} GET /articles/savedJWT
Admin: POST/PUT/DELETE /articles[/:id] (multipart add).
Method Path Auth Description GET /youtube/videos— DB list GET /youtube/videos/:id— DB by ID GET /youtube/videos/:id/detail— Live YouTube API GET /youtube/channel/:channelId— Latest 20 GET /youtube/search?q=— Search POST /youtube/saveJWT Body {video_id, title, channel_title?, thumbnail_url?} → {is_saved} GET /youtube/savedJWT
Admin: POST/PUT/DELETE /youtube/video[/:id].
Method Path Auth GET /komunitas/— GET /komunitas/:id—
Admin: POST/PUT/DELETE /komunitas[/:id] (multipart with image).
Method Path Auth GET /dokter/— GET /dokter/:id—
Admin: POST/PUT/DELETE /dokter[/:id].
See Backend Reminders for the scheduler. All protected (JWT).
Method Path Body / Query POST /pengingat/{tipe(makan/terapi/kontrol), judul, waktu(RFC3339), timezone?, details(json)}POST /pengingat/obat{judul, timezone?, details:{nama_obat, frekuensi, jadwal:[{jam, jumlah, satuan}], aturan}}GET /pengingat/?tipe=optional tipe filter GET /pengingat/:id— PUT /pengingat/:idpartial update DELETE /pengingat/:id—
Method Path Body Notes POST /push/subscribe{endpoint, keys:{p256dh, auth}, user_id?}no JWT POST /push/send{user_id, payload:{title, body, url?}}no JWT
Method Path Body Notes POST /log/user-logLogUserasync-queued, returns 202 POST /log/admin-logLogAdminasync-queued, returns 202
All require role == "admin".
Method Path Description GET /admins/List admins POST /admins/Create admin (multipart: email, password, nama_ktp, role?, nomor_hp?, image) GET /admins/usersList all users GET /admins/users/:idUser detail (NIK decrypted) PUT /admins/users/:id{is_verified?, role_ids?}GET /admins/caregiver-patient/allAll caregiver–patient relations GET /admins/caregiver-patient?caregiver_id=Caregiver's patients POST /admins/caregiver-patient{caregiver_id, patient_id} (validates pendamping + pasien)DELETE /admins/caregiver-patient{caregiver_id, patient_id}GET /admins/:usernamePUT /admins/:idEdit admin (multipart) DELETE /admins/:idDelete admin
All require a role: "superadmin" JWT, re-verified in each handler.
Method Path Description POST /superadmin/queryBody {query} — arbitrary SQL GET /superadmin/tablesList tables GET /superadmin/tables/:tableNameTable data GET /superadmin/tables/:tableName/exporttext/csvPOST /superadmin/query/exportBody {query} → CSV
Arbitrary SQL
/superadmin/query executes arbitrary SQL. It is the most privileged endpoint in the system and should be tightly network-restricted in production.