{
  "openapi": "3.1.0",
  "info": {
    "title": "SIMKATMAWA API",
    "version": "1.0.0",
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-Proprietary"
    },
    "description": "API publik SIMKATMAWA (`/api/v1/*`) buat sistem eksternal (Neosiak, partner lain). Auth: Bearer token dari `ApiClient`, bikin/kelola lewat halaman Developer > API Client di dashboard, atau `php artisan api:client:create \"Nama Partner\" --ability=...`.\n\nTiap endpoint butuh ability spesifik (token yang valid & aktif tapi gak punya ability yang sesuai tetap ditolak 403):\n- `prestasi:read` untuk endpoint Prestasi Mahasiswa\n- `akses-sertifikat:read` untuk cek status sertifikat\n- `akses-sertifikat:download` untuk unduh PDF sertifikat\n\nToken yang dibuat sebelum sistem ability ini ada otomatis diberi `prestasi:read` lewat migrasi.\n\nSpec detail modul Akses Sertifikat SKPI (buat Neosiak): `docs/spec/neosiak-download-sertifikat-api.md`. Postman collection: `docs/prestasi-api.postman_collection.json`."
  },
  "servers": [
    {
      "url": "https://simkatmawa.univpancasila.ac.id",
      "description": "Produksi"
    },
    {
      "url": "http://localhost:8000",
      "description": "Lokal/dev"
    }
  ],
  "security": [
    { "bearerAuth": [] }
  ],
  "tags": [
    {
      "name": "Prestasi Mahasiswa",
      "description": "Bridge data prestasi mahasiswa gabungan (prestasi_mandiri, kegiatan_mhs_berprestasi, skpi_kegiatan_mhs). Butuh ability `prestasi:read`."
    },
    {
      "name": "Akses Sertifikat SKPI (Neosiak)",
      "description": "Cek status & unduh sertifikat SKPI mahasiswa."
    }
  ],
  "paths": {
    "/api/v1/prestasi": {
      "get": {
        "tags": ["Prestasi Mahasiswa"],
        "operationId": "listPrestasi",
        "summary": "List Prestasi",
        "description": "List gabungan prestasi mahasiswa, paginated (50/halaman), diurutin `synced_at` terbaru.",
        "security": [{ "bearerAuth": ["prestasi:read"] }],
        "parameters": [
          {
            "name": "nim",
            "in": "query",
            "description": "Filter substring NIM (opsional)",
            "required": false,
            "schema": { "type": "string" }
          },
          {
            "name": "source_type",
            "in": "query",
            "description": "Filter sumber data (opsional)",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["prestasi_mandiri", "kegiatan_berprestasi", "kegiatan_fakultas"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Daftar prestasi, format paginator Laravel standar",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PrestasiListResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/v1/prestasi/mahasiswa/{nim}": {
      "get": {
        "tags": ["Prestasi Mahasiswa"],
        "operationId": "getPrestasiByNim",
        "summary": "Detail Prestasi per NIM",
        "description": "Semua prestasi (dari 3 sumber) milik satu NIM.",
        "security": [{ "bearerAuth": ["prestasi:read"] }],
        "parameters": [
          { "$ref": "#/components/parameters/NimPath" }
        ],
        "responses": {
          "200": {
            "description": "Daftar prestasi milik NIM tersebut",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PrestasiDetailResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/v1/akses-sertifikat/mahasiswa/{nim}": {
      "get": {
        "tags": ["Akses Sertifikat SKPI (Neosiak)"],
        "operationId": "getAksesSertifikatStatus",
        "summary": "Cek Status Sertifikat",
        "description": "Cek apakah sertifikat SKPI mahasiswa sudah dikasih akses, tanpa generate PDF -- cepat, cocok buat nampilin/nyembunyiin tombol unduh. Selalu balas `200` untuk NIM berformat valid: 'belum dikasih akses' itu jawaban normal buat status-check, bukan error, dan sengaja dikasih bentuk respons yang sama baik NIM-nya gak valid maupun valid-tapi-belum-disetujui (gak bocorin informasi mana yang mana).",
        "security": [{ "bearerAuth": ["akses-sertifikat:read"] }],
        "parameters": [
          { "$ref": "#/components/parameters/NimPath" }
        ],
        "responses": {
          "200": {
            "description": "Status akses sertifikat (selalu 200 untuk NIM berformat valid)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AksesSertifikatStatusResponse" },
                "examples": {
                  "diberiAkses": {
                    "summary": "Sudah dikasih akses",
                    "value": {
                      "nim": "51422001",
                      "diberi_akses": true,
                      "akses": {
                        "nomor_dokumen": "001/D.FT/UP/VIII/2026",
                        "tahun_akademik": "20241",
                        "diberikan_oleh": "staff1",
                        "diberikan_pada": "2026-08-18T03:43:00+00:00"
                      }
                    }
                  },
                  "belumDiberiAkses": {
                    "summary": "Belum dikasih akses / NIM tidak ada",
                    "value": {
                      "nim": "99999999",
                      "diberi_akses": false,
                      "akses": null
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/InvalidNim" }
        }
      }
    },
    "/api/v1/akses-sertifikat/mahasiswa/{nim}/download": {
      "get": {
        "tags": ["Akses Sertifikat SKPI (Neosiak)"],
        "operationId": "downloadAksesSertifikat",
        "summary": "Unduh Sertifikat (PDF)",
        "description": "Generate & unduh file PDF sertifikat SKPI -- cuma jalan kalau sertifikat sudah dikasih akses (cek gating sama kayak endpoint status). Beda dari endpoint status: di sini 'belum dikasih akses' tetap dibalas 404 (bukan 200), karena filenya emang belum ada -- konvensi wajar buat endpoint yang minta file. Rate limit lebih ketat daripada endpoint status karena tiap request memicu render PDF lewat Browsershot.",
        "security": [{ "bearerAuth": ["akses-sertifikat:download"] }],
        "parameters": [
          { "$ref": "#/components/parameters/NimPath" }
        ],
        "responses": {
          "200": {
            "description": "File PDF sertifikat SKPI",
            "headers": {
              "Content-Disposition": {
                "description": "attachment; filename=SKPI_{nim}_{timestamp}.pdf",
                "schema": { "type": "string" }
              }
            },
            "content": {
              "application/pdf": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": {
            "description": "Sertifikat belum dikasih akses, atau NIM tidak ada (dua kasus ini sengaja dikasih pesan yang sama)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorMessage" },
                "example": { "message": "Sertifikat tidak ditemukan" }
              }
            }
          },
          "422": { "$ref": "#/components/responses/InvalidNim" },
          "500": {
            "description": "Gagal generate PDF (mis. Browsershot/Node gagal dieksekusi di server)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorMessage" },
                "example": { "message": "Gagal membuat sertifikat, coba lagi nanti" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Token dari `ApiClient` (di-hash SHA-256 saat disimpan). Tiap token perlu ability spesifik per endpoint -- lihat description masing-masing operasi."
      }
    },
    "parameters": {
      "NimPath": {
        "name": "nim",
        "in": "path",
        "required": true,
        "description": "NIM mahasiswa",
        "schema": { "type": "string", "pattern": "^[A-Za-z0-9]{1,20}$" },
        "example": "51422001"
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Token tidak dikirim, formatnya bukan Bearer, atau token invalid/nonaktif",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorMessage" },
            "example": { "message": "Unauthenticated." }
          }
        }
      },
      "Forbidden": {
        "description": "Token valid & aktif, tapi tidak punya ability yang dibutuhkan endpoint ini",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorMessage" },
            "example": { "message": "Token tidak memiliki izin untuk endpoint ini" }
          }
        }
      },
      "InvalidNim": {
        "description": "Format NIM di path tidak valid",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorMessage" },
            "example": { "message": "Format NIM tidak valid" }
          }
        }
      }
    },
    "schemas": {
      "ErrorMessage": {
        "type": "object",
        "properties": {
          "message": { "type": "string" }
        },
        "required": ["message"]
      },
      "PrestasiItem": {
        "type": "object",
        "properties": {
          "nim": { "type": "string", "example": "51422001" },
          "nama": { "type": "string", "example": "Lomba Basket Antar Fakultas" },
          "sumber": {
            "type": "string",
            "enum": ["prestasi_mandiri", "kegiatan_berprestasi", "kegiatan_fakultas"],
            "example": "kegiatan_fakultas"
          },
          "tingkat": { "type": ["string", "null"], "example": "Nasional" },
          "peringkat": { "type": ["string", "null"], "example": "Juara 1" },
          "tanggal": { "type": ["string", "null"], "format": "date", "example": "2025-05-01" },
          "validasi_kemahasiswaan_universitas": { "type": ["string", "null"], "example": "1" },
          "validasikaprodi": { "type": ["string", "null"], "example": "1" },
          "validasi_kemahasiswaan_fakultas": { "type": ["string", "null"], "example": "1" },
          "synced_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "PrestasiListResponse": {
        "type": "object",
        "description": "Paginator standar Laravel",
        "properties": {
          "current_page": { "type": "integer", "example": 1 },
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/PrestasiItem" }
          },
          "first_page_url": { "type": "string" },
          "from": { "type": ["integer", "null"] },
          "last_page": { "type": "integer" },
          "per_page": { "type": "integer", "example": 50 },
          "total": { "type": "integer" }
        }
      },
      "PrestasiDetailResponse": {
        "type": "object",
        "properties": {
          "nim": { "type": "string", "example": "51422001" },
          "prestasi": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/PrestasiItem" }
          }
        }
      },
      "AksesSertifikatDetail": {
        "type": ["object", "null"],
        "properties": {
          "nomor_dokumen": { "type": "string", "example": "001/D.FT/UP/VIII/2026" },
          "tahun_akademik": { "type": "string", "example": "20241" },
          "diberikan_oleh": { "type": "string", "example": "staff1" },
          "diberikan_pada": { "type": "string", "format": "date-time" }
        }
      },
      "AksesSertifikatStatusResponse": {
        "type": "object",
        "properties": {
          "nim": { "type": "string", "example": "51422001" },
          "diberi_akses": { "type": "boolean" },
          "akses": { "$ref": "#/components/schemas/AksesSertifikatDetail" }
        },
        "required": ["nim", "diberi_akses", "akses"]
      }
    }
  }
}
