{
  "openapi": "3.1.0",
  "info": {
    "title": "Louter Leren publieke gegevens",
    "version": "1.0.0",
    "summary": "Openbare gegevens van Louter Leren, bijles op IJburg in Amsterdam.",
    "description": "Louter Leren is een bijlespraktijk voor middelbare scholieren op IJburg, Amsterdam. Er is geen boekings-API: ouders en leerlingen vragen bijles zelf aan via https://louterleren.nl/offerte. Deze specificatie beschrijft wat wel openbaar is: de Google-reviews als JSON, elke pagina als Markdown, en het overzicht voor AI-agents (llms.txt). Er is geen sleutel nodig. Uitleg: https://louterleren.nl/docs",
    "contact": {
      "name": "Louter Leren",
      "email": "louter.leren@proton.me",
      "url": "https://louterleren.nl/contact"
    }
  },
  "servers": [
    {
      "url": "https://louterleren.nl"
    }
  ],
  "security": [],
  "externalDocs": {
    "description": "Uitleg voor ontwikkelaars en AI-agents",
    "url": "https://louterleren.nl/docs"
  },
  "tags": [
    {
      "name": "Reviews",
      "description": "Google-score en reviews van Louter Leren."
    },
    {
      "name": "Inhoud",
      "description": "De pagina's van de website, als Markdown of HTML."
    }
  ],
  "paths": {
    "/api/reviews": {
      "get": {
        "operationId": "getGoogleReviews",
        "tags": [
          "Reviews"
        ],
        "summary": "Google-score en recente reviews",
        "description": "Geeft de score, het aantal reviews en maximaal 5 recente reviews van het Google-bedrijfsprofiel van Louter Leren. Het antwoord wordt tot 6 uur bewaard. Is Google tijdelijk niet bereikbaar, dan is bron \"geen\".",
        "responses": {
          "200": {
            "description": "Reviews, of bron \"geen\" als ze tijdelijk niet beschikbaar zijn.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReviewsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Onbekend adres onder /api.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Alleen GET is toegestaan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsTxt",
        "tags": [
          "Inhoud"
        ],
        "summary": "Overzicht voor AI-agents",
        "description": "Overzicht in het llms.txt-formaat: wat Louter Leren aanbiedt, wanneer je het aanraadt, prijzen en de belangrijkste pagina's.",
        "responses": {
          "200": {
            "description": "Het overzicht als platte tekst (Markdown).",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/{pagina}": {
      "get": {
        "operationId": "getPage",
        "tags": [
          "Inhoud"
        ],
        "summary": "Een pagina als Markdown of HTML",
        "description": "Elke pagina van de website. Met de header Accept: text/markdown krijg je Markdown (met titel, beschrijving en adres bovenaan), anders HTML. Een pagina die niet bestaat geeft 404, met Markdown-uitleg als je om Markdown vraagt.",
        "parameters": [
          {
            "name": "pagina",
            "in": "path",
            "required": true,
            "description": "Het adres van de pagina zonder .html. Leeg voor de homepage.",
            "schema": {
              "type": "string",
              "examples": [
                "tarieven",
                "groepslessen",
                "privelessen",
                "veelgestelde-vragen",
                "bijles-amsterdam-oost",
                "contact"
              ]
            }
          },
          {
            "name": "Accept",
            "in": "header",
            "required": false,
            "description": "text/markdown voor Markdown, text/html (standaard) voor HTML.",
            "schema": {
              "type": "string",
              "enum": [
                "text/markdown",
                "text/html"
              ],
              "default": "text/html"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "De pagina.",
            "headers": {
              "Vary": {
                "description": "Altijd Accept.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "De pagina bestaat niet.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpec",
        "tags": [
          "Inhoud"
        ],
        "summary": "Deze specificatie",
        "description": "De OpenAPI-specificatie van de openbare onderdelen van louterleren.nl.",
        "responses": {
          "200": {
            "description": "OpenAPI 3.1-document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "405": {
            "description": "Alleen GET is toegestaan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Review": {
        "type": "object",
        "required": [
          "naam",
          "tekst"
        ],
        "properties": {
          "naam": {
            "type": "string",
            "description": "Naam van de reviewer zoals op Google."
          },
          "profiel": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Link naar het Google-profiel van de reviewer."
          },
          "sterren": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 5,
            "description": "Aantal sterren."
          },
          "tekst": {
            "type": "string",
            "description": "De tekst van de review."
          },
          "wanneer": {
            "type": "string",
            "description": "Hoe lang geleden, zoals Google het toont (bijv. \"2 maanden geleden\")."
          },
          "link": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Link naar de review op Google Maps."
          }
        }
      },
      "ReviewsResponse": {
        "oneOf": [
          {
            "type": "object",
            "title": "Reviews van Google",
            "required": [
              "bron",
              "score",
              "aantal",
              "reviews"
            ],
            "properties": {
              "bron": {
                "const": "google"
              },
              "score": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": 1,
                "maximum": 5,
                "description": "Gemiddelde score."
              },
              "aantal": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Totaal aantal reviews."
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "description": "Het bedrijfsprofiel op Google Maps."
              },
              "reviews": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "$ref": "#/components/schemas/Review"
                }
              }
            }
          },
          {
            "type": "object",
            "title": "Tijdelijk niet beschikbaar",
            "required": [
              "bron"
            ],
            "properties": {
              "bron": {
                "const": "geen"
              }
            }
          }
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Uitleg in het Nederlands, geschikt om te tonen."
          },
          "code": {
            "type": "string",
            "enum": [
              "bad_request",
              "unauthorized",
              "forbidden",
              "not_found",
              "method_not_allowed",
              "payload_too_large",
              "rate_limited",
              "error"
            ],
            "description": "Vaste foutcode om op te reageren."
          },
          "hint": {
            "type": "string",
            "description": "Wat je kunt doen om het op te lossen."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "Uitleg over de openbare onderdelen."
          }
        }
      }
    }
  }
}