{
  "info": {
    "description": "Generated from embedded OpenAPI. Import the dev environment, fill local account credentials, send Login akun, then individual requests. Management requires an admin session. Data may use api_key or session_token. Mutations are real: do not run all requests automatically. No runtime secrets or business fixtures are exported. MCP uses its separate protocol client and mcp:read key; this is the REST collection.",
    "name": "pantau-mala API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "item": [
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            },
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "['session_token','client_id','api_key','mcp_api_key','key_id'].forEach(key =\u003e pm.environment.unset(key));"
                ],
                "type": "text/javascript"
              }
            },
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) { const body = pm.response.json(); pm.environment.set('session_token', body.token); pm.environment.set('client_id', body.principal.client_id); }"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Login akun",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"client\": \"{{client_slug}}\",\n  \"password\": \"{{password}}\",\n  \"username\": \"{{username}}\"\n}"
            },
            "description": "Login tunggal untuk semua akun; role dan hak platform ditentukan dari database. Username unik global. Tanpa field client/kosong, server memilih membership client aktif paling awal dibuat (created_at, lalu UUID). Client opsional untuk memilih membership lain yang enabled; input role/platform_admin ditolak. Business reads tetap terisolasi pada client sesi, reader tidak bisa management, admin client tidak bisa mengelola platform. Sesi acak 1 jam, tanpa refresh, rate limit 5/min/IP; /api-guide dan Postman menjelaskan integrasi. Console same-origin dapat mengirim X-Console-Session:1 dengan Origin untuk cookie HttpOnly pemulihan sesi; API biasa tetap bearer tanpa cookie.",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "login"
              ],
              "raw": "{{base_url}}/auth/login"
            }
          }
        },
        {
          "name": "Metode akses workspace",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "config"
              ],
              "raw": "{{base_url}}/auth/config"
            }
          }
        },
        {
          "name": "Client dan izin kredensial saat ini",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "me"
              ],
              "raw": "{{base_url}}/auth/me"
            }
          }
        },
        {
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 204 || pm.response.code === 401) { pm.environment.unset('session_token'); }"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Cabut sesi akun ini",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "logout"
              ],
              "raw": "{{base_url}}/auth/logout"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Daftar akun dan client baru",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"challenge\": \"{{challenge}}\",\n  \"client_name\": \"{{new_client_name}}\",\n  \"client_slug\": \"{{new_client_slug}}\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{new_password}}\",\n  \"username\": \"{{new_username}}\",\n  \"website\": \"\"\n}"
            },
            "description": "Memerlukan fitur aktif, SMTP TLS, Redis dan Turnstile server-side (action register, hostname sesuai origin konfigurasi).3percobaan/IP/menit,3percobaan email/24jam. Respons 202 hanya bila email benar-benar dikirim. Duplikat email/username mendapat 409 email_exists/username_exists; pendaftaran pending mendapat 409 registration_pending tanpa resend/rotasi token. Akun/client belum dibuat sampai verifikasi; tidak menerima role/platform_admin/client_id. Token email30menit, single-use, disimpan hash. Workspace kosong setelah verifikasi; quota30RPM,2active keys,3users. Quota3users menghitung user client non-platform; membership owner untuk operasi tidak memakai slot.",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "register"
              ],
              "raw": "{{base_url}}/auth/register"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Verifikasi email registrasi",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"token\": \"{{verification_token}}\"\n}"
            },
            "description": "POST opaque token dari fragment link email.10percobaan/IP/menit. Token invalid/expired/replay400; disabled503. Konsumsi token, akun, membership, role client dan client baru atomik; tidak menghasilkan bearer otomatis.",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "verify-email"
              ],
              "raw": "{{base_url}}/auth/verify-email"
            }
          }
        }
      ],
      "name": "Autentikasi"
    },
    {
      "item": [
        {
          "name": "Aktivitas user dan client",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Sesi admin client; cakupan platform hanya pemilik aplikasi.40event terbaru tanpa credential/IP. UI bukan otorisasi.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "activity"
              ],
              "query": [
                {
                  "disabled": false,
                  "key": "scope",
                  "value": "client"
                }
              ],
              "raw": "{{base_url}}/auth/activity"
            }
          }
        },
        {
          "name": "Pulihkan sesi console setelah reload",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Khusus console same-origin. Memerlukan cookie pantau_mala_session HttpOnly pada path ini; token sesi yang masih valid dikembalikan untuk Authorization. Tidak memperpanjang masa berlaku; API key tidak dipulihkan.",
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "browser-session"
              ],
              "raw": "{{base_url}}/auth/browser-session"
            }
          }
        },
        {
          "name": "Cabut dan hapus sesi browser",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "DELETE",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "browser-session"
              ],
              "raw": "{{base_url}}/auth/browser-session"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            },
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) { const body = pm.response.json(); ['api_key','mcp_api_key','key_id'].forEach(key =\u003e pm.environment.unset(key)); pm.environment.set('session_token', body.token); pm.environment.set('client_id', body.principal.client_id); } else if (pm.response.code === 401) { pm.environment.unset('session_token'); }"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Beralih workspace client",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"client\": \"{{target_client_slug}}\"\n}"
            },
            "description": "Sesi akun saja. Client harus merupakan membership enabled dari user yang sudah login; server menentukan ulang role dan hak platform dari database. Rotasi sesi dan perubahan client dilakukan atomik; token sebelumnya langsung ditolak. Respons token/principal baru berlaku 1 jam. API key, client nonaktif/tidak dimiliki, input role dan token lama ditolak. Bersihkan state/cache client lama dan gunakan bearer baru. Platform owner dapat memasuki client enabled hasil registration secara eksplisit; membership operasional dibuat/diaudit atomik bersama verifikasi/rotasi sesi. Admin client biasa tetap membutuhkan membership yang diizinkan.",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "client-session"
              ],
              "raw": "{{base_url}}/auth/client-session"
            }
          }
        },
        {
          "name": "Daftar client pada client",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "clients"
              ],
              "raw": "{{base_url}}/auth/clients"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Buat client beserta schema bisnis terisolasi (admin platform)",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"name\": \"{{new_client_name}}\",\n  \"slug\": \"{{new_client_slug}}\"\n}"
            },
            "description": "",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "clients"
              ],
              "raw": "{{base_url}}/auth/clients"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Aktifkan/nonaktifkan clients",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"enabled\": false\n}"
            },
            "description": "",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "PATCH",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "clients",
                "{{target_client_id}}"
              ],
              "raw": "{{base_url}}/auth/clients/{{target_client_id}}"
            }
          }
        },
        {
          "name": "Daftar job pemilik",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Sesi pemilik aplikasi saja. Target client berasal dari konfigurasi server; request tidak dapat memilih client/command. Trigger menyimpan antrean durable; job ganda mendapat409 job_busy. Worker memakai advisory lock bersama timer.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "jobs"
              ],
              "raw": "{{base_url}}/auth/jobs"
            }
          }
        },
        {
          "name": "Antrekan ingestion dataset publik",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Sesi pemilik aplikasi saja. Target client berasal dari konfigurasi server; request tidak dapat memilih client/command. Trigger menyimpan antrean durable; job ganda mendapat409 job_busy. Worker memakai advisory lock bersama timer.",
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "jobs",
                "ingestion",
                "run"
              ],
              "raw": "{{base_url}}/auth/jobs/ingestion/run"
            }
          }
        },
        {
          "name": "Daftar API key pada client",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "keys"
              ],
              "raw": "{{base_url}}/auth/keys"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            },
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 201) { const body = pm.response.json(); pm.environment.set(body.data.scope === 'mcp:read' ? 'mcp_api_key' : 'api_key', body.token); pm.environment.set('key_id', body.data.id); }"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Buat API key; secret lengkap dikembalikan sekali",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"expires_in_days\": 30,\n  \"name\": \"{{key_name}}\",\n  \"scope\": \"{{key_scope}}\"\n}"
            },
            "description": "",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "keys"
              ],
              "raw": "{{base_url}}/auth/keys"
            }
          }
        },
        {
          "name": "Cabut key hanya pada client saat ini",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "DELETE",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "keys",
                "{{key_id}}"
              ],
              "raw": "{{base_url}}/auth/keys/{{key_id}}"
            }
          }
        },
        {
          "name": "Riwayat job dan inspeksi titik client",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Sesi admin saja; role/RLS client aktif.12jobs,panggilan terbaru per source (maks12),50raw observations terbaru. Mapping spatial bukan validasi kebakaran. Scheduler belum terverifikasi; tidak mengarang next_run_at. Sampel adalah dataset client terbaru, tidak linked job_id atau snapshot per-job.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "operations"
              ],
              "raw": "{{base_url}}/auth/operations"
            }
          }
        },
        {
          "name": "Traffic API client aktif sejak proses mulai",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Sesi admin saja; client dari principal. Agregasi request terautentikasi pada proses API ini, termasuk error setelah identitas diketahui. Reset saat restart; tidak menggabungkan replica atau traffic assets Cloudflare. Tanpa IP, query, body, atau token; capacity_reached menunjukkan agregasi mungkin tidak lengkap.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "traffic"
              ],
              "raw": "{{base_url}}/auth/traffic"
            }
          }
        },
        {
          "name": "Daftar pengguna pada client",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "users"
              ],
              "raw": "{{base_url}}/auth/users"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Buat akun untuk client saat ini (admin client)",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"password\": \"{{new_password}}\",\n  \"role\": \"reader\",\n  \"username\": \"{{new_username}}\"\n}"
            },
            "description": "",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "users"
              ],
              "raw": "{{base_url}}/auth/users"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "const payload = JSON.parse(pm.request.body.raw); Object.keys(payload).forEach(key =\u003e { if (typeof payload[key] === 'string') { const match = payload[key].match(/^\\{\\{([^}]+)\\}\\}$/); if (match) payload[key] = pm.environment.get(match[1]) || ''; } }); pm.request.body.update(JSON.stringify(payload));"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Aktifkan/nonaktifkan users",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"enabled\": false\n}"
            },
            "description": "",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "PATCH",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "auth",
                "users",
                "{{user_id}}"
              ],
              "raw": "{{base_url}}/auth/users/{{user_id}}"
            }
          }
        }
      ],
      "name": "Management"
    },
    {
      "item": [
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Status sumber data",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "data-status"
              ],
              "raw": "{{base_url}}/api/v1/data-status"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Gempa BMKG",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Feed BMKG terbaru,15 gempa dirasakan atau15 gempa M5+. Bukan arsip lengkap, tanpa persistence/history. Wajib tampilkan atribusi BMKG; koordinat pusat [lon,lat].",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "earthquakes"
              ],
              "query": [
                {
                  "disabled": false,
                  "key": "kind",
                  "value": "latest"
                }
              ],
              "raw": "{{base_url}}/api/v1/earthquakes"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Titik panas dengan filter area dan waktu",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotspots"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "bbox",
                  "value": ""
                },
                {
                  "disabled": true,
                  "key": "since",
                  "value": ""
                },
                {
                  "disabled": false,
                  "key": "limit",
                  "value": "500"
                },
                {
                  "disabled": false,
                  "key": "offset",
                  "value": "0"
                }
              ],
              "raw": "{{base_url}}/api/v1/hotspots"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Titik panas dalam format GeoJSON",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "hotspots.geojson"
              ],
              "raw": "{{base_url}}/api/v1/hotspots.geojson"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Titik panas di sekitar koordinat",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Opsional; bukan alur geolokasi MVP. Hitung radius 25km di browser dari GeoJSON tanpa mengirim koordinat. Tidak dicache atau dilog; reverse proxy juga harus menonaktifkan query logging.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "nearby"
              ],
              "query": [
                {
                  "disabled": false,
                  "key": "lat",
                  "value": ""
                },
                {
                  "disabled": false,
                  "key": "lon",
                  "value": ""
                },
                {
                  "disabled": false,
                  "key": "radius_km",
                  "value": "25"
                }
              ],
              "raw": "{{base_url}}/api/v1/nearby"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Daftar provinsi",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "provinces"
              ],
              "raw": "{{base_url}}/api/v1/provinces"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Katalog seluruh wilayah impor beserta bbox provinsi",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Semua wilayah milik client, termasuk wilayah tanpa deteksi. Bbox dan center provinsi berasal dari extent polygon impor; bukan klaim batas nasional lengkap.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "region-catalog"
              ],
              "raw": "{{base_url}}/api/v1/region-catalog"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Daftar wilayah dan filter risiko",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "regions"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "province",
                  "value": ""
                },
                {
                  "disabled": true,
                  "key": "categories",
                  "value": ""
                },
                {
                  "disabled": true,
                  "key": "search",
                  "value": ""
                },
                {
                  "disabled": false,
                  "key": "limit",
                  "value": "100"
                },
                {
                  "disabled": false,
                  "key": "offset",
                  "value": "0"
                }
              ],
              "raw": "{{base_url}}/api/v1/regions"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Detail wilayah",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "regions",
                "{{code}}"
              ],
              "raw": "{{base_url}}/api/v1/regions/{{code}}"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Riwayat wilayah",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "regions",
                "{{code}}",
                "history"
              ],
              "raw": "{{base_url}}/api/v1/regions/{{code}}/history"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Debit sungai model per wilayah",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Menggunakan centroid wilayah milik client authenticated;404 jika belum diimpor. Grid model GloFAS5km dapat berbeda dari centroid/sungai yang diinginkan. Tanggal UTC, nullable berarti model tidak tersedia. Bukan laporan atau peringatan banjir. Wajib atribusi Open-Meteo/GloFAS/Copernicus. Tidak dipersist.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "regions",
                "{{code}}",
                "river-discharge"
              ],
              "raw": "{{base_url}}/api/v1/regions/{{code}}/river-discharge"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Ringkasan nasional",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "summary"
              ],
              "raw": "{{base_url}}/api/v1/summary"
            }
          }
        },
        {
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "exec": [
                  "pm.variables.set('data_token', pm.environment.get('api_key') || pm.environment.get('session_token') || '');"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "name": "Prakiraan cuaca desa BMKG",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{data_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "Kode resmi desa adm4 wajib, berbeda dari kode kab/kota. Prakiraan per3jam/3hari, bukan observasi untuk skor. Wajib atribusi BMKG. Tidak dipersist.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "weather",
                "bmkg"
              ],
              "query": [
                {
                  "disabled": false,
                  "key": "adm4",
                  "value": "{{adm4}}"
                }
              ],
              "raw": "{{base_url}}/api/v1/weather/bmkg"
            }
          }
        }
      ],
      "name": "Data"
    },
    {
      "item": [
        {
          "name": "Health live",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "description": "Endpoint operasi internal tanpa bearer pada aplikasi. Reverse proxy produksi memblokir akses dari domain publik; gunakan SSH tunnel atau jaringan internal.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "health",
                "live"
              ],
              "raw": "{{base_url}}/health/live"
            }
          }
        },
        {
          "name": "Health ready",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "description": "Endpoint operasi internal tanpa bearer pada aplikasi. Reverse proxy produksi memblokir akses dari domain publik; gunakan SSH tunnel atau jaringan internal.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "health",
                "ready"
              ],
              "raw": "{{base_url}}/health/ready"
            }
          }
        },
        {
          "name": "Prometheus metrics",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "description": "Endpoint operasi internal tanpa bearer pada aplikasi. Reverse proxy produksi memblokir akses dari domain publik; gunakan SSH tunnel atau jaringan internal.",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "metrics"
              ],
              "raw": "{{base_url}}/metrics"
            }
          }
        },
        {
          "name": "Kontrak OpenAPI",
          "request": {
            "auth": {
              "bearer": [
                {
                  "key": "token",
                  "type": "string",
                  "value": "{{session_token}}"
                }
              ],
              "type": "bearer"
            },
            "description": "",
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "openapi.yaml"
              ],
              "raw": "{{base_url}}/openapi.yaml"
            }
          }
        }
      ],
      "name": "Operasional"
    }
  ]
}