{
  "openapi": "3.0.3",
  "info": {
    "title": "GGapi Public Product API",
    "description": "Public product endpoints proxied through the GGapi gateway. Authenticate with header `x-ggapi-co-id`.\n\nHuman docs: https://ggapi.co.id/docs\nDownload this spec: https://docs.ggapi.co.id/openapi.json",
    "version": "1.0.0",
    "contact": {
      "name": "GGapi.co.id",
      "url": "https://ggapi.co.id"
    }
  },
  "servers": [
    {
      "url": "https://use.ggapi.co.id",
      "description": "Production gateway"
    }
  ],
  "tags": [
    {
      "name": "Exchange Rate",
      "description": "Morningstar (updated every 5 minutes, timestamps in WIB / GMT+7)"
    },
    {
      "name": "Indonesia Bank Exchange Rate",
      "description": "Kemenkeu tax kurs (IDR)"
    },
    {
      "name": "Geolocation",
      "description": "IP geolocation & reverse geocoding"
    },
    {
      "name": "Indonesia Regional",
      "description": "BPS wilayah (province â†’ village) + postal codes"
    },
    {
      "name": "Indonesia University",
      "description": "PDDikti (Kemdiktisaintek) university data — search/filter + detail"
    },
    {
      "name": "Indonesia School",
      "description": "Sekolah Kita (Kemendikdasmen) school data — search/filter + detail"
    },
    {
      "name": "Bea Cukai",
      "description": "Barang kiriman AWB tracking"
    },
    {
      "name": "INSW NIB",
      "description": "Validasi Nomor Induk Berusaha (NIB) via INSW"
    },
    {
      "name": "Bank Verification",
      "description": "Validasi nama pemilik rekening bank Indonesia (kode bank BI — daftar di endpoint /banks)"
    },
    {
      "name": "E-Wallet Verification",
      "description": "Validasi nama pemilik e-wallet Indonesia: DANA, OVO, GoPay, ShopeePay, LinkAja, Maxim"
    },
    {
      "name": "Prayer Time",
      "description": "Indonesia prayer schedule (MyQuran/Kemenag)"
    },
    {
      "name": "Hadis",
      "description": "Hadith encyclopedia (MyQuran)"
    },
    {
      "name": "OCR",
      "description": "KTP/SIM image OCR via multipart upload (`file`). Auth: header `x-ggapi-co-id`. Do not set Content-Type manually."
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-ggapi-co-id",
        "description": "Your GGapi API key from https://ggapi.co.id/credentials"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "WilayahPaging": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "size": {
            "type": "integer",
            "example": 100
          },
          "total_item": {
            "type": "integer"
          },
          "total_page": {
            "type": "integer"
          }
        }
      },
      "WilayahListResponse": {
        "type": "object",
        "properties": {
          "is_success": {
            "type": "boolean"
          },
          "message": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "paging": {
            "$ref": "#/components/schemas/WilayahPaging"
          }
        }
      },
      "OcrKtpFields": {
        "type": "object",
        "properties": {
          "nik": {
            "type": [
              "string",
              "null"
            ],
            "example": "3171012345670001"
          },
          "nama": {
            "type": [
              "string",
              "null"
            ],
            "example": "BUDI SANTOSO"
          },
          "tempat_lahir": {
            "type": [
              "string",
              "null"
            ],
            "example": "JAKARTA"
          },
          "tanggal_lahir": {
            "type": [
              "string",
              "null"
            ],
            "example": "12-08-1996"
          },
          "jenis_kelamin": {
            "type": [
              "string",
              "null"
            ],
            "example": "LAKI-LAKI"
          },
          "golongan_darah": {
            "type": [
              "string",
              "null"
            ]
          },
          "alamat": {
            "type": [
              "string",
              "null"
            ]
          },
          "rt_rw": {
            "type": [
              "string",
              "null"
            ],
            "example": "002/003"
          },
          "kelurahan_desa": {
            "type": [
              "string",
              "null"
            ]
          },
          "kecamatan": {
            "type": [
              "string",
              "null"
            ]
          },
          "agama": {
            "type": [
              "string",
              "null"
            ],
            "example": "ISLAM"
          },
          "status_perkawinan": {
            "type": [
              "string",
              "null"
            ]
          },
          "pekerjaan": {
            "type": [
              "string",
              "null"
            ]
          },
          "kewarganegaraan": {
            "type": [
              "string",
              "null"
            ],
            "example": "WNI"
          },
          "berlaku_hingga": {
            "type": [
              "string",
              "null"
            ],
            "example": "SEUMUR HIDUP"
          }
        }
      },
      "OcrKtpResponse": {
        "type": "object",
        "required": [
          "filename",
          "size",
          "fields",
          "raw_text"
        ],
        "properties": {
          "filename": {
            "type": "string",
            "example": "ktp.jpeg"
          },
          "size": {
            "type": "integer",
            "example": 202482
          },
          "fields": {
            "$ref": "#/components/schemas/OcrKtpFields"
          },
          "raw_text": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Raw OCR lines before field parsing"
          }
        }
      },
      "OcrSimFields": {
        "type": "object",
        "properties": {
          "nomor_sim": {
            "type": [
              "string",
              "null"
            ]
          },
          "golongan": {
            "type": [
              "string",
              "null"
            ]
          },
          "nama": {
            "type": [
              "string",
              "null"
            ]
          },
          "tempat_lahir": {
            "type": [
              "string",
              "null"
            ]
          },
          "tanggal_lahir": {
            "type": [
              "string",
              "null"
            ]
          },
          "alamat": {
            "type": [
              "string",
              "null"
            ]
          },
          "pekerjaan": {
            "type": [
              "string",
              "null"
            ]
          },
          "berlaku_hingga": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OcrSimResponse": {
        "type": "object",
        "required": [
          "filename",
          "size",
          "fields",
          "raw_text"
        ],
        "properties": {
          "filename": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "fields": {
            "$ref": "#/components/schemas/OcrSimFields"
          },
          "raw_text": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Insufficient balance points",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "JsonOk": {
        "description": "JSON success payload",
        "content": {
          "application/json": {
            "schema": {
              "type": "object"
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/api/exchange-rate/latest": {
      "get": {
        "tags": [
          "Exchange Rate"
        ],
        "summary": "Latest Morningstar exchange rates",
        "description": "Returns FX rates scraped from Morningstar currencies (â‰ˆ22 major pairs). Snapshot is USD-based; use `base` to re-base and `symbols` to filter. `updated_at` is WIB (GMT+7).",
        "parameters": [
          {
            "name": "base",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "USD"
            },
            "description": "Base currency code (must exist in the Morningstar pair set)"
          },
          {
            "name": "symbols",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "IDR,EUR"
            },
            "description": "Comma-separated currency filter"
          }
        ],
        "responses": {
          "200": {
            "description": "Latest rates snapshot",
            "content": {
              "application/json": {
                "example": {
                  "base": "USD",
                  "rates": {
                    "IDR": 17999,
                    "EUR": 0.872
                  },
                  "updated_at": "2026-07-16T12:02:37+07:00",
                  "source_unix": 1784178157,
                  "source": "morningstar"
                }
              }
            }
          },
          "400": {
            "description": "Unknown base currency",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "Rates not available yet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-bank-exchange-rate/latest": {
      "get": {
        "tags": [
          "Indonesia Bank Exchange Rate"
        ],
        "summary": "Latest Kemenkeu (KMK) tax exchange rates vs IDR",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "USD,JPY"
            },
            "description": "Comma-separated currency filter"
          }
        ],
        "responses": {
          "200": {
            "description": "Kemenkeu kurs snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "Upstream snapshot not ready",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/ip-geolocation": {
      "get": {
        "tags": [
          "Geolocation"
        ],
        "summary": "IP geolocation lookup",
        "description": "Looks up an IPv4/IPv6 address. Returns country, region, city, coordinates, timezone, ISP, proxy/hosting flags, and more.",
        "parameters": [
          {
            "name": "ip",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "182.8.67.230"
            },
            "description": "IPv4 or IPv6 address to look up"
          }
        ],
        "responses": {
          "200": {
            "description": "Location data for IP",
            "content": {
              "application/json": {
                "example": {
                  "ip": "182.8.67.230",
                  "status": "success",
                  "continent": "Asia",
                  "continent_code": "AS",
                  "country": "Indonesia",
                  "country_code": "ID",
                  "region": "JI",
                  "region_name": "East Java",
                  "city": "Surabaya",
                  "district": "",
                  "zip": "60163",
                  "lat": -7.2352,
                  "lon": 112.7387,
                  "timezone": "Asia/Jakarta",
                  "offset": 25200,
                  "currency": "IDR",
                  "isp": "PT Telekomunikasi Selular Indonesia",
                  "org": "",
                  "as": "AS23693 PT. Telekomunikasi Selular",
                  "asname": "TELKOMSEL-ASN-ID",
                  "mobile": false,
                  "proxy": false,
                  "hosting": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid IP",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "Upstream geolocation unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/reverse-geocoding": {
      "get": {
        "tags": [
          "Geolocation"
        ],
        "summary": "Reverse geocoding (lat/lon â†’ address)",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "example": -6.2
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "example": 106.8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Structured address",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid coordinates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/beacukai-barang-kiriman/track": {
      "get": {
        "tags": [
          "Bea Cukai"
        ],
        "summary": "Track Bea Cukai barang kiriman by AWB",
        "parameters": [
          {
            "name": "awb",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "test"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tracking payload (found or not found)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing awb",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/insw-nib/validate": {
      "get": {
        "tags": [
          "INSW NIB"
        ],
        "summary": "Validate NIB via INSW",
        "parameters": [
          {
            "name": "nib",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0257000901438"
            }
          },
          {
            "name": "npwp",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "958180507626000"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "NIB found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing nib",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "description": "NIB not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "INSW auth token unavailable"
          }
        }
      }
    },
    "/api/bank-verification/validate": {
      "get": {
        "tags": [
          "Bank Verification"
        ],
        "summary": "Validate bank account holder name",
        "description": "Cek nama pemilik rekening bank secara real-time via kode Bank Indonesia (cth `014` = BCA, `002` = BRI). Untuk e-wallet gunakan /api/ewallet-verification/validate. Lookup selesai dengan `found: false` (tidak valid / timeout yang dipetakan ke tidak valid) dikenakan biaya Rp25 (25 points) untuk semua plan, termasuk Premium — akun wajib punya saldo minimal; kekurangan saldo → 402. Lookup `found: true` mengikuti PAYG produk (Standard setelah kuota gratis; Premium tidak dipotong). Parameter salah (400) / rate limit (429) / 202 `{found: false, processing: true}` tidak di-charge. Hit ulang data yang sama selama pengecekan masih berjalan (maks 15 detik) dijawab 202 dan TIDAK di-charge.",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": true,
            "description": "Kode bank numerik standar Bank Indonesia (cth `014`). Lihat /api/bank-verification/codes.",
            "schema": {
              "type": "string",
              "example": "014"
            }
          },
          {
            "name": "account_number",
            "in": "query",
            "required": true,
            "description": "Nomor rekening bank.",
            "schema": {
              "type": "string",
              "example": "5125372349"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Hasil lookup. `found: true` = ditemukan (PAYG produk jika berlaku); `found: false` + `message: \"Account bank tidak valid\"` = tidak terdaftar, dikenakan Rp25.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "found": {
                    "summary": "Rekening ditemukan",
                    "value": {
                      "found": true,
                      "bank_code": "014",
                      "account_number": "5125372349",
                      "account_name": "JETWIN",
                      "account_type": "bank_account",
                      "message": "ok",
                      "updated_at": "2026-08-22T23:59:00+07:00"
                    }
                  },
                  "notFound": {
                    "summary": "Rekening tidak valid / tidak terdaftar",
                    "value": {
                      "found": false,
                      "bank_code": "014",
                      "account_number": "9999999999",
                      "account_name": null,
                      "account_type": "bank_account",
                      "message": "Account bank tidak valid",
                      "updated_at": "2026-08-22T23:59:00+07:00"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Pengecekan data yang sama sedang diproses (single-flight). Respons ini TIDAK di-charge.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "found": false,
                  "processing": true,
                  "bank_code": "014",
                  "account_number": "5125372349",
                  "message": "Pengecekan rekening bank yang sama sedang diproses — mohon tunggu hingga selesai (maks 15 detik) sebelum mencoba lagi."
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "Upstream unavailable"
          },
          "503": {
            "description": "Upstream not configured / auth problem"
          }
        }
      }
    },
    "/api/bank-verification/codes": {
      "get": {
        "tags": [
          "Bank Verification"
        ],
        "summary": "Bank code directory (free)",
        "description": "Daftar 110+ kode bank numerik (standar Bank Indonesia) yang didukung. Gunakan nilai `code` untuk /api/bank-verification/validate. Untuk e-wallet lihat /api/ewallet-verification/codes. Endpoint ini gratis (tidak di-charge).",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Bank code directory",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "count": 2,
                  "banks": [
                    {
                      "code": "008",
                      "name": "BANK MANDIRI",
                      "type": "bank"
                    },
                    {
                      "code": "014",
                      "name": "BANK BCA",
                      "type": "bank"
                    }
                  ],
                  "updated_at": "2026-08-22T23:59:00+07:00"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/ewallet-verification/validate": {
      "get": {
        "tags": [
          "E-Wallet Verification"
        ],
        "summary": "Validate e-wallet account holder name",
        "description": "Cek nama pemilik e-wallet secara real-time (DANA, OVO, GoPay, GoPay Driver, ShopeePay, LinkAja, Maxim) via nomor HP. Untuk rekening bank gunakan /api/bank-verification/validate. Lookup selesai dengan `found: false` (tidak valid / timeout yang dipetakan ke tidak valid) dikenakan biaya Rp25 (25 points) untuk semua plan, termasuk Premium — akun wajib punya saldo minimal; kekurangan saldo → 402. Lookup `found: true` mengikuti PAYG produk (Standard setelah kuota gratis; Premium tidak dipotong). Parameter salah (400) / rate limit (429) / 202 `{found: false, processing: true}` tidak di-charge. Hit ulang data yang sama selama pengecekan masih berjalan (maks 15 detik) dijawab 202 dan TIDAK di-charge.",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": true,
            "description": "Kode e-wallet: DANA, GOPAY, GOPAYDRIVER, LINKAJA, MAXIM, OVO, SHOPEEPAY (case-insensitive).",
            "schema": {
              "type": "string",
              "example": "GOPAY"
            }
          },
          {
            "name": "account_number",
            "in": "query",
            "required": true,
            "description": "Nomor HP terdaftar e-wallet.",
            "schema": {
              "type": "string",
              "example": "085747890120"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Hasil lookup. `found: true` = ditemukan (PAYG produk jika berlaku); `found: false` + `message: \"E-wallet account tidak valid\"` = tidak terdaftar, dikenakan Rp25.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "found": {
                    "summary": "E-wallet ditemukan",
                    "value": {
                      "found": true,
                      "bank_code": "GOPAY",
                      "account_number": "085747890120",
                      "account_name": "AFRIZAL SATRIO PUTRA",
                      "account_type": "wallet_account",
                      "message": "ok",
                      "updated_at": "2026-08-22T23:59:00+07:00"
                    }
                  },
                  "notFound": {
                    "summary": "E-wallet tidak valid / tidak terdaftar",
                    "value": {
                      "found": false,
                      "bank_code": "DANA",
                      "account_number": "087800001234",
                      "account_name": null,
                      "account_type": "wallet_account",
                      "message": "E-wallet account tidak valid",
                      "updated_at": "2026-08-22T23:59:00+07:00"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Pengecekan data yang sama sedang diproses (single-flight). Respons ini TIDAK di-charge.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "found": false,
                  "processing": true,
                  "bank_code": "GOPAY",
                  "account_number": "085747890120",
                  "message": "Pengecekan e-wallet yang sama sedang diproses — mohon tunggu hingga selesai (maks 15 detik) sebelum mencoba lagi."
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters / unsupported e-wallet code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "Upstream unavailable"
          },
          "503": {
            "description": "Upstream not configured / auth problem"
          }
        }
      }
    },
    "/api/ewallet-verification/codes": {
      "get": {
        "tags": [
          "E-Wallet Verification"
        ],
        "summary": "E-wallet code directory (free)",
        "description": "Daftar kode e-wallet yang didukung (DANA, OVO, GoPay, dll). Gunakan nilai `code` untuk /api/ewallet-verification/validate. Endpoint ini gratis (tidak di-charge).",
        "parameters": [],
        "responses": {
          "200": {
            "description": "E-wallet code directory",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "count": 2,
                  "banks": [
                    {
                      "code": "DANA",
                      "name": "DANA",
                      "type": "ewallet"
                    },
                    {
                      "code": "GOPAY",
                      "name": "GOPAY",
                      "type": "ewallet"
                    }
                  ],
                  "updated_at": "2026-08-22T23:59:00+07:00"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/indonesia-regional/provinces": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List provinces",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated provinces (100/page)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WilayahListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/provinces/{code}": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "Get province by code",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Province detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-regional/provinces/{code}/regencies": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List regencies in a province",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "32"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated regencies",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/regencies": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List regencies",
        "parameters": [
          {
            "name": "province_code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated regencies",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/regencies/{code}": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "Get regency by code",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Regency detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-regional/regencies/{code}/districts": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List districts in a regency",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated districts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/districts": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List districts",
        "parameters": [
          {
            "name": "regency_code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated districts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/districts/{code}": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "Get district by code",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "District detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-regional/districts/{code}/villages": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List villages in a district",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated villages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/villages": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List villages",
        "parameters": [
          {
            "name": "district_code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "postal_code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated villages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/villages/{code}": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "Get village by code",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Village detail (includes is_courier_support, postcode)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-regional/postal-codes": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "List postal codes",
        "parameters": [
          {
            "name": "village_code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "postal_code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated postal codes with hierarchy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/postal-codes/{postal_code}": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "Lookup villages by postal code",
        "parameters": [
          {
            "name": "postal_code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "12210"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching villages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-regional/search": {
      "get": {
        "tags": [
          "Indonesia Regional"
        ],
        "summary": "Cross-level search (name or postal code)",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "bandung"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "province",
                "regency",
                "district",
                "village",
                "postal_code"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-university": {
      "get": {
        "tags": [
          "Indonesia University"
        ],
        "summary": "Search & filter Indonesian universities",
        "description": "Pencarian & filter data perguruan tinggi Indonesia (sumber: PDDikti Kemdiktisaintek). Gabungkan filter sesuai kebutuhan; respons selalu berisi paginasi.",
        "parameters": [
          {
            "name": "nama",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "politeknik negeri malang"
            },
            "description": "Kata kunci nama perguruan tinggi (alias: `q`)"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Alias untuk `nama`"
          },
          {
            "name": "jenis",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "PTN",
                "PTS",
                "PTK",
                "PTA"
              ]
            },
            "description": "Jenis: PTN, PTS, PTK (Kedinasan), PTA (Agama)"
          },
          {
            "name": "akreditasi",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Unggul"
            },
            "description": "Akreditasi, bisa CSV — mis. `Unggul`, `A,B,C`, `Baik,Baik Sekali`. `null` = tidak terakreditasi."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "A",
                "B",
                "K",
                "N",
                "H"
              ]
            },
            "description": "Status: A (Aktif), B (Alih Bentuk), K (Alih Kelola), N (Pembinaan), H (Tutup)"
          },
          {
            "name": "provinsi",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Jawa Timur"
            },
            "description": "Nama provinsi (mis. `Jawa Timur`); awalan `Prov. ` ditambahkan otomatis"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 15
            },
            "description": "Jumlah item per halaman (1-100, default 15)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Nomor halaman (default 1)"
          }
        ],
        "responses": {
          "200": {
            "description": "Daftar perguruan tinggi + paginasi",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id_sp": "UUsfvzfI_Ju-aGHuh7tqq3LJmSiiqE4PAnJuDjjRIte1aqiC4mQ7eAJUDsJ0vsGkcnC_cw==",
                      "nama_pt": "Politeknik Negeri Malang",
                      "nama_singkat": "POLINEMA",
                      "jenis_pt": "Negeri",
                      "akreditasi": "Unggul",
                      "status_pt": "Aktif",
                      "provinsi_pt": "Prov. Jawa Timur",
                      "kab_kota_pt": "Kota Malang",
                      "jumlah_prodi": 69,
                      "range_biaya_kuliah": "500.000 - 15.375.000"
                    }
                  ],
                  "page": 1,
                  "limit": 15,
                  "total_items": 1,
                  "total_pages": 1
                }
              }
            }
          },
          "400": {
            "description": "Filter tidak valid (mis. jenis/status di luar daftar)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "Upstream PDDikti unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-university/{id_sp}": {
      "get": {
        "tags": [
          "Indonesia University"
        ],
        "summary": "Get university detail by id_sp",
        "description": "Detail satu perguruan tinggi: alamat, kontak, koordinat, tanggal berdiri, SK pendirian, status & akreditasi. Ambil `id_sp` dari endpoint list dan kirim apa adanya (termasuk `==` di akhir).",
        "parameters": [
          {
            "name": "id_sp",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "kzfGhT5xFd9B_rrPFOp1LYilkMmzdkXdWdC9sKkgOtu0atemgkdyJC1OTN8LpPgHGoZqhg=="
            },
            "description": "ID perguruan tinggi dari endpoint list (URL-safe base64)"
          }
        ],
        "responses": {
          "200": {
            "description": "Detail perguruan tinggi",
            "content": {
              "application/json": {
                "example": {
                  "nama_pt": "Politeknik Negeri Malang",
                  "nm_singkat": "POLINEMA",
                  "kelompok": "Perguruan Tinggi Negeri",
                  "pembina": "PTN",
                  "kode_pt": "005023",
                  "email": "cs@polinema.ac.id",
                  "no_tel": "0341404424",
                  "no_fax": "0341404420",
                  "website": "www.polinema.ac.id",
                  "alamat": "Jalan Soekarno Hatta No 9",
                  "kode_pos": "65141",
                  "provinsi_pt": "Prov. Jawa Timur",
                  "kab_kota_pt": "Kota Malang",
                  "kecamatan_pt": "Kec. Lowokwaru",
                  "lintang_pt": -7.946778,
                  "bujur_pt": 112.615987,
                  "tgl_berdiri_pt": "1979-01-27T00:00:00Z",
                  "sk_pendirian_sp": "03DJKEP1979",
                  "status_pt": "Aktif",
                  "akreditasi_pt": "Unggul"
                }
              }
            }
          },
          "400": {
            "description": "id_sp tidak valid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "description": "Perguruan tinggi tidak ditemukan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "Upstream PDDikti unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-school": {
      "get": {
        "tags": [
          "Indonesia School"
        ],
        "summary": "Search & filter Indonesian schools",
        "description": "Pencarian & filter data sekolah Indonesia (sumber: Sekolah Kita Kemendikdasmen). Filter nama, bentuk pendidikan, status, akreditasi, provinsi/kabupaten/kecamatan dengan paginasi.",
        "parameters": [
          {
            "name": "nama",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "sma negeri 3"
            },
            "description": "Kata kunci nama sekolah (alias: `q`)"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Alias untuk `nama`"
          },
          {
            "name": "bentuk",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "SMA"
            },
            "description": "Bentuk pendidikan: SD, SMP, SMA, SMK, TK, KB, SPS, RA, MI, MTs, PKBM, Kursus, dll."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "NEGERI",
                "SWASTA"
              ]
            },
            "description": "Status sekolah (case-insensitive)"
          },
          {
            "name": "akreditasi",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "A"
            },
            "description": "Akreditasi, bisa CSV — mis. `A,B,C`, `Tidak Terakreditasi`. `null` = belum terakreditasi."
          },
          {
            "name": "provinsi",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Jawa Barat"
            },
            "description": "Nama provinsi (mis. `Jawa Barat`); awalan `Prov. ` ditambahkan otomatis"
          },
          {
            "name": "kabupaten",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Kota Bogor"
            },
            "description": "Kabupaten/kota exact — mis. `Kota Bogor`, `Kab. Bogor`, `Kota Adm. Jakarta Utara`"
          },
          {
            "name": "kecamatan",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Cipondoh"
            },
            "description": "Nama kecamatan; awalan `Kec. ` ditambahkan otomatis"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 15
            },
            "description": "Jumlah item per halaman (1-100, default 15)"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Nomor halaman (default 1)"
          }
        ],
        "responses": {
          "200": {
            "description": "Daftar sekolah + paginasi",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "sekolah_id": "0C1DCB54-D2EB-4E52-A08E-819AC1914F86",
                      "npsn": "20614522",
                      "nama": "SMAS DAARUL QUR AN INTERNASIONAL",
                      "bentuk_pendidikan": "SMA",
                      "status_sekolah": "SWASTA",
                      "provinsi": "Prov. Banten",
                      "kabupaten": "Kota Tangerang",
                      "kecamatan": "Kec. Cipondoh",
                      "alamat_jalan": "JL. THAMRIN KETAPANG CIPONDOH KOTA TANGERANG",
                      "akreditasi": "A",
                      "path_file": "https://file.data.kemendikdasmen.go.id/sekolahkita/20/2061/20614522-4.jpg"
                    }
                  ],
                  "page": 1,
                  "limit": 15,
                  "total_items": 1,
                  "total_pages": 1
                }
              }
            }
          },
          "400": {
            "description": "Filter tidak valid (mis. status di luar NEGERI/SWASTA)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "Database not configured",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-school/{id}": {
      "get": {
        "tags": [
          "Indonesia School"
        ],
        "summary": "Get school detail by sekolah_id",
        "description": "Detail satu sekolah: identitas, alamat, kontak, koordinat, luas tanah, daya listrik, yayasan, plus kondisi ruang (ruang), jumlah guru (ptk), kurikulum, foto, dan rasio siswa/guru. Ambil `sekolah_id` dari endpoint list.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0C1DCB54-D2EB-4E52-A08E-819AC1914F86"
            },
            "description": "UUID sekolah dari endpoint list"
          }
        ],
        "responses": {
          "200": {
            "description": "Detail sekolah + ruang, ptk, rasio, kurikulum, foto",
            "content": {
              "application/json": {
                "example": {
                  "npsn": "20614522",
                  "nama": "SMAS DAARUL QUR AN INTERNASIONAL",
                  "bentuk_pendidikan": "SMA",
                  "status_sekolah": "SWASTA",
                  "provinsi": "Prov. Banten",
                  "kabupaten": "Kota Tangerang",
                  "email": "daqusma@gmail.com",
                  "website": "http://www.daqu.sch.id",
                  "lintang": -6.1841,
                  "bujur": 106.6903,
                  "yayasan": "YAYASAN DAARUL QUR'AN INDONESIA",
                  "ruang": {
                    "ruang_kelas_baik": 23,
                    "ruang_perpustakaan_baik": 1
                  },
                  "ptk": {
                    "ptk_guru_l": 16,
                    "ptk_guru_p": 6
                  },
                  "rasio": {
                    "jml_pd": 273,
                    "jml_rombel": 8,
                    "rasio_siswa_guru": 13
                  },
                  "kurikulum": [
                    {
                      "semester_id": "20251",
                      "kurikulum": "Kurikulum SMA Merdeka"
                    }
                  ],
                  "foto": [
                    "https://file.data.kemendikdasmen.go.id/sekolahkita/20/2061/20614522-4.jpg"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "bukan UUID yang valid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "description": "Sekolah tidak ditemukan / detail belum discrape",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "Database not configured",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/indonesia-prayer-time": {
      "get": {
        "tags": [
          "Prayer Time"
        ],
        "summary": "Prayer-time API root / source info",
        "responses": {
          "200": {
            "description": "Upstream info (pass-through)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-prayer-time/kabkota/semua": {
      "get": {
        "tags": [
          "Prayer Time"
        ],
        "summary": "List all kab/kota",
        "responses": {
          "200": {
            "description": "Kab/kota list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-prayer-time/kabkota/{id}": {
      "get": {
        "tags": [
          "Prayer Time"
        ],
        "summary": "Kab/kota detail",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Kab/kota detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-prayer-time/kabkota/cari/{keyword}": {
      "get": {
        "tags": [
          "Prayer Time"
        ],
        "summary": "Search kab/kota by keyword",
        "parameters": [
          {
            "name": "keyword",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "kediri"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-prayer-time/kabkota/cari": {
      "post": {
        "tags": [
          "Prayer Time"
        ],
        "summary": "Search kab/kota (JSON body)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "keyword": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-prayer-time/jadwal/{id}/today": {
      "get": {
        "tags": [
          "Prayer Time"
        ],
        "summary": "Today's prayer schedule for kab/kota",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "eda80a3d5b344bc40f3bc04f65b7a357"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Today schedule",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/indonesia-prayer-time/jadwal/{id}/{period}": {
      "get": {
        "tags": [
          "Prayer Time"
        ],
        "summary": "Prayer schedule for period (date/month)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "period",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Date or month period string accepted by upstream"
          }
        ],
        "responses": {
          "200": {
            "description": "Period schedule",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Hadis API root",
        "responses": {
          "200": {
            "description": "Root info",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/enc": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Encyclopedia info",
        "responses": {
          "200": {
            "description": "Encyclopedia info",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/enc/show/{id}": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Hadith by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Hadith",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/enc/next/{id}": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Next hadith after ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Hadith",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/enc/prev/{id}": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Previous hadith before ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Hadith",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/enc/random": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Random hadith",
        "responses": {
          "200": {
            "description": "Random hadith",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/enc/explore": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Explore hadith (pagination via query)",
        "responses": {
          "200": {
            "description": "Explore page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/enc/cari/{keyword}": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Search hadith by keyword",
        "parameters": [
          {
            "name": "keyword",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "kiamat"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/perawi": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "List narrators (perawi)",
        "responses": {
          "200": {
            "description": "Narrator list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/perawi/id/{id}": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Narrator detail",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Narrator",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/hadis/perawi/browse": {
      "get": {
        "tags": [
          "Hadis"
        ],
        "summary": "Browse narrator data",
        "responses": {
          "200": {
            "description": "Browse page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/ocr/ktp": {
      "post": {
        "tags": [
          "OCR"
        ],
        "summary": "OCR Indonesian KTP image",
        "description": "Upload a KTP photo as `multipart/form-data` field **`file`** (JPEG/JPG/PNG/WebP/BMP or PDF first page, max 10MB).\n\n**Swagger UI / Postman tips:**\n- Authorize with header `x-ggapi-co-id`\n- Do **not** set `Content-Type` manually — the client must send `multipart/form-data; boundary=...`\n- In Swagger: click **Try it out** → Choose File → Execute\n- Path is `/api/ocr/ktp` (not `/v1/ocr/ktp`)",
        "operationId": "ocrKtp",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Image (jpeg/jpg/png/webp/bmp) or PDF (first page). Form field name must be file."
                  }
                }
              },
              "encoding": {
                "file": {
                  "contentType": "image/jpeg, image/png, image/webp, image/bmp, application/pdf"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted KTP fields + raw OCR lines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OcrKtpResponse"
                },
                "example": {
                  "filename": "ktp.jpeg",
                  "size": 202482,
                  "fields": {
                    "nik": "3171012345670001",
                    "nama": "BUDI SANTOSO",
                    "tempat_lahir": "JAKARTA",
                    "tanggal_lahir": "12-08-1996",
                    "jenis_kelamin": "LAKI-LAKI",
                    "golongan_darah": null,
                    "alamat": "JL. KEMANG RAYA NO. 15",
                    "rt_rw": "002/003",
                    "kelurahan_desa": "BANGKA",
                    "kecamatan": "MAMPANG PRAPATAN",
                    "agama": "ISLAM",
                    "status_perkawinan": "BELUM KAWIN",
                    "pekerjaan": "KARYAWAN SWASTA",
                    "kewarganegaraan": "WNI",
                    "berlaku_hingga": "SEUMUR HIDUP"
                  },
                  "raw_text": [
                    "PROVINSI DKI JAKARTA",
                    "NIK 3171012345670001"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Empty file",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "detail": "File kosong"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "413": {
            "description": "File too large (>10MB)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported media type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "detail": "Tipe file tidak didukung: application/pdf"
                }
              }
            }
          },
          "422": {
            "description": "Missing file field or OCR processing failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "detail": [
                    {
                      "type": "missing",
                      "loc": [
                        "body",
                        "file"
                      ],
                      "msg": "Field required"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "OCR engine unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/ocr/sim": {
      "post": {
        "tags": [
          "OCR"
        ],
        "summary": "OCR Indonesian SIM (driver license) image",
        "description": "Upload a SIM photo as `multipart/form-data` field **`file`** (JPEG/JPG/PNG/WebP/BMP or PDF first page, max 10MB). Same auth/header rules as `/api/ocr/ktp`.",
        "operationId": "ocrSim",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Image (jpeg/jpg/png/webp/bmp) or PDF (first page). Form field name must be file."
                  }
                }
              },
              "encoding": {
                "file": {
                  "contentType": "image/jpeg, image/png, image/webp, image/bmp, application/pdf"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted SIM fields + raw OCR lines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OcrSimResponse"
                }
              }
            }
          },
          "400": {
            "description": "Empty file",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "413": {
            "description": "File too large (>10MB)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported media type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Missing file or OCR failure",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "OCR engine unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}