Dokumentasi API (format Postman)

Base URL: {{baseUrl}} (otomatis mengikuti domain server) ยท Auth: Authorization: Bearer <access_token> untuk mobile, token admin global untuk panel.

Backend Laravel 10 (PHP 8.3) โ€” DB SQLite default, bisa switch MySQL via .env. Login mobile selalu lewat backend ini ke server auth myauth.kuroichi.my.id/docs.

Akun uji: dummy001 โ€ฆ dummy100 โ€” password password.

โฌ‡ Import ke Postman (1 klik isi semua request):
Download Collection (.json) Postman โ†’ Import โ†’ Link โ†’ paste โ€ฆ
โš ๏ธ Khusus testing. Endpoint /test/* menulis/menghapus data dan hanya aktif saat APP_ENV != production. Jangan expose token admin ke publik.

Variables (isi sekali di Postman)

VariableContohKeterangan
baseUrllocation.origin + '/api'Otomatis sesuai domain server
api_tokennotif-secret-key-2026Token admin global (.env API_TOKEN)
employee_tokenโ€”Legacy token pegawai (opsional; access token lebih diutamakan)
nup199001010000000001NUP pegawai (demo)
access_tokenโ€”Dari POST /auth/login. Berlaku sampai logout; satu akun boleh punya banyak token.

Ringkasan Alur Login

Login tidak diproses di HP. Semua kredensial lewat backend agar bebas CORS dan server auth tidak pernah diekspos ke client.

  1. MOBILE kirim POST /api/auth/login berisi username + password.
  2. Laravel memanggil POST http://myauth.kuroichi.my.id/apisisdm/api/apps/sign sebagai multipart id + paswd.
  3. Login sukses bila status = "success" dan ada bearer.access_token dari server auth (bukan login_result.code).
  4. Laravel memetakan profil dari data_pegawai_sisdm (fallback login_result) dan mengambil absensi dari absensi_sikap; hanya employees yang di-upsert.
  5. Laravel membuat baris auth_sessions (hash token) dan mengembalikan Bearer token dari objek bearer.
  6. MOBILE menyimpan token di localStorage dan mengirim Authorization: Bearer <access_token> untuk semua request berikutnya.

Multi-login: login kedua dari perangkat lain membuat baris/token baru. Logout hanya mencabut token yang dipakai, token perangkat lain tetap aktif.

Cek status: saat app dibuka, mobile memanggil /auth/userinfo; backend memvalidasi Bearer token ke GET /apisisdm/api/apps/me (sekaligus memperpanjang masa aktif 30 hari). Profil dan absensi ikut segar. Bila server auth tak terjangkau, sesi lokal dipertahankan; bila token ditolak (401), sesi dicabut dan app logout.

Konfigurasi URL login: backend memakai APP_LOGIN_URL di framework/.env (otomatis di-inject ke preview /mobile). Build APK memakai VITE_LOGIN_URL di MOBILE/.env; jika kosong, dipakai VITE_API_URL + /auth/login.

Status Code & Format Respons

KodeArtiBentuk body
200OK (GET / update){"success":true, ...} atau data
201Created (POST){"success":true,"notification_id":1}
400Bad Request (field kurang){"error":"title and body required"}
401Unauthorized / login gagal{"error":"Unauthorized"}
404Not Found (id/route salah){"error":"Not found"}
422User ID / password kosong{"error":"User ID dan password wajib diisi"}
429Terlalu banyak percobaan login{"error":"Terlalu banyak percobaan login, coba lagi nanti"}
502Server auth bermasalah / tidak terjangkau{"error":"Server auth bermasalah: ..."}

Semua respons sukses mengandung success:true (kecuali /health). Error selalu {"error":"<pesan>"} + HTTP code sesuai tabel.

Hak Akses Token (penting)

AksiAdmin tokenAccess token (mobile)
Kirim notifikasi (POST /notifications, /notifications/broadcast)โœ… bolehโŒ 401
Hapus / pulihkan notifikasi (DELETE, /restore)โœ… bolehโŒ 401
CRUD pegawai, admin logโœ… bolehโŒ 401
GET /employees (data diri)โœ… bolehโœ… boleh (self)
Tarik notifikasi (GET /notifications, detail)โœ… bolehโœ… boleh (self)
Tandai dibaca (POST /{id}/acknowledge)โœ… bolehโœ… boleh (self)
Absensi hari iniโœ… bolehโœ… boleh (self)
Lihat / cabut sesi loginโœ… bolehโŒ 401
/auth/userinfo, /auth/refresh, /auth/logoutโ€”โœ… boleh

Admin token = token server (.env API_TOKEN) untuk Admin Panel & kirim. Access token = token dari POST /auth/login, disimpan di tabel auth_sessions (SHA-256). Satu akun boleh login di banyak perangkat; logout hanya mematikan token yang dipakai.

Auth (SSO ISDM via backend)

Alur: MOBILE โ†’ POST {{baseUrl}}/auth/login โ†’ backend โ†’ POST http://myauth.kuroichi.my.id/apisisdm/api/apps/sign. Mobile tidak pernah memanggil server auth langsung (CORS + kredensial aman).

POST {{baseUrl}}/auth/login

{
  "username": "dummy001",
  "password": "password"
}

โœ… 200 Berhasil:

{
  "success": true,
  "access_token": "1509133|C8PytmNhnGkO79TlsUZh0zl6Ipn7KUdDTRlAY87w",
  "token": "1509133|C8PytmNhnGkO79TlsUZh0zl6Ipn7KUdDTRlAY87w",
  "tokens": { "access_token": "...", "token_type": "Bearer", "expires_in": null },
  "user": {
    "nup": "1990012026013357",
    "nama": "TAUFAN MAULANA HARIES",
    "user_id": "taufan",
    "username": "taufan",
    "unit_kerja": "SUBBAGIAN SISTEM INFORMASI SUMBER DAYA MANUSIA",
    "jabatan": "ANALIS SISTEM INFORMASI",
    "status_pegawai": "NON",
    "role_id": 10
  },
  "absensi_sikap": { "jam_masuk": "07:14:01", "jam_keluar": null }
}

Pemetaan field server auth (acuan utama data_pegawai_sisdm):

Server authDisimpan sebagai
data_pegawai_sisdm.NUP_BP_NOSPACE (fallback user_no)NUP (employees.nup)
data_pegawai_sisdm.NAMA (fallback user_name)Nama lengkap (employees.nama)
user_idUsername (employees.username)
data_pegawai_sisdm.UNIT_KERJA_SUB โ†’ UNIT_KERJA (fallback dept_name)Unit kerja (employees.unit_kerja)
data_pegawai_sisdm.NAMA_JABATAN (fallback position_name_2)Jabatan (employees.jabatan)
data_pegawai_sisdm.STATUS_PEGAWAI (menggantikan level_id/position_name)Status pegawai (employees.status_pegawai)
bearer.access_tokenPenanda login sukses + token akses mobile (auth_sessions.access_token = SHA-256)
absensi_sikap.data.jam_masuk / jam_keluarAbsensi hari ini (dibaca langsung, tidak disimpan ke DB)

Email tidak dipakai dan tidak ada kolomnya di database.

โŒ 401 Gagal โ€” code "01" (user/password salah):

{ "error": "User ID atau password salah" }

โŒ 422 Gagal โ€” field kosong:

{ "error": "User ID dan password wajib diisi" }

โŒ 429 Gagal โ€” terlalu banyak percobaan (code "03"):

{ "error": "Terlalu banyak percobaan login, coba lagi nanti" }

GET {{baseUrl}}/auth/userinfo

Auth: Bearer {{access_token}}

Dipakai mobile saat halaman beranda (absensi) dibuka untuk mengecek status login: Bearer token divalidasi ke GET /apisisdm/api/apps/me, profil + absensi diperbarui, dan masa aktif token diperpanjang (sliding 30 hari). Tidak dipanggil dari polling notifikasi/background service. Bila server auth tak terjangkau, profil lokal tetap dipakai; token ditolak โ†’ 401 dan sesi lokal dicabut.

โœ… 200 Berhasil: {"success":true,"user":{...},"absensi_sikap":{"jam_masuk":"08:00","jam_keluar":null}}

POST {{baseUrl}}/auth/refresh

Sama dengan /auth/userinfo (validasi ke /me), ditambah tokens berisi Bearer token yang sama.

{ "access_token": "{{access_token}}" }

โœ… 200: {"success":true,"tokens":{...},"user":{...}} ยท โŒ 401: {"error":"Sesi berakhir, silakan login ulang"}

POST {{baseUrl}}/auth/logout

Auth: Bearer {{access_token}}

Cabut token yang dipakai saja. Token lain milik akun yang sama tetap aktif (multi-login).

โœ… 200: {"success":true}

โŒ 401: {"error":"Token akses tidak valid"}

Sesi & Multi-login

Setiap login menyimpan satu baris di auth_sessions. Token asli hanya dikirim ke client satu kali; database menyimpan hash-nya.

KolomIsi
access_tokenSHA-256 dari token asli (unik)
auth_code00 = sukses
auth_payloadOutput JSON server auth apa adanya
auth_hashSHA-256 dari auth_payload
nup, username, employee_idPemilik sesi
device_uuid, ip_address, user_agentInfo login/perangkat
last_used_at, revoked_atTerakhir dipakai; diisi saat logout

Contoh: HP A dan HP B login akun yang sama โ†’ dua token berbeda. Logout di HP A hanya mengisi revoked_at token A; HP B tetap bisa memakai token B.

Validasi token: Authorization: Bearer <token> โ†’ hash โ†’ cari baris dengan revoked_at = null. Tidak valid/revoked โ†’ 401, app logout.

Umum & Device

GET {{baseUrl}}/health

Publik, tanpa auth. Dipakai mobile untuk status server aktif/tidak.

โœ… 200 Berhasil:

{ "status": "ok", "timestamp": "2026-10-05T08:38:44+00:00" }

GET {{baseUrl}}/app-config

Publik. Konfigurasi client dari server: interval polling (menit). Mobile menyimpannya di localStorage. Diatur dari Admin Panel โ†’ Konfigurasi โ†’ Polling Notifikasi; fallback APP_POLL_INTERVAL_MINUTES di framework/.env.

{
  "status": "ok",
  "poll_interval_minutes": 1,
  "server_time": "2026-10-26T01:15:00+07:00"
}

POST {{baseUrl}}/register

Auth: Bearer {{api_token}}

{
  "device_id": "test-device-001",
  "device_name": "HP Test",
  "platform": "android"
}

โœ… 200 Berhasil: {"success":true}

โŒ 401 Gagal โ€” token salah/kosong: {"error":"Unauthorized"}

โŒ 400 Gagal โ€” device_id kosong: {"error":"device_id required"}

GET {{baseUrl}}/apk/info

Publik. Info APK yang tersedia di public/apk/ (nama, ukuran, waktu update).

{ "apk": { "file": "AppNotifikasi-debug.apk", "size_mb": 8.14, "updated_at": "..." } }

Notifikasi (NUP-locked)

Alur sama dengan halaman /admin: Kirim pakai server token (2 mode: Semua Device / NUP Tertentu), Tarik pakai access token (NUP dari sesi).

A. Kirim Notifikasi (Admin Panel ยท server token)

POST {{baseUrl}}/notifications/broadcast โ€” Tujuan: Semua Device

Auth: Bearer {{api_token}} (server token) ยท setara pilihan "Semua Device" di Admin

{
  "title": "Pengumuman",
  "body": "Untuk semua device"
}

โœ… 201 Berhasil:

{
  "success": true,
  "notification_id": 5,
  "devices_targeted": 3
}

Di log Admin: target_summary = "Semua Device", is_broadcast = 1.

โŒ 400 Gagal โ€” title/body kosong: {"error":"title and body required"}

โŒ 401 Gagal โ€” token bukan access token/admin: {"error":"Unauthorized"}

POST {{baseUrl}}/notifications/broadcast โ€” Tujuan: NUP Tertentu

Auth: Bearer {{api_token}} (server token) ยท setara pilihan "NUP Tertentu" di Admin

Di Admin, NUP diambil dari upload Excel/CSV kolom NUPNIP atau ketik manual (pisah koma/baris). Semua NUP terkirim โ€” termasuk yang belum terdaftar di Data Pegawai (dikirim langsung berdasarkan NUP).

{
  "title": "Khusus",
  "body": "Hanya NUP terpilih",
  "link": "https://example.com/doc.pdf",
  "target_nups": ["199001010000000001"]
}

โœ… 201 Berhasil:

{
  "success": true,
  "notification_id": 6,
  "devices_targeted": 1
}

Di log Admin: target_summary = "NUP Bulk (1)", target_nups_total = 1.

Catatan: API juga menerima device_ids, unit_kerja, jabatan untuk kebutuhan lanjutan (di luar UI Admin). Endpoint POST /notifications tersedia untuk kirim satu pesan dengan body yang sama (tanpa is_broadcast).

B. Tarik di Mobile (NUP-locked ยท access token)

GET {{baseUrl}}/notifications

Auth: Bearer {{access_token}} ยท NUP diambil dari token

Hanya broadcast (is_broadcast) atau NUP dikunci untuk user. Device terima target_nups_count (bukan daftar). Retensi 6 bulan.

โœ… 200 Berhasil:

{
  "notifications": [
    {
      "id": 1,
      "title": "Uji Notifikasi Laravel",
      "body": "Server Laravel",
      "link": "https://example.com/dokumen.pdf",
      "is_broadcast": 1,
      "acknowledged": 0,
      "target_nups_count": 0,
      "created_at": "2026-10-05 10:51:27",
      "updated_at": "2026-10-05 10:51:27"
    }
  ],
  "count": 1
}

โŒ 400 Gagal โ€” tanpa device_id/token/nup:

{
  "error": "device_id, employee_token or nup required"
}

GET {{baseUrl}}/notifications/:id

Detail + target_summary. 404 bila bukan target / lebih tua 6 bulan.

โœ… 200 Berhasil: {"notification":{...,"target_summary":"Semua Device","acknowledged":1}}

โŒ 404 Gagal โ€” bukan target / id tak ada: {"error":"Not found"}

POST {{baseUrl}}/notifications/:id/acknowledge

{
  "nup": "{{nup}}"
}

โœ… 200 Berhasil: {"success":true}

โŒ 401 Gagal โ€” token bukan pegawai & bukan admin: {"error":"Unauthorized"}

โŒ 400 Gagal โ€” device_id kosong: {"error":"device_id required"}

C. Kelola (Admin ยท server token)

DELETE {{baseUrl}}/notifications/:id โ€” soft delete

Sembunyi dari device, badge di Admin. Pulihkan via POST {{baseUrl}}/notifications/:id/restore

โœ… 200 Berhasil: {"success":true} ยท โŒ 404: {"error":"Not found"}

Admin

GET {{baseUrl}}/admin/notifications

Log full (tanpa retensi): targets_count, target_summary, daftar NUP max 200 + target_nups_total.

GET {{baseUrl}}/admin/devices  ยท  GET {{baseUrl}}/admin/stats

GET {{baseUrl}}/admin/settings

Auth: Bearer {{api_token}}

โœ… 200: {"settings":{"poll_interval_minutes":1}}

POST {{baseUrl}}/admin/settings โ€” simpan polling

Auth: Bearer {{api_token}}

{ "poll_interval_minutes": 5 }

โœ… 200: {"success":true,"settings":{"poll_interval_minutes":5}} ยท โŒ 422: nilai harus 1-1440 menit

GET {{baseUrl}}/admin/sessions?nup={{nup}}

Auth: Bearer {{api_token}}

Daftar sesi login per pegawai (max 200): device_uuid, ip_address, user_agent, last_used_at, revoked_at, active. Tanpa nup = semua sesi.

POST {{baseUrl}}/admin/sessions/:id/revoke โ€” logout 1 sesi

Auth: Bearer {{api_token}}

โœ… 200: {"success":true} ยท โŒ 404: {"error":"Not found"}

POST {{baseUrl}}/admin/sessions/revoke-user โ€” logout semua sesi user

Auth: Bearer {{api_token}}

{ "nup": "{{nup}}" }

โœ… 200: {"success":true,"revoked":2} ยท โŒ 400: {"error":"nup or employee_id required"}

Di Admin Panel: tombol ๐Ÿ”‘ pada baris pegawai โ†’ modal Sesi Login โ†’ tombol Logout per sesi atau Logout Semua. Token yang dicabut langsung dapat 401 di request berikutnya (mobile auto-logout).

Pegawai

GET {{baseUrl}}/employees  ยท  GET {{baseUrl}}/employees?nup={{nup}}

Access token โ†’ hanya data diri sendiri. Admin token โ†’ semua + filter nup/search.

โœ… 200 Berhasil: {"employees":[{...,"token":"ef105f..."}],"count":1} ยท โŒ 401: {"error":"Unauthorized"}

POST {{baseUrl}}/employees โ€” tambah (token auto)

{
  "nama": "Budi Santoso",
  "nup": "12345",
  "username": "budi",
  "unit_kerja": "IT",
  "jabatan": "Staff",
  "status_pegawai": "Aktif"
}

โœ… 201 Berhasil: {"success":true,"id":1,"token":"a1b2c3..."}

โŒ 400 Gagal โ€” nama/nup kosong: {"error":"nama and nup required"}

PUT {{baseUrl}}/employees  ยท  DELETE {{baseUrl}}/employees

PUT:    { "id": 1, "jabatan": "Supervisor" }
DELETE: { "id": 1 }

โœ… 200 Berhasil: {"success":true} ยท โŒ 400: {"error":"id required"} ยท โŒ 404: {"error":"Not found"}

POST {{baseUrl}}/employees/:id/regenerate-token

Rotate token tak memutus kiriman (fetch kunci NUP).

โœ… 200 Berhasil: {"success":true,"token":"token-baru..."} ยท โŒ 404: {"error":"Not found"}

Absensi (dari server auth)

Absensi hari ini dibaca langsung dari objek absensi_sikap.data di server auth (jam_masuk/jam_keluar) memakai Bearer token yang sama. Tidak ada tabel attendances dan data tidak disimpan di database.

GET {{baseUrl}}/attendance/today

Auth: Bearer {{access_token}} (self)

โœ… 200: {"attendance":{"jam_masuk":"08:00","jam_keluar":null},"status":"Sudah Masuk","tanggal":"2026-10-25"}

โŒ 401: Bearer token tidak valid / sudah logout ยท โŒ 502: server auth tidak terjangkau

Test Dummy

POST {{baseUrl}}/test/notification โ€” dummy broadcast

{"title":"Uji Coba","body":"Pesan dummy"}

POST {{baseUrl}}/test/reset โ€” bersihkan

Default 3 tabel (notifications, device_notifications, notification_nups). employees/devices aman โ€” kecuali kirim {"tables":["all"]} (semua 6 tabel termasuk auth_sessions, semua token hangus, HP wajib login ulang).

{}

# Reset SEMUA tabel (bahaya):
{"tables":["all"]}

Alur test cepat (urutan Postman)

1. POST /auth/login  {"username":"dummy001","password":"password"}
2. GET  /auth/userinfo        (Bearer access_token)
3. POST /test/notification {}
4. GET  /notifications        (Bearer access_token)
5. POST /auth/logout          (Bearer access_token)