{
  "openapi": "3.1.0",
  "info": {
    "title": "Kahve Tabela Public Discovery API",
    "version": "2026-07-02",
    "description": "Read-only public endpoints for Kahve Tabela heritage discovery, nearby route listening, and place media lookup."
  },
  "servers": [
    {
      "url": "https://kahvetabela.com"
    }
  ],
  "paths": {
    "/api/locations": {
      "get": {
        "summary": "List heritage places by viewport, city, category, and limit.",
        "operationId": "listLocations",
        "parameters": [
          { "name": "s", "in": "query", "schema": { "type": "number" }, "description": "South latitude of the viewport." },
          { "name": "w", "in": "query", "schema": { "type": "number" }, "description": "West longitude of the viewport." },
          { "name": "n", "in": "query", "schema": { "type": "number" }, "description": "North latitude of the viewport." },
          { "name": "e", "in": "query", "schema": { "type": "number" }, "description": "East longitude of the viewport." },
          { "name": "city", "in": "query", "schema": { "type": "string" }, "description": "Uppercase Turkish city name. When present, city filtering is used instead of the bbox." },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "museum",
                "archaeological_site",
                "castle",
                "monument",
                "ruins",
                "mosque",
                "church",
                "tomb",
                "historic_building",
                "natural",
                "other"
              ]
            }
          },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 5000, "default": 1500 } }
        ],
        "responses": {
          "200": {
            "description": "A capped list of matching places.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/LocationListResponse" }
              }
            }
          }
        }
      }
    },
    "/api/nearby": {
      "get": {
        "summary": "Return nearby heritage places for route/listening use.",
        "operationId": "listNearbyLocations",
        "parameters": [
          { "name": "lat", "in": "query", "required": true, "schema": { "type": "number" } },
          { "name": "lon", "in": "query", "required": true, "schema": { "type": "number" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 24, "default": 6 } },
          { "name": "audio", "in": "query", "schema": { "type": "string", "enum": ["1"] }, "description": "When set to 1, returns only narratable places before distance slicing." }
        ],
        "responses": {
          "200": {
            "description": "Nearby places.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/NearbyResponse" }
              }
            }
          },
          "400": {
            "description": "Missing or invalid lat/lon."
          }
        }
      }
    },
    "/api/place-media/{id}": {
      "get": {
        "summary": "Fetch per-place photo fields for a heritage place.",
        "operationId": "getPlaceMedia",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": {
            "description": "Photo metadata for the place.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PlaceMedia" }
              }
            }
          },
          "404": {
            "description": "Place not found."
          }
        }
      }
    },
    "/api/place-audio/{id}": {
      "get": {
        "summary": "Return or redirect to an audio guide for a heritage place.",
        "operationId": "getPlaceAudio",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "lang", "in": "query", "schema": { "type": "string", "enum": ["tr", "en"], "default": "tr" } }
        ],
        "responses": {
          "200": {
            "description": "Generated audio bytes.",
            "content": {
              "audio/mpeg": {
                "schema": {
                  "type": "string",
                  "contentEncoding": "binary"
                }
              }
            }
          },
          "307": {
            "description": "Redirect to cached public audio file."
          },
          "404": {
            "description": "Place not found or no audio is available."
          },
          "503": {
            "description": "Audio generation is not configured."
          }
        }
      }
    },
    "/api/agent/markdown": {
      "get": {
        "summary": "Render a supported Kahve Tabela page as markdown.",
        "operationId": "getAgentMarkdown",
        "parameters": [
          { "name": "path", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Site path such as /tr/place/1 or /en/mavi-koy/yer/gemiler-adasi." }
        ],
        "responses": {
          "200": {
            "description": "Markdown representation.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unsupported or unknown page."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "LocationSummary": {
        "type": "object",
        "required": ["id", "name", "lat", "lon", "category"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "lat": { "type": "number" },
          "lon": { "type": "number" },
          "category": { "type": "string" },
          "image_url": { "type": "string" }
        }
      },
      "LocationListResponse": {
        "type": "object",
        "required": ["results", "total", "capped"],
        "properties": {
          "results": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/LocationSummary" }
          },
          "total": { "type": "integer" },
          "capped": { "type": "boolean" }
        }
      },
      "NearbyResponse": {
        "type": "object",
        "required": ["results"],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "allOf": [
                { "$ref": "#/components/schemas/LocationSummary" },
                {
                  "type": "object",
                  "properties": {
                    "distance": { "type": "number" },
                    "has_audio": { "type": "boolean" }
                  }
                }
              ]
            }
          }
        }
      },
      "PlaceMedia": {
        "type": "object",
        "required": ["image_url", "image_source", "photo_attribution"],
        "properties": {
          "image_url": { "type": ["string", "null"] },
          "image_source": { "type": ["string", "null"] },
          "photo_attribution": {
            "type": ["array", "null"],
            "items": { "type": "string" }
          }
        }
      }
    }
  }
}
