{
  "openapi": "3.1.0",
  "info": {
    "title": "API Indonesia",
    "version": "1.0.0",
    "description": "REST API for Indonesian public data.",
    "contact": {
      "url": "https://apiindonesia.id"
    },
    "license": {
      "name": "API Indonesia Terms of Service",
      "url": "https://apiindonesia.id/terms"
    }
  },
  "servers": [
    {
      "url": "https://use.apiindonesia.id",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "API documentation",
    "url": "https://docs.apiindonesia.id/en/docs"
  },
  "tags": [
    {
      "name": "wilayah",
      "description": "Regions. Indonesian administrative region data from province down to village level, complete with region codes, postal codes, and coordinates. All list endpoints support pagination."
    },
    {
      "name": "hari-libur",
      "description": "Public Holidays. Calendar of national public holidays, religious holidays, and joint leave days based on the SKB 3 Menteri (joint ministerial decree)."
    },
    {
      "name": "jadwal-sholat",
      "description": "Prayer Times. Daily and monthly prayer times for 514 regencies/cities across Indonesia. Data is sourced from Kemenag (Ministry of Religious Affairs)."
    },
    {
      "name": "sekolah",
      "description": "Schools. Directory of 200,000+ schools across Indonesia (SD, SMP, SMA, SMK, SLB) with NPSN (national school ID), address, accreditation, and coordinates."
    },
    {
      "name": "kampus",
      "description": "Universities. Directory of 4,300+ state and private higher-education institutions: universities, institutes, polytechnics, academies, and colleges."
    },
    {
      "name": "rumah-sakit",
      "description": "Hospitals. Directory of 3,000+ hospitals across Indonesia with type, class, ownership, bed capacity, and contact details."
    },
    {
      "name": "gempa",
      "description": "Earthquakes. Realtime earthquake data sourced from BMKG, refreshed automatically every hour via cron."
    },
    {
      "name": "cuaca",
      "description": "Weather Forecast. 3-day weather forecast from BMKG for all villages across Indonesia. Data is fetched on demand and cached for 6 hours. Use the village code (adm4) from the Regions endpoint."
    },
    {
      "name": "peringatan-dini",
      "description": "Weather Warnings. Extreme weather warnings from BMKG RSS/CAP. Data is refreshed hourly and refreshed on demand when the cache is empty."
    },
    {
      "name": "kodepos",
      "description": "Postal Codes. Directory of 83,000+ postal codes across Indonesia complete with regional hierarchy from province to village. Static data sourced from the official dataset."
    },
    {
      "name": "kurs",
      "description": "Exchange Rates. Foreign exchange rates against Indonesian Rupiah (IDR) from Frankfurter/ECB. Data is refreshed automatically daily via cron. Available pairs: USD, EUR, SGD, GBP to IDR."
    },
    {
      "name": "quran",
      "description": "Al-Quran. Complete list of 114 Al-Quran surahs with Arabic name, Latin transliteration, translation, number of verses, and revelation place. Static data sourced from quran.gading.dev."
    },
    {
      "name": "alkitab",
      "description": "Alkitab (Bible). Indonesian Bible (TB translation) — 66 books (39 Old Testament + 27 New Testament) with 31,104 verses. Data sourced from alkitab.sabda.org."
    },
    {
      "name": "kbbi",
      "description": "KBBI (Indonesian Dictionary). Indonesian Great Dictionary (KBBI) with 62,850+ word entries. Supports word search, exact word lookup, and random words. Data sourced from github.com/dyazincahya/KBBI-SQL-database (MIT License)."
    },
    {
      "name": "plat-nomor",
      "description": "Vehicle Plates. Complete list of 56 Indonesian vehicle plate codes with region and province names. Static data sourced from an open dataset."
    },
    {
      "name": "ump",
      "description": "Minimum Provincial Wages (UMP). Provincial Minimum Wage (UMP) data from 1997 to 2025 across 38 Indonesian provinces. Source: Kaggle \"Indonesian Salary by Region\" (Apache 2.0). Supports filtering by year, province name, and salary range."
    },
    {
      "name": "validasi",
      "description": "Format Validation. Structural validation for NIK, NPWP, phone numbers, and bank accounts — no lookup to official databases. Includes Indonesian bank code list."
    },
    {
      "name": "util",
      "description": "Developer Utilities. Pure local utilities: Rupiah format/parse, dummy data generators (NIK/NPWP/phone), and QRIS parser."
    },
    {
      "name": "kurs-bi",
      "description": "Bank Indonesia Rates. Official Bank Indonesia exchange rates (transaction rates). Cached in D1 and refreshed via cron. BI attribution required."
    },
    {
      "name": "halal",
      "description": "Halal (BPJPH). Search halal supervisors/businesses from the BPJPH portal. On-demand: fetches upstream on cache miss, 24h TTL per keyword."
    },
    {
      "name": "bpom",
      "description": "BPOM Products. Search BPOM registered products (NIE, name, ingredients). On-demand with 24h cache per keyword."
    },
    {
      "name": "hijriah",
      "description": "Hijri Calendar. Gregorian ↔ Hijri date conversion based on the Indonesian Hijri Calendar (Kemenag, MABIMS). Static 2026 data."
    },
    {
      "name": "akun",
      "description": "Account Authentication. Register and log in to obtain a JWT token. The token is used to manage API keys, view usage, and subscribe."
    },
    {
      "name": "api-keys",
      "description": "API Keys. Manage your API keys: create a key per application, list your keys, and revoke keys you no longer use. All endpoints require a JWT token."
    },
    {
      "name": "ojk-invest",
      "description": "OJK Investment. Investment data from Otoritas Jasa Keuangan (OJK — Indonesian Financial Services Authority): legal mutual fund products, legal investment apps, and illegal investment watchlist. Data sourced from Namchee/ojk-invest-api (MIT License)."
    },
    {
      "name": "billing",
      "description": "Billing. Subscribe to the Pro or Business plan, or buy pay-as-you-go credits, through Sumopod. Payments default to QRIS. Every paid plan grants credits; requests beyond the included quota debit 1 credit per successful request."
    },
    {
      "name": "astronomi",
      "description": "Astronomy. Computed astronomy from public NOAA/USNO formulas: sunrise/sunset, day length, twilight, moon phase, upcoming moon phases, and eclipse predictions. Pure local computation — no external data source, commercially safe to use."
    },
    {
      "name": "merek",
      "description": "Trademarks (DJKI). Indonesian trademark data from DJKI: 900,000+ registered marks with application numbers, owners, NICE classes, and consultants, plus the trademark journal (Berita Resmi Merek) to monitor new filings by your brand or competitors. Search by name, owner, and class."
    }
  ],
  "paths": {
    "/api/v1/wilayah/provinsi": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-provinsi",
        "summary": "List provinces",
        "description": "Returns a list of all provinces in Indonesia.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "example": 1,
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer",
              "example": 20,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "alt_name": {
                            "type": "string"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "32",
                      "code": "32",
                      "name": "JAWA BARAT",
                      "alt_name": "Jawa Barat",
                      "lat": -6.914744,
                      "lng": 107.609811,
                      "is_active": 1
                    }
                  ],
                  "meta": {
                    "total": 38,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 2
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/provinsi/{id}": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-provinsi-detail",
        "summary": "Retrieve a province",
        "description": "Returns a single province by its region code.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Province code, e.g. 32.",
            "schema": {
              "type": "string",
              "example": "32"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "alt_name": {
                          "type": "string"
                        },
                        "lat": {
                          "type": "number"
                        },
                        "lng": {
                          "type": "number"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "32",
                    "code": "32",
                    "name": "JAWA BARAT",
                    "alt_name": "Jawa Barat",
                    "lat": -6.914744,
                    "lng": 107.609811,
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/kabupaten": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-kabupaten",
        "summary": "List regencies/cities",
        "description": "Returns a list of regencies and cities, optionally filtered by province.",
        "parameters": [
          {
            "name": "provinsi_id",
            "in": "query",
            "required": false,
            "description": "Filter by province code.",
            "schema": {
              "type": "string",
              "example": "32"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "alt_name": {
                            "type": "string"
                          },
                          "is_city": {
                            "type": "number"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3273",
                      "province_id": "32",
                      "code": "32.73",
                      "name": "KOTA BANDUNG",
                      "alt_name": "Kota Bandung",
                      "is_city": 1,
                      "lat": -6.9175,
                      "lng": 107.6191,
                      "is_active": 1
                    }
                  ],
                  "meta": {
                    "total": 27,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 2
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/kabupaten/{id}": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-kabupaten-detail",
        "summary": "Retrieve a regency/city",
        "description": "Returns a single regency/city by its region code.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Regency/city code, e.g. 3273.",
            "schema": {
              "type": "string",
              "example": "3273"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "province_id": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "alt_name": {
                          "type": "string"
                        },
                        "is_city": {
                          "type": "number"
                        },
                        "lat": {
                          "type": "number"
                        },
                        "lng": {
                          "type": "number"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "3273",
                    "province_id": "32",
                    "code": "32.73",
                    "name": "KOTA BANDUNG",
                    "alt_name": "Kota Bandung",
                    "is_city": 1,
                    "lat": -6.9175,
                    "lng": 107.6191,
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/kecamatan": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-kecamatan",
        "summary": "List districts",
        "description": "Returns a list of districts, optionally filtered by regency/city.",
        "parameters": [
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": false,
            "description": "Filter by regency/city code.",
            "schema": {
              "type": "string",
              "example": "3273"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "alt_name": {
                            "type": "string"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3273010",
                      "regency_id": "3273",
                      "code": "32.73.01",
                      "name": "SUKASARI",
                      "alt_name": "Sukasari",
                      "lat": -6.8636,
                      "lng": 107.5843,
                      "is_active": 1
                    }
                  ],
                  "meta": {
                    "total": 30,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 2
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/kecamatan/{id}": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-kecamatan-detail",
        "summary": "Retrieve a district",
        "description": "Returns a single district by its region code.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "District code, e.g. 3273010.",
            "schema": {
              "type": "string",
              "example": "3273010"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "regency_id": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "alt_name": {
                          "type": "string"
                        },
                        "lat": {
                          "type": "number"
                        },
                        "lng": {
                          "type": "number"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "3273010",
                    "regency_id": "3273",
                    "code": "32.73.01",
                    "name": "SUKASARI",
                    "alt_name": "Sukasari",
                    "lat": -6.8636,
                    "lng": 107.5843,
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/kelurahan": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-kelurahan",
        "summary": "List villages",
        "description": "Returns a list of villages complete with postal codes, optionally filtered by district.",
        "parameters": [
          {
            "name": "kecamatan_id",
            "in": "query",
            "required": false,
            "description": "Filter by district code.",
            "schema": {
              "type": "string",
              "example": "3273010"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "district_id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "alt_name": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "is_courier_support": {
                            "type": "number"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3273010001",
                      "district_id": "3273010",
                      "code": "32.73.01.1001",
                      "name": "ISOLA",
                      "alt_name": "Isola",
                      "postal_code": "40154",
                      "is_courier_support": 1,
                      "lat": -6.8593,
                      "lng": 107.5921,
                      "is_active": 1
                    }
                  ],
                  "meta": {
                    "total": 4,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/kelurahan/{id}": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-kelurahan-detail",
        "summary": "Retrieve a village",
        "description": "Returns a single village by its region code.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Village code, e.g. 3273010001.",
            "schema": {
              "type": "string",
              "example": "3273010001"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "district_id": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "alt_name": {
                          "type": "string"
                        },
                        "postal_code": {
                          "type": "string"
                        },
                        "is_courier_support": {
                          "type": "number"
                        },
                        "lat": {
                          "type": "number"
                        },
                        "lng": {
                          "type": "number"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "3273010001",
                    "district_id": "3273010",
                    "code": "32.73.01.1001",
                    "name": "ISOLA",
                    "alt_name": "Isola",
                    "postal_code": "40154",
                    "is_courier_support": 1,
                    "lat": -6.8593,
                    "lng": 107.5921,
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/wilayah/search": {
      "get": {
        "tags": [
          "wilayah"
        ],
        "operationId": "wilayah-search",
        "summary": "Search regions",
        "description": "Searches region names across all levels at once (province, regency, district, village). The level field indicates the region level of each result.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword, minimum 2 characters.",
            "schema": {
              "type": "string",
              "example": "bandung"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "alt_name": {
                            "type": "string"
                          },
                          "level": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3273",
                      "name": "KOTA BANDUNG",
                      "alt_name": "Kota Bandung",
                      "level": "kabupaten"
                    },
                    {
                      "id": "3204",
                      "name": "KABUPATEN BANDUNG",
                      "alt_name": "Kabupaten Bandung",
                      "level": "kabupaten"
                    }
                  ],
                  "meta": {
                    "total": 12,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/libur": {
      "get": {
        "tags": [
          "hari-libur"
        ],
        "operationId": "libur-list",
        "summary": "List public holidays",
        "description": "Returns a list of public holidays, sorted by date. Can be filtered by year and month.",
        "parameters": [
          {
            "name": "tahun",
            "in": "query",
            "required": false,
            "description": "Filter by year, e.g. 2026.",
            "schema": {
              "type": "integer",
              "example": 2026
            }
          },
          {
            "name": "bulan",
            "in": "query",
            "required": false,
            "description": "Filter by month (two digits), e.g. 06.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "is_joint_leave": {
                            "type": "number"
                          },
                          "description": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "source": {
                            "type": "string"
                          },
                          "year": {
                            "type": "number"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "hol_2026_001",
                      "date": "2026-01-01",
                      "name": "Tahun Baru 2026 Masehi",
                      "type": "nasional",
                      "is_joint_leave": 0,
                      "description": null,
                      "source": "SKB 3 Menteri",
                      "year": 2026,
                      "is_active": 1
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/libur/upcoming": {
      "get": {
        "tags": [
          "hari-libur"
        ],
        "operationId": "libur-upcoming",
        "summary": "Upcoming holidays",
        "description": "Returns up to 10 upcoming public holidays, counted from today.",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "is_joint_leave": {
                            "type": "number"
                          },
                          "source": {
                            "type": "string"
                          },
                          "year": {
                            "type": "number"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "hol_2026_014",
                      "date": "2026-08-17",
                      "name": "Hari Kemerdekaan RI",
                      "type": "nasional",
                      "is_joint_leave": 0,
                      "source": "SKB 3 Menteri",
                      "year": 2026,
                      "is_active": 1
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/libur/{id}": {
      "get": {
        "tags": [
          "hari-libur"
        ],
        "operationId": "libur-detail",
        "summary": "Retrieve a holiday",
        "description": "Returns a single public holiday by ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Holiday ID.",
            "schema": {
              "type": "string",
              "example": "hol_2026_014"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "date": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        },
                        "is_joint_leave": {
                          "type": "number"
                        },
                        "description": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "source": {
                          "type": "string"
                        },
                        "year": {
                          "type": "number"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "hol_2026_014",
                    "date": "2026-08-17",
                    "name": "Hari Kemerdekaan RI",
                    "type": "nasional",
                    "is_joint_leave": 0,
                    "description": null,
                    "source": "SKB 3 Menteri",
                    "year": 2026,
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sholat/list-kota": {
      "get": {
        "tags": [
          "jadwal-sholat"
        ],
        "operationId": "sholat-list-kota",
        "summary": "List available cities",
        "description": "Returns the regencies/cities that have prayer time data. Use the regency_id from this response with the schedule endpoints.",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "regency_id": {
                            "type": "string"
                          },
                          "regency_name": {
                            "type": "string"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "province_name": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "regency_id": "3273",
                      "regency_name": "KOTA BANDUNG",
                      "province_id": "32",
                      "province_name": "JAWA BARAT"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sholat": {
      "get": {
        "tags": [
          "jadwal-sholat"
        ],
        "operationId": "sholat-harian",
        "summary": "Daily prayer times",
        "description": "Returns one day of prayer times for a given regency/city.",
        "parameters": [
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": true,
            "description": "Regency/city code, e.g. 3273.",
            "schema": {
              "type": "string",
              "example": "3273"
            }
          },
          {
            "name": "tanggal",
            "in": "query",
            "required": false,
            "description": "Date in YYYY-MM-DD format. Defaults to today.",
            "schema": {
              "type": "string",
              "example": "2026-07-03"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "regency_id": {
                          "type": "string"
                        },
                        "date": {
                          "type": "string"
                        },
                        "imsyak": {
                          "type": "string"
                        },
                        "shubuh": {
                          "type": "string"
                        },
                        "terbit": {
                          "type": "string"
                        },
                        "dhuha": {
                          "type": "string"
                        },
                        "dzuhur": {
                          "type": "string"
                        },
                        "ashr": {
                          "type": "string"
                        },
                        "maghrib": {
                          "type": "string"
                        },
                        "isya": {
                          "type": "string"
                        },
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "pt_3273_2026-07-03",
                    "regency_id": "3273",
                    "date": "2026-07-03",
                    "imsyak": "04:29",
                    "shubuh": "04:39",
                    "terbit": "05:58",
                    "dhuha": "06:26",
                    "dzuhur": "11:56",
                    "ashr": "15:17",
                    "maghrib": "17:47",
                    "isya": "19:01",
                    "source": "Kemenag"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sholat/monthly": {
      "get": {
        "tags": [
          "jadwal-sholat"
        ],
        "operationId": "sholat-bulanan",
        "summary": "Monthly prayer times",
        "description": "Returns a full month of prayer times for a given regency/city, sorted by date.",
        "parameters": [
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": true,
            "description": "Regency/city code, e.g. 3273.",
            "schema": {
              "type": "string",
              "example": "3273"
            }
          },
          {
            "name": "tahun",
            "in": "query",
            "required": true,
            "description": "Year, e.g. 2026.",
            "schema": {
              "type": "integer",
              "example": 2026
            }
          },
          {
            "name": "bulan",
            "in": "query",
            "required": true,
            "description": "Month (1–12), e.g. 7.",
            "schema": {
              "type": "integer",
              "example": 7
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "imsyak": {
                            "type": "string"
                          },
                          "shubuh": {
                            "type": "string"
                          },
                          "terbit": {
                            "type": "string"
                          },
                          "dhuha": {
                            "type": "string"
                          },
                          "dzuhur": {
                            "type": "string"
                          },
                          "ashr": {
                            "type": "string"
                          },
                          "maghrib": {
                            "type": "string"
                          },
                          "isya": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "pt_3273_2026-07-01",
                      "regency_id": "3273",
                      "date": "2026-07-01",
                      "imsyak": "04:28",
                      "shubuh": "04:38",
                      "terbit": "05:57",
                      "dhuha": "06:25",
                      "dzuhur": "11:55",
                      "ashr": "15:17",
                      "maghrib": "17:46",
                      "isya": "19:00",
                      "source": "Kemenag"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sekolah": {
      "get": {
        "tags": [
          "sekolah"
        ],
        "operationId": "sekolah-list",
        "summary": "List schools",
        "description": "Returns a paginated list of schools with region, level, and status filters.",
        "parameters": [
          {
            "name": "provinsi_id",
            "in": "query",
            "required": false,
            "description": "Filter by province code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": false,
            "description": "Filter by regency/city code.",
            "schema": {
              "type": "string",
              "example": "3273"
            }
          },
          {
            "name": "jenis",
            "in": "query",
            "required": false,
            "description": "School level: SD, SMP, SMA, SMK, or SLB.",
            "schema": {
              "type": "string",
              "example": "SMA"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "School status: Negeri (state) or Swasta (private).",
            "schema": {
              "type": "string",
              "example": "Negeri"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "npsn": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "jenis": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          },
                          "district_id": {
                            "type": "string"
                          },
                          "village_id": {
                            "type": "string"
                          },
                          "address": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "phone": {
                            "type": "string"
                          },
                          "email": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "website": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "accreditation": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "npsn": "20219557",
                      "name": "SMA NEGERI 3 BANDUNG",
                      "jenis": "SMA",
                      "status": "Negeri",
                      "province_id": "32",
                      "regency_id": "3273",
                      "district_id": "3273190",
                      "village_id": "3273190004",
                      "address": "Jl. Belitung No. 8",
                      "postal_code": "40113",
                      "lat": -6.9106,
                      "lng": 107.6183,
                      "phone": "0224235154",
                      "email": null,
                      "website": null,
                      "accreditation": "A",
                      "is_active": 1
                    }
                  ],
                  "meta": {
                    "total": 27,
                    "page": 1,
                    "per_page": 50,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sekolah/search": {
      "get": {
        "tags": [
          "sekolah"
        ],
        "operationId": "sekolah-search",
        "summary": "Search schools",
        "description": "Searches schools by name. Returns up to 50 results.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword, minimum 3 characters.",
            "schema": {
              "type": "string",
              "example": "taruna"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "npsn": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "jenis": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          },
                          "accreditation": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "npsn": "20302879",
                      "name": "SMA TARUNA NUSANTARA",
                      "jenis": "SMA",
                      "status": "Swasta",
                      "province_id": "33",
                      "regency_id": "3308",
                      "accreditation": "A",
                      "is_active": 1
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/sekolah/{npsn}": {
      "get": {
        "tags": [
          "sekolah"
        ],
        "operationId": "sekolah-detail",
        "summary": "Retrieve a school",
        "description": "Returns a single school by NPSN, including its province and regency/city names.",
        "parameters": [
          {
            "name": "npsn",
            "in": "path",
            "required": true,
            "description": "NPSN (national school ID).",
            "schema": {
              "type": "string",
              "example": "20219557"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "npsn": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "jenis": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "province_id": {
                          "type": "string"
                        },
                        "regency_id": {
                          "type": "string"
                        },
                        "address": {
                          "type": "string"
                        },
                        "postal_code": {
                          "type": "string"
                        },
                        "accreditation": {
                          "type": "string"
                        },
                        "is_active": {
                          "type": "number"
                        },
                        "province_name": {
                          "type": "string"
                        },
                        "regency_name": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "npsn": "20219557",
                    "name": "SMA NEGERI 3 BANDUNG",
                    "jenis": "SMA",
                    "status": "Negeri",
                    "province_id": "32",
                    "regency_id": "3273",
                    "address": "Jl. Belitung No. 8",
                    "postal_code": "40113",
                    "accreditation": "A",
                    "is_active": 1,
                    "province_name": "JAWA BARAT",
                    "regency_name": "KOTA BANDUNG"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kampus": {
      "get": {
        "tags": [
          "kampus"
        ],
        "operationId": "kampus-list",
        "summary": "List universities",
        "description": "Returns a paginated, filterable list of higher-education institutions, including province and regency/city names.",
        "parameters": [
          {
            "name": "provinsi_id",
            "in": "query",
            "required": false,
            "description": "Filter by province code.",
            "schema": {
              "type": "string",
              "example": "32"
            }
          },
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": false,
            "description": "Filter by regency/city code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "jenis",
            "in": "query",
            "required": false,
            "description": "Type: universitas, institut, politeknik, akademi, or sekolah_tinggi.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kelompok",
            "in": "query",
            "required": false,
            "description": "Group: PTN (state) or PTS (private).",
            "schema": {
              "type": "string",
              "example": "PTN"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "short_name": {
                            "type": "string"
                          },
                          "jenis": {
                            "type": "string"
                          },
                          "kelompok": {
                            "type": "string"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          },
                          "address": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "website": {
                            "type": "string"
                          },
                          "accreditation": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "number"
                          },
                          "province_name": {
                            "type": "string"
                          },
                          "regency_name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "pt_001",
                      "name": "INSTITUT TEKNOLOGI BANDUNG",
                      "short_name": "ITB",
                      "jenis": "institut",
                      "kelompok": "PTN",
                      "province_id": "32",
                      "regency_id": "3273",
                      "address": "Jl. Ganesha No. 10",
                      "postal_code": "40132",
                      "website": "https://itb.ac.id",
                      "accreditation": "Unggul",
                      "is_active": 1,
                      "province_name": "JAWA BARAT",
                      "regency_name": "KOTA BANDUNG"
                    }
                  ],
                  "meta": {
                    "total": 12,
                    "page": 1,
                    "per_page": 50,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kampus/search": {
      "get": {
        "tags": [
          "kampus"
        ],
        "operationId": "kampus-search",
        "summary": "Search universities",
        "description": "Searches institutions by name or abbreviation. Returns up to 50 results.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword, minimum 2 characters.",
            "schema": {
              "type": "string",
              "example": "itb"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "short_name": {
                            "type": "string"
                          },
                          "jenis": {
                            "type": "string"
                          },
                          "kelompok": {
                            "type": "string"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "pt_001",
                      "name": "INSTITUT TEKNOLOGI BANDUNG",
                      "short_name": "ITB",
                      "jenis": "institut",
                      "kelompok": "PTN",
                      "province_id": "32",
                      "regency_id": "3273"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kampus/{id}": {
      "get": {
        "tags": [
          "kampus"
        ],
        "operationId": "kampus-detail",
        "summary": "Retrieve a university",
        "description": "Returns a single institution by ID, including province and regency/city names.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Institution ID.",
            "schema": {
              "type": "string",
              "example": "pt_001"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "short_name": {
                          "type": "string"
                        },
                        "jenis": {
                          "type": "string"
                        },
                        "kelompok": {
                          "type": "string"
                        },
                        "province_id": {
                          "type": "string"
                        },
                        "regency_id": {
                          "type": "string"
                        },
                        "address": {
                          "type": "string"
                        },
                        "postal_code": {
                          "type": "string"
                        },
                        "website": {
                          "type": "string"
                        },
                        "accreditation": {
                          "type": "string"
                        },
                        "is_active": {
                          "type": "number"
                        },
                        "province_name": {
                          "type": "string"
                        },
                        "regency_name": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "pt_001",
                    "name": "INSTITUT TEKNOLOGI BANDUNG",
                    "short_name": "ITB",
                    "jenis": "institut",
                    "kelompok": "PTN",
                    "province_id": "32",
                    "regency_id": "3273",
                    "address": "Jl. Ganesha No. 10",
                    "postal_code": "40132",
                    "website": "https://itb.ac.id",
                    "accreditation": "Unggul",
                    "is_active": 1,
                    "province_name": "JAWA BARAT",
                    "regency_name": "KOTA BANDUNG"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/rumah-sakit": {
      "get": {
        "tags": [
          "rumah-sakit"
        ],
        "operationId": "rs-list",
        "summary": "List hospitals",
        "description": "Returns a paginated, filterable list of hospitals, including province and regency/city names.",
        "parameters": [
          {
            "name": "provinsi_id",
            "in": "query",
            "required": false,
            "description": "Filter by province code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": false,
            "description": "Filter by regency/city code.",
            "schema": {
              "type": "string",
              "example": "3273"
            }
          },
          {
            "name": "jenis",
            "in": "query",
            "required": false,
            "description": "Hospital type: RSUM (general), RSWS (regional/private), RSP (teaching), RSJ (psychiatric), RSK (specialty).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kelas",
            "in": "query",
            "required": false,
            "description": "Hospital class: A, B, C, D, or Pratama.",
            "schema": {
              "type": "string",
              "example": "A"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 50, maximum 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "jenis": {
                            "type": "string"
                          },
                          "kelas": {
                            "type": "string"
                          },
                          "ownership": {
                            "type": "string"
                          },
                          "is_blu": {
                            "type": "number"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          },
                          "address": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "phone": {
                            "type": "string"
                          },
                          "beds_total": {
                            "type": "number"
                          },
                          "icu_beds": {
                            "type": "number"
                          },
                          "is_active": {
                            "type": "number"
                          },
                          "province_name": {
                            "type": "string"
                          },
                          "regency_name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "rs_3273_001",
                      "name": "RSUP Dr. Hasan Sadikin",
                      "jenis": "RSUM",
                      "kelas": "A",
                      "ownership": "Kemenkes",
                      "is_blu": 1,
                      "province_id": "32",
                      "regency_id": "3273",
                      "address": "Jl. Pasteur No. 38",
                      "postal_code": "40161",
                      "phone": "0222034953",
                      "beds_total": 944,
                      "icu_beds": 40,
                      "is_active": 1,
                      "province_name": "JAWA BARAT",
                      "regency_name": "KOTA BANDUNG"
                    }
                  ],
                  "meta": {
                    "total": 3,
                    "page": 1,
                    "per_page": 50,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/rumah-sakit/search": {
      "get": {
        "tags": [
          "rumah-sakit"
        ],
        "operationId": "rs-search",
        "summary": "Search hospitals",
        "description": "Searches hospitals by name. Returns up to 50 results.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword, minimum 2 characters.",
            "schema": {
              "type": "string",
              "example": "hasan sadikin"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "jenis": {
                            "type": "string"
                          },
                          "kelas": {
                            "type": "string"
                          },
                          "ownership": {
                            "type": "string"
                          },
                          "province_id": {
                            "type": "string"
                          },
                          "regency_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "rs_3273_001",
                      "name": "RSUP Dr. Hasan Sadikin",
                      "jenis": "RSUM",
                      "kelas": "A",
                      "ownership": "Kemenkes",
                      "province_id": "32",
                      "regency_id": "3273"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/rumah-sakit/{id}": {
      "get": {
        "tags": [
          "rumah-sakit"
        ],
        "operationId": "rs-detail",
        "summary": "Retrieve a hospital",
        "description": "Returns a single hospital by ID, including province and regency/city names.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Hospital ID.",
            "schema": {
              "type": "string",
              "example": "rs_3273_001"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "jenis": {
                          "type": "string"
                        },
                        "kelas": {
                          "type": "string"
                        },
                        "ownership": {
                          "type": "string"
                        },
                        "is_blu": {
                          "type": "number"
                        },
                        "province_id": {
                          "type": "string"
                        },
                        "regency_id": {
                          "type": "string"
                        },
                        "address": {
                          "type": "string"
                        },
                        "postal_code": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "beds_total": {
                          "type": "number"
                        },
                        "icu_beds": {
                          "type": "number"
                        },
                        "doctors_gp": {
                          "type": "number"
                        },
                        "doctors_spec": {
                          "type": "number"
                        },
                        "is_active": {
                          "type": "number"
                        },
                        "province_name": {
                          "type": "string"
                        },
                        "regency_name": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "rs_3273_001",
                    "name": "RSUP Dr. Hasan Sadikin",
                    "jenis": "RSUM",
                    "kelas": "A",
                    "ownership": "Kemenkes",
                    "is_blu": 1,
                    "province_id": "32",
                    "regency_id": "3273",
                    "address": "Jl. Pasteur No. 38",
                    "postal_code": "40161",
                    "phone": "0222034953",
                    "beds_total": 944,
                    "icu_beds": 40,
                    "doctors_gp": 78,
                    "doctors_spec": 312,
                    "is_active": 1,
                    "province_name": "JAWA BARAT",
                    "regency_name": "KOTA BANDUNG"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/gempa/terkini": {
      "get": {
        "tags": [
          "gempa"
        ],
        "operationId": "gempa-terkini",
        "summary": "Latest earthquakes",
        "description": "Returns the 15 most recent earthquakes, newest first.",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "datetime": {
                            "type": "string"
                          },
                          "magnitude": {
                            "type": "number"
                          },
                          "depth_km": {
                            "type": "number"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "region": {
                            "type": "string"
                          },
                          "potential": {
                            "type": "string"
                          },
                          "is_felt": {
                            "type": "number"
                          },
                          "felt_areas": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "eq_20260702_0413",
                      "datetime": "2026-07-02 04:13:21",
                      "magnitude": 5.4,
                      "depth_km": 10,
                      "lat": -8.12,
                      "lng": 110.34,
                      "region": "Pacitan, Jawa Timur",
                      "potential": "Tidak berpotensi tsunami",
                      "is_felt": 1,
                      "felt_areas": "III Yogyakarta, II-III Pacitan",
                      "source": "BMKG"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/gempa/dirasakan": {
      "get": {
        "tags": [
          "gempa"
        ],
        "operationId": "gempa-dirasakan",
        "summary": "Felt earthquakes",
        "description": "Returns the 15 most recent felt earthquakes (BMKG term for quakes reported felt by the public), complete with the MMI scale per area.",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "datetime": {
                            "type": "string"
                          },
                          "magnitude": {
                            "type": "number"
                          },
                          "depth_km": {
                            "type": "number"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "region": {
                            "type": "string"
                          },
                          "potential": {
                            "type": "string"
                          },
                          "is_felt": {
                            "type": "number"
                          },
                          "felt_areas": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "eq_20260702_0413",
                      "datetime": "2026-07-02 04:13:21",
                      "magnitude": 5.4,
                      "depth_km": 10,
                      "lat": -8.12,
                      "lng": 110.34,
                      "region": "Pacitan, Jawa Timur",
                      "potential": "Tidak berpotensi tsunami",
                      "is_felt": 1,
                      "felt_areas": "III Yogyakarta, II-III Pacitan",
                      "source": "BMKG"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/gempa/history": {
      "get": {
        "tags": [
          "gempa"
        ],
        "operationId": "gempa-history",
        "summary": "Earthquake history",
        "description": "Returns earthquakes within a given date range, up to 100 records, newest first.",
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "required": true,
            "description": "Start date, YYYY-MM-DD format.",
            "schema": {
              "type": "string",
              "example": "2026-06-01"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "description": "End date, YYYY-MM-DD format.",
            "schema": {
              "type": "string",
              "example": "2026-06-30"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "datetime": {
                            "type": "string"
                          },
                          "magnitude": {
                            "type": "number"
                          },
                          "depth_km": {
                            "type": "number"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          },
                          "region": {
                            "type": "string"
                          },
                          "potential": {
                            "type": "string"
                          },
                          "is_felt": {
                            "type": "number"
                          },
                          "felt_areas": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "source": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "eq_20260628_2205",
                      "datetime": "2026-06-28 22:05:44",
                      "magnitude": 4.8,
                      "depth_km": 22,
                      "lat": -3.51,
                      "lng": 128.09,
                      "region": "Ambon, Maluku",
                      "potential": "Tidak berpotensi tsunami",
                      "is_felt": 0,
                      "felt_areas": null,
                      "source": "BMKG"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/cuaca": {
      "get": {
        "tags": [
          "cuaca"
        ],
        "operationId": "cuaca-desa",
        "summary": "Village weather forecast",
        "description": "Returns a 3-day weather forecast for a single village. Choose between adm4 (village code) or kabupaten_id to fetch a representative forecast for the regency.",
        "parameters": [
          {
            "name": "adm4",
            "in": "query",
            "required": false,
            "description": "Village code from the Regions endpoint, e.g. 32.73.01.1001.",
            "schema": {
              "type": "string",
              "example": "32.73.01.1001"
            }
          },
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": false,
            "description": "Regency/city code (alternative to adm4), e.g. 3273.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "adm4": {
                            "type": "string"
                          },
                          "provinsi": {
                            "type": "string"
                          },
                          "kotkab": {
                            "type": "string"
                          },
                          "kecamatan": {
                            "type": "string"
                          },
                          "desa": {
                            "type": "string"
                          },
                          "datetime": {
                            "type": "string"
                          },
                          "local_datetime": {
                            "type": "string"
                          },
                          "weather": {
                            "type": "string"
                          },
                          "weather_code": {
                            "type": "string"
                          },
                          "weather_desc": {
                            "type": "string"
                          },
                          "weather_desc_en": {
                            "type": "string"
                          },
                          "temperature_c": {
                            "type": "number"
                          },
                          "humidity_percent": {
                            "type": "number"
                          },
                          "cloud_cover_percent": {
                            "type": "number"
                          },
                          "precipitation_mm": {
                            "type": "number"
                          },
                          "wind_direction": {
                            "type": "string"
                          },
                          "wind_direction_to": {
                            "type": "string"
                          },
                          "wind_direction_degrees": {
                            "type": "number"
                          },
                          "wind_speed": {
                            "type": "number"
                          },
                          "visibility_m": {
                            "type": "number"
                          },
                          "visibility_text": {
                            "type": "string"
                          },
                          "analysis_date": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "w_327301100120260706010000",
                      "adm4": "32.73.01.1001",
                      "provinsi": "Jawa Barat",
                      "kotkab": "Kota Bandung",
                      "kecamatan": "Sukasari",
                      "desa": "Isola",
                      "datetime": "2026-07-06T01:00:00",
                      "local_datetime": "2026-07-06 08:00:00",
                      "weather": "Cerah",
                      "weather_code": "0",
                      "weather_desc": "Cerah Berawan",
                      "weather_desc_en": "Partly Cloudy",
                      "temperature_c": 29,
                      "humidity_percent": 62,
                      "cloud_cover_percent": 2,
                      "precipitation_mm": 0,
                      "wind_direction": "E",
                      "wind_direction_to": "W",
                      "wind_direction_degrees": 125,
                      "wind_speed": 3.5,
                      "visibility_m": 15060,
                      "visibility_text": "> 10 km",
                      "analysis_date": "2026-07-05T12:00:00",
                      "source": "BMKG"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/cuaca/search": {
      "get": {
        "tags": [
          "cuaca"
        ],
        "operationId": "cuaca-search",
        "summary": "Search forecast location",
        "description": "Searches for villages or cities that have weather forecast data by name. Returns a list of adm4 codes usable in the main endpoint.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword, e.g. bandung.",
            "schema": {
              "type": "string",
              "example": "bandung"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "adm4": {
                            "type": "string"
                          },
                          "provinsi": {
                            "type": "string"
                          },
                          "kotkab": {
                            "type": "string"
                          },
                          "kecamatan": {
                            "type": "string"
                          },
                          "desa": {
                            "type": "string"
                          },
                          "lat": {
                            "type": "number"
                          },
                          "lng": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "adm4": "32.73.01.1001",
                      "provinsi": "Jawa Barat",
                      "kotkab": "Kota Bandung",
                      "kecamatan": "Sukasari",
                      "desa": "Isola",
                      "lat": -6.8593,
                      "lng": 107.5921
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/peringatan-dini": {
      "get": {
        "tags": [
          "peringatan-dini"
        ],
        "operationId": "warnings-list",
        "summary": "List weather warnings",
        "description": "Returns all currently active weather warnings. Can be filtered by province name.",
        "parameters": [
          {
            "name": "provinsi",
            "in": "query",
            "required": false,
            "description": "Filter by province name, e.g. Jawa Barat.",
            "schema": {
              "type": "string",
              "example": "Jawa Barat"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "alert_id": {
                            "type": "string"
                          },
                          "event": {
                            "type": "string"
                          },
                          "urgency": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string"
                          },
                          "certainty": {
                            "type": "string"
                          },
                          "area": {
                            "type": "string"
                          },
                          "province": {
                            "type": "string"
                          },
                          "effective": {
                            "type": "string"
                          },
                          "expires": {
                            "type": "string"
                          },
                          "headline": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "instruction": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "warn_2-49-0-1-360-0-2026-07-05-21-18-003",
                      "alert_id": "2.49.0.1.360.0.2026.07.05.21.18.003",
                      "event": "Hujan Lebat disertai Petir",
                      "urgency": "Expected",
                      "severity": "Moderate",
                      "certainty": "Likely",
                      "area": "Batanghari, Bumi Waras, Enggal",
                      "province": "Lampung",
                      "effective": "2026-07-05T21:00:00.000Z",
                      "expires": "06 July 2026, 07:30 WIB",
                      "headline": "Hujan Lebat disertai Petir di Lampung",
                      "description": "Hujan lebat disertai petir akan terjadi...",
                      "instruction": "Ikuti informasi resmi BMKG dan arahan petugas setempat.",
                      "source": "BMKG-CAP",
                      "is_active": 1
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kodepos": {
      "get": {
        "tags": [
          "kodepos"
        ],
        "operationId": "kodepos-list",
        "summary": "List postal codes",
        "description": "Returns a paginated list of postal codes. Can be filtered by province, regency/city, or district.",
        "parameters": [
          {
            "name": "provinsi_id",
            "in": "query",
            "required": false,
            "description": "Filter by province code, e.g. 32.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kabupaten_id",
            "in": "query",
            "required": false,
            "description": "Filter by regency/city code, e.g. 3273.",
            "schema": {
              "type": "string",
              "example": "3273"
            }
          },
          {
            "name": "kecamatan_id",
            "in": "query",
            "required": false,
            "description": "Filter by district code, e.g. 3273010.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "village_code": {
                            "type": "string"
                          },
                          "village_name": {
                            "type": "string"
                          },
                          "district_code": {
                            "type": "string"
                          },
                          "district_name": {
                            "type": "string"
                          },
                          "regency_code": {
                            "type": "string"
                          },
                          "regency_name": {
                            "type": "string"
                          },
                          "province_code": {
                            "type": "string"
                          },
                          "province_name": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "kp_3273010001",
                      "village_code": "32.73.01.1001",
                      "village_name": "ISOLA",
                      "district_code": "32.73.01",
                      "district_name": "SUKASARI",
                      "regency_code": "32.73",
                      "regency_name": "KOTA BANDUNG",
                      "province_code": "32",
                      "province_name": "JAWA BARAT",
                      "postal_code": "40154",
                      "is_active": 1
                    }
                  ],
                  "meta": {
                    "total": 151,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 8
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kodepos/search": {
      "get": {
        "tags": [
          "kodepos"
        ],
        "operationId": "kodepos-search",
        "summary": "Search postal codes",
        "description": "Searches postal codes by village name or postal code number.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Keyword, minimum 2 characters. Can be a village name or postal code.",
            "schema": {
              "type": "string",
              "example": "40154"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "village_code": {
                            "type": "string"
                          },
                          "village_name": {
                            "type": "string"
                          },
                          "district_code": {
                            "type": "string"
                          },
                          "district_name": {
                            "type": "string"
                          },
                          "regency_code": {
                            "type": "string"
                          },
                          "regency_name": {
                            "type": "string"
                          },
                          "province_code": {
                            "type": "string"
                          },
                          "province_name": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "kp_3273010001",
                      "village_code": "32.73.01.1001",
                      "village_name": "ISOLA",
                      "district_code": "32.73.01",
                      "district_name": "SUKASARI",
                      "regency_code": "32.73",
                      "regency_name": "KOTA BANDUNG",
                      "province_code": "32",
                      "province_name": "JAWA BARAT",
                      "postal_code": "40154",
                      "is_active": 1
                    }
                  ],
                  "meta": {
                    "total": 1,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kodepos/{id}": {
      "get": {
        "tags": [
          "kodepos"
        ],
        "operationId": "kodepos-detail",
        "summary": "Retrieve a postal code",
        "description": "Returns a single postal code entry by ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Postal code entry ID, e.g. kp_3273010001.",
            "schema": {
              "type": "string",
              "example": "kp_3273010001"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "village_code": {
                          "type": "string"
                        },
                        "village_name": {
                          "type": "string"
                        },
                        "district_code": {
                          "type": "string"
                        },
                        "district_name": {
                          "type": "string"
                        },
                        "regency_code": {
                          "type": "string"
                        },
                        "regency_name": {
                          "type": "string"
                        },
                        "province_code": {
                          "type": "string"
                        },
                        "province_name": {
                          "type": "string"
                        },
                        "postal_code": {
                          "type": "string"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "kp_3273010001",
                    "village_code": "32.73.01.1001",
                    "village_name": "ISOLA",
                    "district_code": "32.73.01",
                    "district_name": "SUKASARI",
                    "regency_code": "32.73",
                    "regency_name": "KOTA BANDUNG",
                    "province_code": "32",
                    "province_name": "JAWA BARAT",
                    "postal_code": "40154",
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kurs/latest": {
      "get": {
        "tags": [
          "kurs"
        ],
        "operationId": "kurs-latest",
        "summary": "Latest exchange rate",
        "description": "Returns the latest exchange rate for a given currency pair.",
        "parameters": [
          {
            "name": "base",
            "in": "query",
            "required": false,
            "description": "Source currency. Defaults to USD.",
            "schema": {
              "type": "string",
              "example": "USD"
            }
          },
          {
            "name": "target",
            "in": "query",
            "required": false,
            "description": "Target currency. Defaults to IDR.",
            "schema": {
              "type": "string",
              "example": "IDR"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "date": {
                          "type": "string"
                        },
                        "base": {
                          "type": "string"
                        },
                        "target": {
                          "type": "string"
                        },
                        "rate": {
                          "type": "number"
                        },
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "kr_2026-07-03_USD_IDR",
                    "date": "2026-07-03",
                    "base": "USD",
                    "target": "IDR",
                    "rate": 16350.5,
                    "source": "Frankfurter/ECB"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kurs/history": {
      "get": {
        "tags": [
          "kurs"
        ],
        "operationId": "kurs-history",
        "summary": "Historical exchange rates",
        "description": "Returns historical exchange rates for a given date range.",
        "parameters": [
          {
            "name": "base",
            "in": "query",
            "required": false,
            "description": "Source currency. Defaults to USD.",
            "schema": {
              "type": "string",
              "example": "USD"
            }
          },
          {
            "name": "target",
            "in": "query",
            "required": false,
            "description": "Target currency. Defaults to IDR.",
            "schema": {
              "type": "string",
              "example": "IDR"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "description": "Start date, YYYY-MM-DD format.",
            "schema": {
              "type": "string",
              "example": "2026-06-01"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "description": "End date, YYYY-MM-DD format.",
            "schema": {
              "type": "string",
              "example": "2026-06-30"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "base": {
                            "type": "string"
                          },
                          "target": {
                            "type": "string"
                          },
                          "rate": {
                            "type": "number"
                          },
                          "source": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "kr_2026-06-01_USD_IDR",
                      "date": "2026-06-01",
                      "base": "USD",
                      "target": "IDR",
                      "rate": 16290,
                      "source": "Frankfurter/ECB"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/quran/list-surah": {
      "get": {
        "tags": [
          "quran"
        ],
        "operationId": "quran-list-surah",
        "summary": "List surah",
        "description": "Returns the complete list of 114 Al-Quran surahs, ordered by number.",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "name": {
                            "type": "string"
                          },
                          "name_latin": {
                            "type": "string"
                          },
                          "name_arabic": {
                            "type": "string"
                          },
                          "name_translation": {
                            "type": "string"
                          },
                          "number_of_ayahs": {
                            "type": "number"
                          },
                          "revelation_place": {
                            "type": "string"
                          },
                          "revelation_order": {
                            "type": "number"
                          },
                          "audio_url": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "name": "الفاتحة",
                      "name_latin": "Al-Fatihah",
                      "name_arabic": "الفاتحة",
                      "name_translation": "Pembukaan",
                      "number_of_ayahs": 7,
                      "revelation_place": "mecca",
                      "revelation_order": 5,
                      "audio_url": "https://cdn.islamic.network/quran/audio/128/ar.alafasy/1.mp3"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/quran/surah/{id}": {
      "get": {
        "tags": [
          "quran"
        ],
        "operationId": "quran-surah-detail",
        "summary": "Retrieve a surah",
        "description": "Returns the metadata of a single surah by number (1–114).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Surah number, 1–114.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "number"
                        },
                        "name": {
                          "type": "string"
                        },
                        "name_latin": {
                          "type": "string"
                        },
                        "name_arabic": {
                          "type": "string"
                        },
                        "name_translation": {
                          "type": "string"
                        },
                        "number_of_ayahs": {
                          "type": "number"
                        },
                        "revelation_place": {
                          "type": "string"
                        },
                        "revelation_order": {
                          "type": "number"
                        },
                        "audio_url": {
                          "type": "string"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "name": "الفاتحة",
                    "name_latin": "Al-Fatihah",
                    "name_arabic": "الفاتحة",
                    "name_translation": "Pembukaan",
                    "number_of_ayahs": 7,
                    "revelation_place": "mecca",
                    "revelation_order": 5,
                    "audio_url": "https://cdn.islamic.network/quran/audio/128/ar.alafasy/1.mp3",
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alkitab/books": {
      "get": {
        "tags": [
          "alkitab"
        ],
        "operationId": "alkitab-books",
        "summary": "List books",
        "description": "Returns all 66 Bible books with chapter counts and testament classification (OT/NT).",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "abbr": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "chapter_count": {
                            "type": "number"
                          },
                          "testament": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "abbr": "Kej",
                      "name": "Kejadian",
                      "chapter_count": 50,
                      "testament": "PL"
                    },
                    {
                      "id": 40,
                      "abbr": "Mat",
                      "name": "Matius",
                      "chapter_count": 28,
                      "testament": "PB"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alkitab/books/{testament}": {
      "get": {
        "tags": [
          "alkitab"
        ],
        "operationId": "alkitab-books-testament",
        "summary": "List books by testament",
        "description": "Filter books by testament: PL (Old Testament) or PB (New Testament).",
        "parameters": [
          {
            "name": "testament",
            "in": "path",
            "required": true,
            "description": "PL or PB.",
            "schema": {
              "type": "string",
              "example": "PB"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "abbr": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "chapter_count": {
                            "type": "number"
                          },
                          "testament": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 40,
                      "abbr": "Mat",
                      "name": "Matius",
                      "chapter_count": 28,
                      "testament": "PB"
                    },
                    {
                      "id": 41,
                      "abbr": "Mrk",
                      "name": "Markus",
                      "chapter_count": 16,
                      "testament": "PB"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alkitab/passage/{bookAbbr}/{chapter}": {
      "get": {
        "tags": [
          "alkitab"
        ],
        "operationId": "alkitab-passage",
        "summary": "Read a chapter",
        "description": "Returns all verses in a chapter. Use book abbreviation (e.g. Gen, John, Ps) or full name.",
        "parameters": [
          {
            "name": "bookAbbr",
            "in": "path",
            "required": true,
            "description": "Book abbreviation or name, e.g. Kej, Yoh, Mzm.",
            "schema": {
              "type": "string",
              "example": "Kej"
            }
          },
          {
            "name": "chapter",
            "in": "path",
            "required": true,
            "description": "Chapter number.",
            "schema": {
              "type": "integer",
              "example": 1
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "book": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "number"
                            },
                            "abbr": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            }
                          }
                        },
                        "chapter": {
                          "type": "number"
                        },
                        "total_verses": {
                          "type": "number"
                        },
                        "verses": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "verse_number": {
                                "type": "number"
                              },
                              "title": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "text": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "book": {
                      "id": 1,
                      "abbr": "Kej",
                      "name": "Kejadian"
                    },
                    "chapter": 1,
                    "total_verses": 31,
                    "verses": [
                      {
                        "verse_number": 1,
                        "title": null,
                        "text": "Pada mulanya Allah menciptakan langit dan bumi."
                      },
                      {
                        "verse_number": 2,
                        "title": null,
                        "text": "Bumi itu batal dan kosong, gelap gulita menutupi samudera raya..."
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alkitab/verse/{bookAbbr}/{chapter}/{verse}": {
      "get": {
        "tags": [
          "alkitab"
        ],
        "operationId": "alkitab-verse",
        "summary": "Read a single verse",
        "description": "Returns a single verse by book, chapter, and verse number.",
        "parameters": [
          {
            "name": "bookAbbr",
            "in": "path",
            "required": true,
            "description": "Book abbreviation or name.",
            "schema": {
              "type": "string",
              "example": "Yoh"
            }
          },
          {
            "name": "chapter",
            "in": "path",
            "required": true,
            "description": "Chapter number.",
            "schema": {
              "type": "integer",
              "example": 3
            }
          },
          {
            "name": "verse",
            "in": "path",
            "required": true,
            "description": "Verse number.",
            "schema": {
              "type": "integer",
              "example": 16
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "book": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "number"
                            },
                            "abbr": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            }
                          }
                        },
                        "chapter": {
                          "type": "number"
                        },
                        "verse_number": {
                          "type": "number"
                        },
                        "title": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "text": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "book": {
                      "id": 43,
                      "abbr": "Yoh",
                      "name": "Yohanes"
                    },
                    "chapter": 3,
                    "verse_number": 16,
                    "title": null,
                    "text": "Karena begitu besar kasih Allah akan dunia ini, sehingga Ia telah mengaruniakan Anak-Nya yang tunggal, supaya setiap orang yang percaya kepada-Nya tidak binasa, melainkan beroleh hidup yang kekal."
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/alkitab/search": {
      "get": {
        "tags": [
          "alkitab"
        ],
        "operationId": "alkitab-search",
        "summary": "Search verses",
        "description": "Search for a word or phrase across all 31,104 Bible verses. Supports pagination via limit and offset.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword, minimum 3 characters.",
            "schema": {
              "type": "string",
              "example": "Imanuel"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per page. Defaults to 20, maximum 100.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Pagination offset. Defaults to 0.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "verse_number": {
                            "type": "number"
                          },
                          "title": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "text": {
                            "type": "string"
                          },
                          "abbr": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "chapter": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "verse_number": 14,
                      "title": null,
                      "text": "Sebab itu Tuhan sendirilah yang akan memberikan kepadamu suatu pertanda: Sesungguhnya, seorang perawan akan mengandung dan melahirkan seorang anak laki-laki, dan ia akan menamakan Dia Imanuel.",
                      "abbr": "Yes",
                      "name": "Yesaya",
                      "chapter": 7
                    }
                  ],
                  "meta": {
                    "total": 3,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kbbi/search": {
      "get": {
        "tags": [
          "kbbi"
        ],
        "operationId": "kbbi-search",
        "summary": "Search words",
        "description": "Search KBBI dictionary by keyword. Supports pagination.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword.",
            "schema": {
              "type": "string",
              "example": "rumah"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number (default 1).",
            "schema": {
              "type": "integer",
              "example": 1,
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Items per page (default 20, max 100).",
            "schema": {
              "type": "integer",
              "example": 5,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "word": {
                            "type": "string"
                          },
                          "arti": {
                            "type": "string"
                          },
                          "type": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 50001,
                      "word": "rumah",
                      "arti": "Nomina (kata benda)\n(1) bangunan untuk tempat tinggal manusia",
                      "type": 2
                    }
                  ],
                  "meta": {
                    "total": 15,
                    "page": 1,
                    "per_page": 5,
                    "total_pages": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kbbi/word/{word}": {
      "get": {
        "tags": [
          "kbbi"
        ],
        "operationId": "kbbi-word",
        "summary": "Look up a word",
        "description": "Returns all KBBI entries for a specific word (some words have multiple entries).",
        "parameters": [
          {
            "name": "word",
            "in": "path",
            "required": true,
            "description": "Word to look up, e.g. rumah.",
            "schema": {
              "type": "string",
              "example": "rumah"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "word": {
                            "type": "string"
                          },
                          "arti": {
                            "type": "string"
                          },
                          "type": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 50001,
                      "word": "rumah",
                      "arti": "Nomina (kata benda)\n(1) bangunan untuk tempat tinggal manusia",
                      "type": 2
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kbbi/random": {
      "get": {
        "tags": [
          "kbbi"
        ],
        "operationId": "kbbi-random",
        "summary": "Random words",
        "description": "Returns random words from KBBI. Useful for games or learning apps.",
        "parameters": [
          {
            "name": "count",
            "in": "query",
            "required": false,
            "description": "Number of words (default 1, max 20).",
            "schema": {
              "type": "integer",
              "example": 3
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "word": {
                            "type": "string"
                          },
                          "arti": {
                            "type": "string"
                          },
                          "type": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 12345,
                      "word": "budaya",
                      "arti": "Nomina (kata benda)\n(1) pikiran; akal budi",
                      "type": 2
                    },
                    {
                      "id": 67890,
                      "word": "karya",
                      "arti": "Nomina (kata benda)\n(1) hasil perbuatan; cipta",
                      "type": 2
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/plates": {
      "get": {
        "tags": [
          "plat-nomor"
        ],
        "operationId": "plates-list",
        "summary": "List vehicle plates",
        "description": "Returns all 56 vehicle plate codes, sorted by code.",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "plate_code": {
                            "type": "string"
                          },
                          "region": {
                            "type": "string"
                          },
                          "province": {
                            "type": "string"
                          },
                          "cities": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "plate_B",
                      "plate_code": "B",
                      "region": "Jakarta",
                      "province": "DKI Jakarta",
                      "cities": "Jakarta Pusat, Jakarta Utara, Jakarta Barat, Jakarta Selatan, Jakarta Timur, Kepulauan Seribu",
                      "is_active": 1
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/plates/{code}": {
      "get": {
        "tags": [
          "plat-nomor"
        ],
        "operationId": "plates-detail",
        "summary": "Retrieve a plate code",
        "description": "Returns details for a single plate code.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Plate code, e.g. B, DK, L.",
            "schema": {
              "type": "string",
              "example": "B"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "plate_code": {
                          "type": "string"
                        },
                        "region": {
                          "type": "string"
                        },
                        "province": {
                          "type": "string"
                        },
                        "cities": {
                          "type": "string"
                        },
                        "is_active": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "plate_B",
                    "plate_code": "B",
                    "region": "Jakarta",
                    "province": "DKI Jakarta",
                    "cities": "Jakarta Pusat, Jakarta Utara, Jakarta Barat, Jakarta Selatan, Jakarta Timur, Kepulauan Seribu",
                    "is_active": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ump": {
      "get": {
        "tags": [
          "ump"
        ],
        "operationId": "ump-list",
        "summary": "List UMP (latest first)",
        "description": "All provincial minimum wage records. Use query params to filter.",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "required": false,
            "description": "Specific year (1997–2025)",
            "schema": {
              "type": "number",
              "example": 2025
            }
          },
          {
            "name": "province_name",
            "in": "query",
            "required": false,
            "description": "Province name (case-insensitive, partial)",
            "schema": {
              "type": "string",
              "example": "jawa"
            }
          },
          {
            "name": "min_salary",
            "in": "query",
            "required": false,
            "description": "Minimum salary (IDR)",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "max_salary",
            "in": "query",
            "required": false,
            "description": "Maximum salary (IDR)",
            "schema": {
              "type": "number"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "province_code": {
                            "type": "string"
                          },
                          "province_name": {
                            "type": "string"
                          },
                          "year": {
                            "type": "number"
                          },
                          "salary": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "ump-32-2025",
                      "province_code": "32",
                      "province_name": "JAWA BARAT",
                      "year": 2025,
                      "salary": 2500000
                    },
                    {
                      "id": "ump-35-2025",
                      "province_code": "35",
                      "province_name": "JAWA TIMUR",
                      "year": 2025,
                      "salary": 2305984
                    }
                  ],
                  "meta": {
                    "total": 2,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ump/{province_code}": {
      "get": {
        "tags": [
          "ump"
        ],
        "operationId": "ump-by-province",
        "summary": "UMP per province (all years)",
        "description": "All UMP records for one province, sorted by latest year.",
        "parameters": [
          {
            "name": "province_code",
            "in": "path",
            "required": true,
            "description": "Province code (e.g., 32 = West Java)",
            "schema": {
              "type": "string",
              "example": "32"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "province_code": {
                            "type": "string"
                          },
                          "province_name": {
                            "type": "string"
                          },
                          "year": {
                            "type": "number"
                          },
                          "salary": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "ump-32-2025",
                      "province_code": "32",
                      "province_name": "JAWA BARAT",
                      "year": 2025,
                      "salary": 2500000
                    },
                    {
                      "id": "ump-32-2024",
                      "province_code": "32",
                      "province_name": "JAWA BARAT",
                      "year": 2024,
                      "salary": 2200000
                    }
                  ],
                  "meta": {
                    "total": 2,
                    "page": 1,
                    "per_page": 20,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ump/{province_code}/{year}": {
      "get": {
        "tags": [
          "ump"
        ],
        "operationId": "ump-by-province-year",
        "summary": "UMP province × year",
        "description": "A single UMP record for a specific province and year.",
        "parameters": [
          {
            "name": "province_code",
            "in": "path",
            "required": true,
            "description": "Province code",
            "schema": {
              "type": "string",
              "example": "32"
            }
          },
          {
            "name": "year",
            "in": "path",
            "required": true,
            "description": "Year (1997–2025)",
            "schema": {
              "type": "number",
              "example": 2025
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "province_code": {
                          "type": "string"
                        },
                        "province_name": {
                          "type": "string"
                        },
                        "year": {
                          "type": "number"
                        },
                        "salary": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "ump-32-2025",
                    "province_code": "32",
                    "province_name": "JAWA BARAT",
                    "year": 2025,
                    "salary": 2500000
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/validasi/bank": {
      "get": {
        "tags": [
          "validasi"
        ],
        "operationId": "validasi-bank-list",
        "summary": "List banks",
        "description": "Returns bank codes with valid account number lengths.",
        "parameters": [],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "short_name": {
                            "type": "string"
                          },
                          "min_account_length": {
                            "type": "number"
                          },
                          "max_account_length": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "code": "014",
                      "name": "Bank BCA",
                      "short_name": "BCA",
                      "min_account_length": 10,
                      "max_account_length": 10
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/validasi/nik": {
      "post": {
        "tags": [
          "validasi"
        ],
        "operationId": "validasi-nik",
        "summary": "Validate NIK",
        "description": "Checks 16-digit NIK format and extracts region/birth date codes. Not Dukcapil verification.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "nik": {
                    "type": "string",
                    "example": "3201010101900001",
                    "description": "16-digit NIK"
                  }
                },
                "required": [
                  "nik"
                ]
              },
              "example": {
                "nik": "3201010101900001"
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "valid": {
                          "type": "boolean"
                        },
                        "province_code": {
                          "type": "string"
                        },
                        "birth_date": {
                          "type": "string"
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "disclaimer": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "valid": true,
                    "province_code": "32",
                    "birth_date": "1990-01-01"
                  },
                  "meta": {
                    "disclaimer": "Validasi struktural saja..."
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/validasi/npwp": {
      "post": {
        "tags": [
          "validasi"
        ],
        "operationId": "validasi-npwp",
        "summary": "Validate NPWP",
        "description": "Checks 15-digit NPWP format. Not DJP verification.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "npwp": {
                    "type": "string",
                    "example": "01.234.567.8-901.000",
                    "description": "NPWP number"
                  }
                },
                "required": [
                  "npwp"
                ]
              },
              "example": {
                "npwp": "01.234.567.8-901.000"
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "valid": {
                          "type": "boolean"
                        },
                        "formatted": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "valid": true,
                    "formatted": "01.234.567.8-901.000"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/util/rupiah/format": {
      "post": {
        "tags": [
          "util"
        ],
        "operationId": "util-rupiah-format",
        "summary": "Format Rupiah",
        "description": "Formats a number as Indonesian Rupiah string.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "number",
                    "example": 125000,
                    "description": "Amount in IDR"
                  }
                },
                "required": [
                  "amount"
                ]
              },
              "example": {
                "amount": 125000
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "formatted": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "formatted": "Rp125.000",
                    "amount": 125000
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/util/qris/parse": {
      "post": {
        "tags": [
          "util"
        ],
        "operationId": "util-qris-parse",
        "summary": "Parse QRIS",
        "description": "Decodes a QRIS EMVCo payload string.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "payload": {
                    "type": "string",
                    "example": "000201...",
                    "description": "QRIS string"
                  }
                },
                "required": [
                  "payload"
                ]
              },
              "example": {
                "payload": "000201..."
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "merchant_name": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "number"
                        },
                        "currency": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "merchant_name": "TOKO",
                    "amount": 50000,
                    "currency": "360"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kurs-bi/latest": {
      "get": {
        "tags": [
          "kurs-bi"
        ],
        "operationId": "kurs-bi-latest",
        "summary": "Latest rates",
        "description": "Latest transaction rates for USD/EUR/SGD (default) or other currencies.",
        "parameters": [
          {
            "name": "currencies",
            "in": "query",
            "required": false,
            "description": "Comma-separated ISO codes, e.g. USD,EUR,SGD",
            "schema": {
              "type": "string",
              "example": "USD,EUR,SGD"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "buy_rate": {
                            "type": "number"
                          },
                          "sell_rate": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "attribution": {
                          "type": "string"
                        },
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "date": "2026-07-09",
                      "currency": "USD",
                      "buy_rate": 18090,
                      "sell_rate": 18095
                    }
                  ],
                  "meta": {
                    "attribution": "Sumber: Bank Indonesia",
                    "source": "Bank Indonesia"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/kurs-bi/history": {
      "get": {
        "tags": [
          "kurs-bi"
        ],
        "operationId": "kurs-bi-history",
        "summary": "Rate history",
        "description": "Daily rate history for one currency (max 366 days).",
        "parameters": [
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "ISO code, default USD",
            "schema": {
              "type": "string",
              "example": "USD"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string",
              "example": "2026-07-01"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string",
              "example": "2026-07-09"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "mid_rate": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "attribution": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "date": "2026-07-09",
                      "currency": "USD",
                      "mid_rate": 18092.52
                    }
                  ],
                  "meta": {
                    "attribution": "Sumber: Bank Indonesia"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/halal": {
      "get": {
        "tags": [
          "halal"
        ],
        "operationId": "halal-search",
        "summary": "Search halal",
        "description": "Search by business or supervisor name.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Keyword (min. 2 chars)",
            "schema": {
              "type": "string",
              "example": "indomie"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max 200, default 50",
            "schema": {
              "type": "integer",
              "example": 10
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id_penyelia": {
                            "type": "string"
                          },
                          "company_name": {
                            "type": "string"
                          },
                          "supervisor_name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "attribution": {
                          "type": "string"
                        },
                        "refreshed": {
                          "type": "boolean"
                        },
                        "cache_ttl_hours": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id_penyelia": "...",
                      "company_name": "Dapoer indomie",
                      "supervisor_name": "..."
                    }
                  ],
                  "meta": {
                    "attribution": "Sumber: BPJPH",
                    "refreshed": true,
                    "cache_ttl_hours": 24
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/halal/{id}": {
      "get": {
        "tags": [
          "halal"
        ],
        "operationId": "halal-detail",
        "summary": "Supervisor detail",
        "description": "Get one record by id_penyelia UUID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Supervisor UUID",
            "schema": {
              "type": "string",
              "example": "C7A753BB-3282-4D57-A485-B2B724D8267C"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id_penyelia": {
                          "type": "string"
                        },
                        "company_name": {
                          "type": "string"
                        },
                        "issuer": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id_penyelia": "...",
                    "company_name": "...",
                    "issuer": "BPJPH"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/bpom": {
      "get": {
        "tags": [
          "bpom"
        ],
        "operationId": "bpom-search",
        "summary": "Search products",
        "description": "Search registered medicine/traditional/cosmetic products.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Keyword (min. 2 chars)",
            "schema": {
              "type": "string",
              "example": "paracetamol"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "nie": {
                            "type": "string"
                          },
                          "product_name": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "attribution": {
                          "type": "string"
                        },
                        "refreshed": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "nie": "DBL9424502004A1",
                      "product_name": "PANADOL EXTRA",
                      "status": "Berlaku"
                    }
                  ],
                  "meta": {
                    "attribution": "Sumber: BPOM RI",
                    "refreshed": true
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/bpom/{nie}": {
      "get": {
        "tags": [
          "bpom"
        ],
        "operationId": "bpom-detail",
        "summary": "NIE detail",
        "description": "Get product by NIE (registration number).",
        "parameters": [
          {
            "name": "nie",
            "in": "path",
            "required": true,
            "description": "NIE e.g. DBL9424502004A1",
            "schema": {
              "type": "string",
              "example": "DBL9424502004A1"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "nie": {
                          "type": "string"
                        },
                        "product_name": {
                          "type": "string"
                        },
                        "category": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "nie": "DBL9424502004A1",
                    "product_name": "PANADOL EXTRA",
                    "category": "Obat"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/hijriah/konversi": {
      "get": {
        "tags": [
          "hijriah"
        ],
        "operationId": "hijriah-konversi",
        "summary": "Convert date",
        "description": "Convert one Gregorian date to Hijri.",
        "parameters": [
          {
            "name": "tanggal",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD",
            "schema": {
              "type": "string",
              "example": "2026-07-11"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "gregorian_date": {
                          "type": "string"
                        },
                        "hijri_formatted": {
                          "type": "string"
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "disclaimer": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "gregorian_date": "2026-07-11",
                    "hijri_formatted": "26 Muharram 1448 H"
                  },
                  "meta": {
                    "disclaimer": "Berdasarkan Kalender Hijriah Indonesia (hisab MABIMS)..."
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/hijriah/kalender": {
      "get": {
        "tags": [
          "hijriah"
        ],
        "operationId": "hijriah-kalender",
        "summary": "Year calendar",
        "description": "Full date mapping for one Gregorian year (currently 2026).",
        "parameters": [
          {
            "name": "tahun",
            "in": "query",
            "required": true,
            "description": "Gregorian year, e.g. 2026",
            "schema": {
              "type": "integer",
              "example": 2026
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "gregorian_date": {
                            "type": "string"
                          },
                          "hijri_day": {
                            "type": "number"
                          },
                          "hijri_month_name": {
                            "type": "string"
                          },
                          "hijri_year": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "tahun": {
                          "type": "number"
                        },
                        "total_days": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "gregorian_date": "2026-01-01",
                      "hijri_day": 12,
                      "hijri_month_name": "Rajab",
                      "hijri_year": 1447
                    }
                  ],
                  "meta": {
                    "tahun": 2026,
                    "total_days": 365
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/auth/register": {
      "post": {
        "tags": [
          "akun"
        ],
        "operationId": "auth-register",
        "summary": "Register",
        "description": "Creates a new account. This endpoint is protected by Turnstile; use a token from the dashboard. The first API key is generated automatically and returned once in this response.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "example": "anda@perusahaan.com",
                    "description": "Email address, must be unique."
                  },
                  "password": {
                    "type": "string",
                    "example": "rahasia123",
                    "description": "Account password."
                  },
                  "name": {
                    "type": "string",
                    "example": "Budi Santoso",
                    "description": "Full name (optional)."
                  },
                  "turnstileToken": {
                    "type": "string",
                    "example": "0.xxxxx",
                    "description": "Cloudflare Turnstile token from the dashboard form."
                  }
                },
                "required": [
                  "email",
                  "password",
                  "turnstileToken"
                ]
              },
              "example": {
                "email": "anda@perusahaan.com",
                "password": "rahasia123",
                "name": "Budi Santoso",
                "turnstileToken": "0.xxxxx"
              }
            }
          }
        },
        "security": [],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "plan": {
                          "type": "string"
                        }
                      }
                    },
                    "api_key": {
                      "type": "string"
                    },
                    "token": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "user_a1b2c3d4e5f6",
                    "email": "anda@perusahaan.com",
                    "name": "Budi Santoso",
                    "plan": "free"
                  },
                  "api_key": "aip_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/auth/login": {
      "post": {
        "tags": [
          "akun"
        ],
        "operationId": "auth-login",
        "summary": "Log in",
        "description": "Exchanges an email and password for a JWT token. This endpoint is protected by Turnstile; use a token from the dashboard.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "example": "anda@perusahaan.com",
                    "description": "Registered email address."
                  },
                  "password": {
                    "type": "string",
                    "example": "rahasia123",
                    "description": "Account password."
                  },
                  "turnstileToken": {
                    "type": "string",
                    "example": "0.xxxxx",
                    "description": "Cloudflare Turnstile token from the dashboard form."
                  }
                },
                "required": [
                  "email",
                  "password",
                  "turnstileToken"
                ]
              },
              "example": {
                "email": "anda@perusahaan.com",
                "password": "rahasia123",
                "turnstileToken": "0.xxxxx"
              }
            }
          }
        },
        "security": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "plan": {
                          "type": "string"
                        }
                      }
                    },
                    "token": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "user_a1b2c3d4e5f6",
                    "email": "anda@perusahaan.com",
                    "name": "Budi Santoso",
                    "plan": "free"
                  },
                  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/auth/me": {
      "get": {
        "tags": [
          "akun"
        ],
        "operationId": "auth-me",
        "summary": "Get current user",
        "description": "Returns the profile of the currently authenticated user based on the JWT token.",
        "parameters": [],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "plan": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "user_a1b2c3d4e5f6",
                    "email": "anda@perusahaan.com",
                    "name": "Budi Santoso",
                    "plan": "free",
                    "status": "active"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/user/keys": {
      "get": {
        "tags": [
          "api-keys"
        ],
        "operationId": "keys-list",
        "summary": "List API keys",
        "description": "Returns all API keys owned by the user. The full key is never shown again — only its prefix.",
        "parameters": [],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "key_prefix": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "plan": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string"
                          },
                          "last_used_at": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "key_9f8e7d6c5b4a",
                      "key_prefix": "aip_live_Ab1",
                      "name": "Default",
                      "plan": "free",
                      "status": "active",
                      "created_at": "2026-07-01T08:12:44.000Z",
                      "last_used_at": "2026-07-03T02:31:09.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "api-keys"
        ],
        "operationId": "keys-create",
        "summary": "Create an API key",
        "description": "Creates a new API key. The api_key field is returned only once in this response and cannot be retrieved again.",
        "parameters": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Aplikasi Android",
                    "description": "Key name for identification, e.g. \"Android App\". Defaults to \"Default\"."
                  }
                }
              },
              "example": {
                "name": "Aplikasi Android"
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "key_prefix": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "plan": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "api_key": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "key_1a2b3c4d5e6f",
                    "key_prefix": "aip_live_Xy9",
                    "name": "Aplikasi Android",
                    "plan": "free",
                    "status": "active",
                    "api_key": "aip_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/user/keys/{id}": {
      "delete": {
        "tags": [
          "api-keys"
        ],
        "operationId": "keys-revoke",
        "summary": "Revoke an API key",
        "description": "Permanently revokes an API key. Revoked keys are rejected immediately on all endpoints.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID of the API key to revoke.",
            "schema": {
              "type": "string",
              "example": "key_1a2b3c4d5e6f"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "key_1a2b3c4d5e6f",
                    "status": "revoked"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/user/usage": {
      "get": {
        "tags": [
          "api-keys"
        ],
        "operationId": "user-usage",
        "summary": "Usage statistics",
        "description": "Returns monthly, per-product API usage summaries across all of the user's keys.",
        "parameters": [],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "year": {
                            "type": "number"
                          },
                          "month": {
                            "type": "number"
                          },
                          "api_product": {
                            "type": "string"
                          },
                          "total_hits": {
                            "type": "number"
                          },
                          "successful_hits": {
                            "type": "number"
                          },
                          "failed_hits": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "year": 2026,
                      "month": 7,
                      "api_product": "wilayah",
                      "total_hits": 1240,
                      "successful_hits": 1233,
                      "failed_hits": 7
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ojk/products": {
      "get": {
        "tags": [
          "ojk-invest"
        ],
        "operationId": "ojk-products",
        "summary": "List legal investment products",
        "description": "Returns a list of legal mutual fund products registered with OJK. Supports pagination and name search.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Search products by name (fuzzy match).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page (max 200, default 50).",
            "schema": {
              "type": "number",
              "example": 5
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of items to skip (for pagination).",
            "schema": {
              "type": "number"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "name": {
                            "type": "string"
                          },
                          "management": {
                            "type": "string"
                          },
                          "custodian": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "name": "Schroder Dana Likuid",
                      "management": "Schroder Investment Management Indonesia",
                      "custodian": "PT Bank HSBC Indonesia",
                      "type": "Money Market"
                    }
                  ],
                  "meta": {
                    "total": 2189,
                    "page": 1,
                    "per_page": 5,
                    "total_pages": 438
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ojk/products/{id}": {
      "get": {
        "tags": [
          "ojk-invest"
        ],
        "operationId": "ojk-products-id",
        "summary": "Investment product detail",
        "description": "Returns details of a single mutual fund product by ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Product ID.",
            "schema": {
              "type": "number",
              "example": 1
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "number"
                        },
                        "name": {
                          "type": "string"
                        },
                        "management": {
                          "type": "string"
                        },
                        "custodian": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "name": "Schroder Dana Likuid",
                    "management": "Schroder Investment Management Indonesia",
                    "custodian": "PT Bank HSBC Indonesia",
                    "type": "Money Market"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ojk/apps": {
      "get": {
        "tags": [
          "ojk-invest"
        ],
        "operationId": "ojk-apps",
        "summary": "List legal investment apps",
        "description": "Returns a list of legal investment apps registered with OJK. Supports pagination and name search.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Search apps by name (fuzzy match).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page (max 200, default 50).",
            "schema": {
              "type": "number",
              "example": 5
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of items to skip (for pagination).",
            "schema": {
              "type": "number"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "name": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string"
                          },
                          "owner": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "name": "Ajaib",
                      "url": "https://ajaib.co.id",
                      "owner": "Ajaib Sekuritas Asia"
                    }
                  ],
                  "meta": {
                    "total": 93,
                    "page": 1,
                    "per_page": 5,
                    "total_pages": 19
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ojk/apps/{id}": {
      "get": {
        "tags": [
          "ojk-invest"
        ],
        "operationId": "ojk-apps-id",
        "summary": "Investment app detail",
        "description": "Returns details of a single investment app by ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "App ID.",
            "schema": {
              "type": "number",
              "example": 1
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "number"
                        },
                        "name": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string"
                        },
                        "owner": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "name": "Ajaib",
                    "url": "https://ajaib.co.id",
                    "owner": "Ajaib Sekuritas Asia"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ojk/illegals": {
      "get": {
        "tags": [
          "ojk-invest"
        ],
        "operationId": "ojk-illegals",
        "summary": "List illegal investments",
        "description": "Returns a list of illegal investments flagged by OJK. Each entry may include aliases, addresses, phone numbers, emails, websites, entity type, activity types, and a description. Supports pagination and name search.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Search illegal investments by name (fuzzy match).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page (max 200, default 50).",
            "schema": {
              "type": "number",
              "example": 3
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of items to skip (for pagination).",
            "schema": {
              "type": "number"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "number"
                          },
                          "name": {
                            "type": "string"
                          },
                          "alias": {
                            "type": "array",
                            "items": {}
                          },
                          "address": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "phone": {
                            "type": "array",
                            "items": {}
                          },
                          "email": {
                            "type": "array",
                            "items": {}
                          },
                          "web": {
                            "type": "array",
                            "items": {}
                          },
                          "entity_type": {
                            "type": "string"
                          },
                          "activity_type": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "input_date": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "number"
                        },
                        "page": {
                          "type": "number"
                        },
                        "per_page": {
                          "type": "number"
                        },
                        "total_pages": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "name": "PT Jelita Harmoni Mas",
                      "alias": [],
                      "address": [
                        "Jakarta"
                      ],
                      "phone": [],
                      "email": [],
                      "web": [],
                      "entity_type": "Investasi Bodong",
                      "activity_type": [
                        "Penipuan"
                      ],
                      "input_date": "20/01/2020",
                      "description": "SP1/III/INFEST/2019"
                    }
                  ],
                  "meta": {
                    "total": 11278,
                    "page": 1,
                    "per_page": 3,
                    "total_pages": 3760
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/ojk/illegals/{id}": {
      "get": {
        "tags": [
          "ojk-invest"
        ],
        "operationId": "ojk-illegals-id",
        "summary": "Illegal investment detail",
        "description": "Returns details of a single illegal investment by ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Illegal investment ID.",
            "schema": {
              "type": "number",
              "example": 1
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "number"
                        },
                        "name": {
                          "type": "string"
                        },
                        "alias": {
                          "type": "array",
                          "items": {}
                        },
                        "address": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "phone": {
                          "type": "array",
                          "items": {}
                        },
                        "email": {
                          "type": "array",
                          "items": {}
                        },
                        "web": {
                          "type": "array",
                          "items": {}
                        },
                        "entity_type": {
                          "type": "string"
                        },
                        "activity_type": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "input_date": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": 1,
                    "name": "PT Jelita Harmoni Mas",
                    "alias": [],
                    "address": [
                      "Jakarta"
                    ],
                    "phone": [],
                    "email": [],
                    "web": [],
                    "entity_type": "Investasi Bodong",
                    "activity_type": [
                      "Penipuan"
                    ],
                    "input_date": "20/01/2020",
                    "description": "SP1/III/INFEST/2019"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/billing/plans": {
      "get": {
        "tags": [
          "billing"
        ],
        "operationId": "billing-plans",
        "summary": "Plan list",
        "description": "Returns available plans, monthly/yearly pricing, quotas, and rate limits.",
        "parameters": [],
        "security": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "plans": {
                          "type": "object",
                          "properties": {
                            "free": {
                              "type": "object",
                              "properties": {
                                "monthlyPrice": {
                                  "type": "number"
                                },
                                "monthlyQuota": {
                                  "type": "number"
                                }
                              }
                            },
                            "pro": {
                              "type": "object",
                              "properties": {
                                "monthlyPrice": {
                                  "type": "number"
                                },
                                "yearlyPrice": {
                                  "type": "number"
                                },
                                "monthlyQuota": {
                                  "type": "number"
                                }
                              }
                            },
                            "business": {
                              "type": "object",
                              "properties": {
                                "monthlyPrice": {
                                  "type": "number"
                                },
                                "yearlyPrice": {
                                  "type": "number"
                                },
                                "monthlyQuota": {
                                  "type": "number"
                                }
                              }
                            }
                          }
                        },
                        "checkout_plans": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "billing_cycles": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "payg_packages": {
                          "type": "object",
                          "properties": {
                            "starter": {
                              "type": "object",
                              "properties": {
                                "credits": {
                                  "type": "number"
                                },
                                "amount": {
                                  "type": "number"
                                }
                              }
                            },
                            "standard": {
                              "type": "object",
                              "properties": {
                                "credits": {
                                  "type": "number"
                                },
                                "amount": {
                                  "type": "number"
                                }
                              }
                            },
                            "growth": {
                              "type": "object",
                              "properties": {
                                "credits": {
                                  "type": "number"
                                },
                                "amount": {
                                  "type": "number"
                                }
                              }
                            },
                            "scale": {
                              "type": "object",
                              "properties": {
                                "credits": {
                                  "type": "number"
                                },
                                "amount": {
                                  "type": "number"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "plans": {
                      "free": {
                        "monthlyPrice": 0,
                        "monthlyQuota": 1000
                      },
                      "pro": {
                        "monthlyPrice": 49000,
                        "yearlyPrice": 490000,
                        "monthlyQuota": 50000
                      },
                      "business": {
                        "monthlyPrice": 149000,
                        "yearlyPrice": 1490000,
                        "monthlyQuota": 250000
                      }
                    },
                    "checkout_plans": [
                      "pro",
                      "business"
                    ],
                    "billing_cycles": [
                      "monthly",
                      "yearly"
                    ],
                    "payg_packages": {
                      "starter": {
                        "credits": 25000,
                        "amount": 29000
                      },
                      "standard": {
                        "credits": 50000,
                        "amount": 59000
                      },
                      "growth": {
                        "credits": 100000,
                        "amount": 109000
                      },
                      "scale": {
                        "credits": 250000,
                        "amount": 249000
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/billing/subscribe": {
      "post": {
        "tags": [
          "billing"
        ],
        "operationId": "billing-subscribe",
        "summary": "Start checkout",
        "description": "Creates a pending subscription and generates a Sumopod payment link. Redirect the user to redirect_url to complete the payment. Plan credits are granted once the paid webhook arrives.",
        "parameters": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plan": {
                    "type": "string",
                    "example": "pro",
                    "description": "Subscription plan: \"pro\" or \"business\"."
                  },
                  "billing_cycle": {
                    "type": "string",
                    "example": "monthly",
                    "description": "Billing cycle: \"monthly\" or \"yearly\". Default: \"monthly\"."
                  }
                }
              },
              "example": {
                "plan": "pro",
                "billing_cycle": "monthly"
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "subscription_id": {
                          "type": "string"
                        },
                        "invoice_id": {
                          "type": "string"
                        },
                        "plan": {
                          "type": "string"
                        },
                        "billing_cycle": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "number"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "payment_provider": {
                          "type": "string"
                        },
                        "payment_url": {
                          "type": "string"
                        },
                        "redirect_url": {
                          "type": "string"
                        },
                        "payment_id": {
                          "type": "string"
                        },
                        "credits": {
                          "type": "number"
                        },
                        "expires_at": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "subscription_id": "sub_7c1e...",
                    "invoice_id": "inv_2f9a...",
                    "plan": "pro",
                    "billing_cycle": "monthly",
                    "amount": 49000,
                    "currency": "IDR",
                    "payment_provider": "sumopod",
                    "payment_url": "https://pay.sumopod.com/...",
                    "redirect_url": "https://pay.sumopod.com/...",
                    "payment_id": "pay_8b3d...",
                    "credits": 50000,
                    "expires_at": "2026-07-31T08:15:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/billing/status": {
      "get": {
        "tags": [
          "billing"
        ],
        "operationId": "billing-status",
        "summary": "Subscription status",
        "description": "Returns the user's most recent subscription. Returns 404 if the user has never subscribed.",
        "parameters": [],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "user_id": {
                          "type": "string"
                        },
                        "plan": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "started_at": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string"
                        },
                        "auto_renew": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "sub-7c1e...",
                    "user_id": "user_a1b2c3d4e5f6",
                    "plan": "pro",
                    "status": "active",
                    "started_at": "2026-07-01T08:15:00.000Z",
                    "expires_at": "2026-07-31T08:15:00.000Z",
                    "auto_renew": 0
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/billing/webhook": {
      "post": {
        "tags": [
          "billing"
        ],
        "operationId": "billing-webhook",
        "summary": "Sumopod webhook",
        "description": "Payment notification endpoint called by Sumopod (not meant to be called directly). Register this URL as the webhook URL in the Sumopod dashboard. Requests are verified via the x-webhook-token header or a Svix signature (svix-id, svix-timestamp, svix-signature) — either one being valid is enough. When the payment status becomes paid, the invoice is marked paid, the user's plan is upgraded, and credits are granted.",
        "parameters": [],
        "security": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "status": "ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/billing/topup": {
      "post": {
        "tags": [
          "billing"
        ],
        "operationId": "billing-topup",
        "summary": "Buy credits (pay as you go)",
        "description": "Buys a one-off credit package without a subscription. Credits do not expire and are added once the payment is settled.",
        "parameters": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "package_id": {
                    "type": "string",
                    "example": "standard",
                    "description": "Credit package: \"starter\", \"standard\", \"growth\", or \"scale\"."
                  }
                }
              },
              "example": {
                "package_id": "standard"
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "invoice_id": {
                          "type": "string"
                        },
                        "package_id": {
                          "type": "string"
                        },
                        "credits": {
                          "type": "number"
                        },
                        "amount": {
                          "type": "number"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "payment_provider": {
                          "type": "string"
                        },
                        "payment_url": {
                          "type": "string"
                        },
                        "redirect_url": {
                          "type": "string"
                        },
                        "payment_id": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "invoice_id": "inv_2f9a...",
                    "package_id": "standard",
                    "credits": 50000,
                    "amount": 59000,
                    "currency": "IDR",
                    "payment_provider": "sumopod",
                    "payment_url": "https://pay.sumopod.com/...",
                    "redirect_url": "https://pay.sumopod.com/...",
                    "payment_id": "pay_8b3d...",
                    "expires_at": "2026-07-02T08:15:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/billing/credits": {
      "get": {
        "tags": [
          "billing"
        ],
        "operationId": "billing-credits",
        "summary": "Credit balance",
        "description": "Returns the credit balance and the 50 most recent ledger entries. Successful requests beyond the included quota debit 1 credit; failed requests are refunded automatically.",
        "parameters": [],
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "balance": {
                          "type": "number"
                        },
                        "cost_per_successful_request": {
                          "type": "number"
                        },
                        "free_monthly_included_credits": {
                          "type": "number"
                        },
                        "ledger": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string"
                              },
                              "amount": {
                                "type": "number"
                              },
                              "balance_after": {
                                "type": "number"
                              },
                              "description": {
                                "type": "string"
                              },
                              "created_at": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "balance": 48750,
                    "cost_per_successful_request": 1,
                    "free_monthly_included_credits": 1000,
                    "ledger": [
                      {
                        "id": "8f1c...",
                        "type": "usage",
                        "amount": 1,
                        "balance_after": 48750,
                        "description": "GET /api/v1/wilayah/provinces",
                        "created_at": "2026-07-01T09:02:11.000Z"
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/astronomi/sun": {
      "get": {
        "tags": [
          "astronomi"
        ],
        "operationId": "astronomi-sun",
        "summary": "Sunrise & sunset",
        "description": "Returns sunrise, sunset, solar noon, and day length for a given lat/lon and date. Handles polar night / midnight sun.",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "description": "Decimal latitude (-90..90), e.g. -6.2.",
            "schema": {
              "type": "number",
              "example": -6.2
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "description": "Decimal longitude (-180..180), e.g. 106.8.",
            "schema": {
              "type": "number",
              "example": 106.8
            }
          },
          {
            "name": "tanggal",
            "in": "query",
            "required": false,
            "description": "Date in YYYY-MM-DD. Defaults to today.",
            "schema": {
              "type": "string",
              "example": "2025-06-21"
            }
          },
          {
            "name": "tz",
            "in": "query",
            "required": false,
            "description": "ISO timezone offset, e.g. +07:00. Defaults to +00:00.",
            "schema": {
              "type": "string",
              "example": "+07:00"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "date": {
                          "type": "string"
                        },
                        "latitude": {
                          "type": "number"
                        },
                        "longitude": {
                          "type": "number"
                        },
                        "timezone": {
                          "type": "string"
                        },
                        "sunrise": {
                          "type": "string"
                        },
                        "sunset": {
                          "type": "string"
                        },
                        "solarNoon": {
                          "type": "string"
                        },
                        "dayLength": {
                          "type": "number"
                        },
                        "declination": {
                          "type": "number"
                        },
                        "equationOfTime": {
                          "type": "number"
                        },
                        "polarPhase": {
                          "type": "string"
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "disclaimer": {
                          "type": "string"
                        },
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "date": "2025-06-21",
                    "latitude": -6.2,
                    "longitude": 106.8,
                    "timezone": "+07:00",
                    "sunrise": "06:00",
                    "sunset": "17:46",
                    "solarNoon": "11:53",
                    "dayLength": 42343,
                    "declination": 23.44,
                    "equationOfTime": -0.76,
                    "polarPhase": "normal"
                  },
                  "meta": {
                    "disclaimer": "Hasil dihitung dari rumus astronomi publik (NOAA/USNO). Aksen ±2 menit adalah normal untuk terbit/terbenam.",
                    "source": "Computed (NOAA/USNO)"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/astronomi/twilight": {
      "get": {
        "tags": [
          "astronomi"
        ],
        "operationId": "astronomi-twilight",
        "summary": "Twilight times",
        "description": "Returns civil, nautical, and astronomical twilight boundaries for photography/observation.",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "description": "Decimal latitude.",
            "schema": {
              "type": "number",
              "example": -6.2
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "description": "Decimal longitude.",
            "schema": {
              "type": "number",
              "example": 106.8
            }
          },
          {
            "name": "tanggal",
            "in": "query",
            "required": false,
            "description": "Date in YYYY-MM-DD. Defaults to today.",
            "schema": {
              "type": "string",
              "example": "2025-06-21"
            }
          },
          {
            "name": "tz",
            "in": "query",
            "required": false,
            "description": "ISO timezone offset, e.g. +07:00.",
            "schema": {
              "type": "string",
              "example": "+07:00"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "civilDawn": {
                          "type": "string"
                        },
                        "civilDusk": {
                          "type": "string"
                        },
                        "nauticalDawn": {
                          "type": "string"
                        },
                        "nauticalDusk": {
                          "type": "string"
                        },
                        "astronomicalDawn": {
                          "type": "string"
                        },
                        "astronomicalDusk": {
                          "type": "string"
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "disclaimer": {
                          "type": "string"
                        },
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "civilDawn": "05:35",
                    "civilDusk": "18:11",
                    "nauticalDawn": "05:06",
                    "nauticalDusk": "18:40",
                    "astronomicalDawn": "04:37",
                    "astronomicalDusk": "19:09"
                  },
                  "meta": {
                    "disclaimer": "Hasil dihitung dari rumus astronomi publik (NOAA/USNO)...",
                    "source": "Computed (NOAA/USNO)"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/astronomi/moon": {
      "get": {
        "tags": [
          "astronomi"
        ],
        "operationId": "astronomi-moon",
        "summary": "Moon phase",
        "description": "Returns the moon phase (illumination, phase name, age, distance) for a given date.",
        "parameters": [
          {
            "name": "tanggal",
            "in": "query",
            "required": false,
            "description": "Date in YYYY-MM-DD. Defaults to today.",
            "schema": {
              "type": "string",
              "example": "2025-02-12"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "date": {
                          "type": "string"
                        },
                        "illumination": {
                          "type": "number"
                        },
                        "phaseAngle": {
                          "type": "number"
                        },
                        "phaseName": {
                          "type": "string"
                        },
                        "phaseNameEn": {
                          "type": "string"
                        },
                        "age": {
                          "type": "number"
                        },
                        "visiblePercent": {
                          "type": "number"
                        },
                        "trend": {
                          "type": "string"
                        },
                        "distanceEarthRadii": {
                          "type": "number"
                        },
                        "julianDay": {
                          "type": "number"
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "date": "2025-02-12",
                    "illumination": 0.98,
                    "phaseAngle": 178.2,
                    "phaseName": "Bulan Purnama",
                    "phaseNameEn": "Full Moon",
                    "age": 14.7,
                    "visiblePercent": 98,
                    "trend": "waxing",
                    "distanceEarthRadii": 60.3,
                    "julianDay": 2460713.5
                  },
                  "meta": {
                    "source": "Computed (NOAA/USNO)"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/astronomi/moon/next": {
      "get": {
        "tags": [
          "astronomi"
        ],
        "operationId": "astronomi-moon-next",
        "summary": "Upcoming moon phases",
        "description": "Returns the next major moon phases (new/first quarter/full/last quarter), up to 12 ahead.",
        "parameters": [
          {
            "name": "tanggal",
            "in": "query",
            "required": false,
            "description": "Start date in YYYY-MM-DD. Defaults to today.",
            "schema": {
              "type": "string",
              "example": "2025-01-01"
            }
          },
          {
            "name": "count",
            "in": "query",
            "required": false,
            "description": "Number of phases (1–12). Defaults to 4.",
            "schema": {
              "type": "integer",
              "example": 4
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "phase": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "nameEn": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "daysUntil": {
                            "type": "number"
                          },
                          "phaseAngle": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "source": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "phase": "first_quarter",
                      "name": "Kuartil Awal",
                      "nameEn": "First Quarter",
                      "date": "2025-01-05",
                      "daysUntil": 4,
                      "phaseAngle": 90
                    },
                    {
                      "phase": "full",
                      "name": "Bulan Purnama",
                      "nameEn": "Full Moon",
                      "date": "2025-01-13",
                      "daysUntil": 12,
                      "phaseAngle": 180
                    }
                  ],
                  "meta": {
                    "source": "Computed (NOAA/USNO)"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/astronomi/gerhana": {
      "get": {
        "tags": [
          "astronomi"
        ],
        "operationId": "astronomi-gerhana",
        "summary": "Eclipse predictions",
        "description": "Returns upcoming solar & lunar eclipses (approximation based on saros/draconic cycles). Dates & kinds are indicative.",
        "parameters": [
          {
            "name": "tanggal",
            "in": "query",
            "required": false,
            "description": "Start date in YYYY-MM-DD. Defaults to today.",
            "schema": {
              "type": "string",
              "example": "2025-01-01"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Horizon in days (1–1825). Defaults to 365.",
            "schema": {
              "type": "integer",
              "example": 730
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "kindEn": {
                            "type": "string"
                          },
                          "magnitude": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "source": {
                          "type": "string"
                        },
                        "note": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "type": "lunar",
                      "date": "2025-03-14",
                      "kind": "Total",
                      "kindEn": "Total",
                      "magnitude": 0.93
                    },
                    {
                      "type": "solar",
                      "date": "2025-03-29",
                      "kind": "Sebagian",
                      "kindEn": "Partial",
                      "magnitude": 0.71
                    }
                  ],
                  "meta": {
                    "source": "Computed (NOAA/USNO)",
                    "note": "Prediksi aproximasi berbasis siklus saros/draconic. Tanggal & jenis bersifat indikatif."
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/merek/search": {
      "get": {
        "tags": [
          "merek"
        ],
        "operationId": "merek-search",
        "summary": "Search registered trademarks",
        "description": "Returns registered trademarks matching a keyword. Search covers the mark-name column; use optional parameters to filter by owner, NICE class, or search within the owner column instead.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search keyword, e.g. garuda.",
            "schema": {
              "type": "string",
              "example": "garuda"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "example": 1,
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "description": "Filter by trademark owner name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "class",
            "in": "query",
            "required": false,
            "description": "Filter by NICE class, e.g. 35.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "in",
            "in": "query",
            "required": false,
            "description": "Set in=owner to search the keyword within the owner column instead of the mark name.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "app_number": {
                            "type": "string"
                          },
                          "mark_name": {
                            "type": "string"
                          },
                          "class": {
                            "type": "string"
                          },
                          "owner": {
                            "type": "string"
                          },
                          "consultant": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "reg_date": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "page": {
                          "type": "number"
                        },
                        "limit": {
                          "type": "number"
                        },
                        "total": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "app_number": "DID2019050403",
                      "mark_name": "GARUDA SUPER",
                      "class": "16",
                      "owner": "DARWIN EFFENDY",
                      "consultant": null,
                      "reg_date": "2019-09-03"
                    },
                    {
                      "app_number": "JID2019056084",
                      "mark_name": "GARUDA + GAMBAR",
                      "class": "35",
                      "owner": "SURYA CHANDRA",
                      "consultant": "Hendra Prasetya M.Si",
                      "reg_date": "2019-09-24"
                    },
                    {
                      "app_number": "DID2019068788",
                      "mark_name": "GARUDA",
                      "class": "13",
                      "owner": "KURNIA SETIAWAN TANGGUH",
                      "consultant": "Ichwan Anggarawirya S.Sn.",
                      "reg_date": "2019-11-13"
                    },
                    {
                      "app_number": "JID2019068996",
                      "mark_name": "GARUDA TOURBILLON",
                      "class": "39",
                      "owner": "PT. Garuda Indonesia (Persero) Tbk",
                      "consultant": null,
                      "reg_date": "2019-11-13"
                    },
                    {
                      "app_number": "JID2019056818",
                      "mark_name": "GERY X-QUEST FACTORY + lukisan",
                      "class": "41",
                      "owner": "PT Garudafood Putra Putri Jaya Tbk",
                      "consultant": null,
                      "reg_date": "2019-09-26"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 5,
                    "total": 1755
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/merek/{appNumber}": {
      "get": {
        "tags": [
          "merek"
        ],
        "operationId": "merek-detail",
        "summary": "Trademark detail",
        "description": "Returns details of a single trademark by application number. If the number only appears in the publication journal (not yet registered), the response still returns 200 with journal data plus a journal_events list.",
        "parameters": [
          {
            "name": "appNumber",
            "in": "path",
            "required": true,
            "description": "Trademark application number, e.g. DID2019050403.",
            "schema": {
              "type": "string",
              "example": "DID2019050403"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "app_number": {
                          "type": "string"
                        },
                        "mark_name": {
                          "type": "string"
                        },
                        "class": {
                          "type": "string"
                        },
                        "owner": {
                          "type": "string"
                        },
                        "consultant": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "reg_date": {
                          "type": "string"
                        },
                        "journal_events": {
                          "type": "array",
                          "items": {}
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "app_number": "DID2019050403",
                    "mark_name": "GARUDA SUPER",
                    "class": "16",
                    "owner": "DARWIN EFFENDY",
                    "consultant": null,
                    "reg_date": "2019-09-03",
                    "journal_events": []
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/merek/journal": {
      "get": {
        "tags": [
          "merek"
        ],
        "operationId": "merek-journal",
        "summary": "Search the trademark journal",
        "description": "Returns trademark applications published in the DJKI Berita Resmi Merek journal. Coverage is a rolling window of recent editions (roughly twice a month, last few months). Use it to monitor lookalike filings during the opposition window.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search keyword, e.g. garuda.",
            "schema": {
              "type": "string",
              "example": "garuda"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page to retrieve. Defaults to 1.",
            "schema": {
              "type": "integer",
              "example": 1,
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page. Defaults to 20.",
            "schema": {
              "type": "integer",
              "example": 5
            }
          },
          {
            "name": "journal_no",
            "in": "query",
            "required": false,
            "description": "Filter by journal number, e.g. 140/P-M/VII/A/2026.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "app_number": {
                            "type": "string"
                          },
                          "journal_no": {
                            "type": "string"
                          },
                          "journal_date": {
                            "type": "string"
                          },
                          "opposition_end": {
                            "type": "string"
                          },
                          "mark_name": {
                            "type": "string"
                          },
                          "class": {
                            "type": "string"
                          },
                          "owner": {
                            "type": "string"
                          },
                          "filing_date": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "page": {
                          "type": "number"
                        },
                        "limit": {
                          "type": "number"
                        },
                        "total": {
                          "type": "number"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "app_number": "DID2026064189",
                      "journal_no": "140/P-M/VII/A/2026",
                      "journal_date": "2026-07-10",
                      "opposition_end": "2026-09-10",
                      "mark_name": "GLS Garuda Lintas Segoro + Logo",
                      "class": "20",
                      "owner": "LILY",
                      "filing_date": "2026-07-02"
                    },
                    {
                      "app_number": "JID2026033404",
                      "journal_no": "137/P-M/VII/A/2026",
                      "journal_date": "2026-07-06",
                      "opposition_end": "2026-09-06",
                      "mark_name": "Sama Juga Bohong",
                      "class": "41",
                      "owner": "PT Garuda Film",
                      "filing_date": "2026-04-15"
                    },
                    {
                      "app_number": "DID2026061024",
                      "journal_no": "141/P-M/VII/A/2026",
                      "journal_date": "2026-07-10",
                      "opposition_end": "2026-09-10",
                      "mark_name": "GARUDAFLY",
                      "class": "12",
                      "owner": "PT GARUDAFLY AGRI DRONE",
                      "filing_date": "2026-06-24"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 5,
                    "total": 51
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "SumopodWebhookToken": {
        "type": "apiKey",
        "in": "header",
        "name": "x-webhook-token"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "example": "VALIDATION_ERROR"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid credentials",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "QuotaExceeded": {
        "description": "Monthly quota exceeded",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds before retrying"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
