{
  "components": {
    "schemas": {
      "AdminHealthResponse": {
        "description": "Response für GET /v1/admin/health — Service-Liveness der Kernkomponenten.",
        "properties": {
          "checked_at": {
            "format": "date-time",
            "title": "Checked At",
            "type": "string"
          },
          "lod2_db_bl": {
            "title": "Lod2 Db Bl",
            "type": "integer"
          },
          "lod2_mesh_bl": {
            "title": "Lod2 Mesh Bl",
            "type": "integer"
          },
          "probes": {
            "items": {
              "$ref": "#/components/schemas/HealthProbe"
            },
            "title": "Probes",
            "type": "array"
          },
          "status": {
            "title": "Status",
            "type": "string"
          },
          "terrain_mesh_bl": {
            "title": "Terrain Mesh Bl",
            "type": "integer"
          },
          "terrain_tiff_bl": {
            "title": "Terrain Tiff Bl",
            "type": "integer"
          }
        },
        "required": [
          "status",
          "probes",
          "lod2_db_bl",
          "lod2_mesh_bl",
          "terrain_tiff_bl",
          "terrain_mesh_bl",
          "checked_at"
        ],
        "title": "AdminHealthResponse",
        "type": "object"
      },
      "AnonymousUsageReport": {
        "description": "Response for GET /v1/admin/usage/anonymous.\n\nKeyless / free-tier traffic (`api_key_id IS NULL`, ADR-0014). Same buckets\nas the per-key report, minus quota fields (anonymous traffic has no quota).\n`unique_ips` is a rough distinct-caller proxy — anonymous calls carry no\nkey, so IP is the only (NAT-folded) identity signal.",
        "properties": {
          "bl_distribution": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/ApiKeyBlDistribution"
            },
            "title": "Bl Distribution",
            "type": "array"
          },
          "current_month_calls": {
            "title": "Current Month Calls",
            "type": "integer"
          },
          "periods": {
            "items": {
              "$ref": "#/components/schemas/ApiKeyUsagePeriod"
            },
            "title": "Periods",
            "type": "array"
          },
          "top_endpoints": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/ApiKeyTopEndpoint"
            },
            "title": "Top Endpoints",
            "type": "array"
          },
          "total_calls": {
            "title": "Total Calls",
            "type": "integer"
          },
          "unique_ips": {
            "title": "Unique Ips",
            "type": "integer"
          },
          "window_months": {
            "title": "Window Months",
            "type": "integer"
          }
        },
        "required": [
          "window_months",
          "total_calls",
          "current_month_calls",
          "unique_ips",
          "periods"
        ],
        "title": "AnonymousUsageReport",
        "type": "object"
      },
      "ApiKeyBlDistribution": {
        "description": "One BL bucket in a usage report (derived from gmlid prefix).",
        "properties": {
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "call_count": {
            "title": "Call Count",
            "type": "integer"
          }
        },
        "required": [
          "bundesland_code",
          "call_count"
        ],
        "title": "ApiKeyBlDistribution",
        "type": "object"
      },
      "ApiKeyCreateRequest": {
        "description": "Request body for POST /v1/admin/api-keys.\n\nPlaintext key is generated server-side and returned exactly once in the\nresponse — the caller (Konsti, via curl/Postman) hands it to the customer.",
        "properties": {
          "contact_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Contact Email"
          },
          "customer_name": {
            "title": "Customer Name",
            "type": "string"
          },
          "monthly_call_quota": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Monthly Call Quota"
          },
          "notes": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Notes"
          },
          "stripe_customer_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stripe Customer Id"
          },
          "tier": {
            "title": "Tier",
            "type": "string"
          }
        },
        "required": [
          "customer_name",
          "tier"
        ],
        "title": "ApiKeyCreateRequest",
        "type": "object"
      },
      "ApiKeyCreatedResponse": {
        "description": "Response for POST /v1/admin/api-keys — plaintext shown once only.",
        "properties": {
          "api_key_id": {
            "title": "Api Key Id",
            "type": "string"
          },
          "contact_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Contact Email"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "customer_name": {
            "title": "Customer Name",
            "type": "string"
          },
          "key_prefix": {
            "title": "Key Prefix",
            "type": "string"
          },
          "monthly_call_quota": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Monthly Call Quota"
          },
          "notes": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Notes"
          },
          "plaintext_key": {
            "title": "Plaintext Key",
            "type": "string"
          },
          "stripe_customer_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stripe Customer Id"
          },
          "tier": {
            "title": "Tier",
            "type": "string"
          }
        },
        "required": [
          "api_key_id",
          "plaintext_key",
          "key_prefix",
          "customer_name",
          "tier",
          "created_at"
        ],
        "title": "ApiKeyCreatedResponse",
        "type": "object"
      },
      "ApiKeyListItem": {
        "description": "One row in GET /v1/admin/api-keys.",
        "properties": {
          "api_key_id": {
            "title": "Api Key Id",
            "type": "string"
          },
          "contact_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Contact Email"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "customer_name": {
            "title": "Customer Name",
            "type": "string"
          },
          "key_prefix": {
            "title": "Key Prefix",
            "type": "string"
          },
          "last_used_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Used At"
          },
          "monthly_call_quota": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Monthly Call Quota"
          },
          "notes": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Notes"
          },
          "revoked_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Revoked At"
          },
          "stripe_customer_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stripe Customer Id"
          },
          "tier": {
            "title": "Tier",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "api_key_id",
          "key_prefix",
          "customer_name",
          "tier",
          "created_at",
          "updated_at"
        ],
        "title": "ApiKeyListItem",
        "type": "object"
      },
      "ApiKeyListResponse": {
        "properties": {
          "api_keys": {
            "items": {
              "$ref": "#/components/schemas/ApiKeyListItem"
            },
            "title": "Api Keys",
            "type": "array"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          }
        },
        "required": [
          "api_keys",
          "count"
        ],
        "title": "ApiKeyListResponse",
        "type": "object"
      },
      "ApiKeyRevokedResponse": {
        "properties": {
          "api_key_id": {
            "title": "Api Key Id",
            "type": "string"
          },
          "customer_name": {
            "title": "Customer Name",
            "type": "string"
          },
          "key_prefix": {
            "title": "Key Prefix",
            "type": "string"
          },
          "revoked_at": {
            "format": "date-time",
            "title": "Revoked At",
            "type": "string"
          },
          "tier": {
            "title": "Tier",
            "type": "string"
          }
        },
        "required": [
          "api_key_id",
          "key_prefix",
          "customer_name",
          "tier",
          "revoked_at"
        ],
        "title": "ApiKeyRevokedResponse",
        "type": "object"
      },
      "ApiKeyTopEndpoint": {
        "description": "One row in the top-endpoints list of a usage report.",
        "properties": {
          "call_count": {
            "title": "Call Count",
            "type": "integer"
          },
          "endpoint_pattern": {
            "title": "Endpoint Pattern",
            "type": "string"
          },
          "error_count": {
            "title": "Error Count",
            "type": "integer"
          }
        },
        "required": [
          "endpoint_pattern",
          "call_count",
          "error_count"
        ],
        "title": "ApiKeyTopEndpoint",
        "type": "object"
      },
      "ApiKeyUsagePeriod": {
        "description": "One calendar-month bucket in a usage report.\n\n`calls_over_quota` = max(0, call_count - monthly_call_quota); only\nmeaningful when monthly_call_quota is set on the key.",
        "properties": {
          "bytes_sent": {
            "title": "Bytes Sent",
            "type": "integer"
          },
          "call_count": {
            "title": "Call Count",
            "type": "integer"
          },
          "calls_over_quota": {
            "default": 0,
            "title": "Calls Over Quota",
            "type": "integer"
          },
          "error_count": {
            "title": "Error Count",
            "type": "integer"
          },
          "period_start": {
            "format": "date-time",
            "title": "Period Start",
            "type": "string"
          }
        },
        "required": [
          "period_start",
          "call_count",
          "bytes_sent",
          "error_count"
        ],
        "title": "ApiKeyUsagePeriod",
        "type": "object"
      },
      "ApiKeyUsageReport": {
        "description": "Response for GET /v1/admin/api-keys/{id}/usage.\n\n`top_endpoints` and `bl_distribution` aggregate across the report window\n(default last 6 months); `periods` are split per calendar month.",
        "properties": {
          "api_key_id": {
            "title": "Api Key Id",
            "type": "string"
          },
          "bl_distribution": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/ApiKeyBlDistribution"
            },
            "title": "Bl Distribution",
            "type": "array"
          },
          "current_month_calls": {
            "title": "Current Month Calls",
            "type": "integer"
          },
          "customer_name": {
            "title": "Customer Name",
            "type": "string"
          },
          "monthly_call_quota": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Monthly Call Quota"
          },
          "periods": {
            "items": {
              "$ref": "#/components/schemas/ApiKeyUsagePeriod"
            },
            "title": "Periods",
            "type": "array"
          },
          "tier": {
            "title": "Tier",
            "type": "string"
          },
          "top_endpoints": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/ApiKeyTopEndpoint"
            },
            "title": "Top Endpoints",
            "type": "array"
          }
        },
        "required": [
          "api_key_id",
          "customer_name",
          "tier",
          "current_month_calls",
          "periods"
        ],
        "title": "ApiKeyUsageReport",
        "type": "object"
      },
      "BuildingDetail": {
        "description": "Single-building detail aus surfaces_slim — aggregierte Geometrie aller Surfaces.\n\nAttribute wie Dachform, LoD und ADE-Properties sind im aktuellen\nSlim-Datenbestand nicht verfügbar. gmlid identifiziert eine Surface;\nbuilding_id gruppiert alle Surfaces eines Gebäudes.",
        "properties": {
          "building_id": {
            "title": "Building Id",
            "type": "integer"
          },
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "geometry": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Geometry"
          },
          "gmlid": {
            "title": "Gmlid",
            "type": "string"
          },
          "lod": {
            "default": "LoD2",
            "title": "Lod",
            "type": "string"
          }
        },
        "required": [
          "gmlid",
          "bundesland_code",
          "building_id"
        ],
        "title": "BuildingDetail",
        "type": "object"
      },
      "BuildingRoofResponse": {
        "description": "GET /v1/buildings/{gmlid}/roof — GeoJSON FeatureCollection aller RoofSurfaces.\n\nJedes Feature ist eine con:RoofSurface (objectclass_id 712 in 3DCityDB v5)\nmit Solar-Attributen: Neigung, Ausrichtung, Fläche. Leere FeatureCollection\nist valide (z.B. SL-INSPIRE-Import ohne RoofSurface-Features).",
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "features": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Features",
            "type": "array"
          },
          "type": {
            "default": "FeatureCollection",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "features",
          "count"
        ],
        "title": "BuildingRoofResponse",
        "type": "object"
      },
      "BuildingsBboxAttribution": {
        "description": "Per-BL attribution entry within the lodapi-block of /v1/buildings.\n\nMirrors EXACTLY the dict emitted by ``_build_attribution_block`` in\nmain.py (6 keys), which in turn comes from ``SQL_ATTRIBUTION_FOR_BL``\nagainst ``lodapi_meta.attribution``. Nullability follows that table's DDL\n(see code/dev-env/init/01_create_lodapi_meta.sql): bundesland_code,\nlicense_id, attribution_text are NOT NULL; the two URLs are nullable;\nrequires_attribution is NOT NULL (DEFAULT true). This model is only used\nfor OpenAPI documentation — the handler returns a raw JSONResponse — so it\nMUST stay byte-faithful to the wire form, not the other way round.",
        "properties": {
          "attribution_text": {
            "title": "Attribution Text",
            "type": "string"
          },
          "attribution_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Attribution Url"
          },
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "license_id": {
            "title": "License Id",
            "type": "string"
          },
          "license_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "License Url"
          },
          "requires_attribution": {
            "title": "Requires Attribution",
            "type": "boolean"
          }
        },
        "required": [
          "bundesland_code",
          "license_id",
          "attribution_text",
          "requires_attribution"
        ],
        "title": "BuildingsBboxAttribution",
        "type": "object"
      },
      "BuildingsBboxLodapiMeta": {
        "description": "lodapi-Block der /v1/buildings-FeatureCollection (Pagination + Attribution).",
        "properties": {
          "attribution": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/BuildingsBboxAttribution"
            },
            "title": "Attribution",
            "type": "array"
          },
          "next": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next"
          }
        },
        "title": "BuildingsBboxLodapiMeta",
        "type": "object"
      },
      "BuildingsBboxResponse": {
        "description": "GET /v1/buildings — föderierte GeoJSON-FeatureCollection per WGS84-Bbox.\n\nTop-Level-Bbox spiegelt den Request-Bbox. Cursor-Pagination via\n`lodapi.next` (opak). Pro BL ein Attribution-Eintrag in `lodapi.\nattribution`. Features sind als `list[dict]` typisiert (s. Begründung\nbei `TerrainProfileResponse`).",
        "properties": {
          "bbox": {
            "items": {
              "type": "number"
            },
            "title": "Bbox",
            "type": "array"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "features": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Features",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "lodapi": {
            "$ref": "#/components/schemas/BuildingsBboxLodapiMeta"
          },
          "type": {
            "default": "FeatureCollection",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "bbox",
          "count",
          "limit",
          "features",
          "lodapi"
        ],
        "title": "BuildingsBboxResponse",
        "type": "object"
      },
      "DataFreshnessResponse": {
        "description": "Response für GET /v1/admin/data-freshness — BL × Produkt-Pflegesicht.",
        "properties": {
          "checked_at": {
            "format": "date-time",
            "title": "Checked At",
            "type": "string"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/DataFreshnessRow"
            },
            "title": "Rows",
            "type": "array"
          },
          "stale_threshold_days": {
            "title": "Stale Threshold Days",
            "type": "integer"
          }
        },
        "required": [
          "rows",
          "count",
          "stale_threshold_days",
          "checked_at"
        ],
        "title": "DataFreshnessResponse",
        "type": "object"
      },
      "DataFreshnessRow": {
        "description": "Eine BL × Produkt-Zeile der Frische-Matrix.",
        "properties": {
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "days_since_snapshot": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Days Since Snapshot"
          },
          "feature_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Feature Count"
          },
          "is_live": {
            "title": "Is Live",
            "type": "boolean"
          },
          "is_stale": {
            "title": "Is Stale",
            "type": "boolean"
          },
          "last_built_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Built At"
          },
          "license_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "License Id"
          },
          "product": {
            "title": "Product",
            "type": "string"
          },
          "snapshot_date": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Snapshot Date"
          }
        },
        "required": [
          "product",
          "bundesland_code",
          "is_live",
          "is_stale"
        ],
        "title": "DataFreshnessRow",
        "type": "object"
      },
      "Dataset": {
        "description": "A single LoD2 dataset entry — flattened from lodapi_meta.dataset+attribution.",
        "properties": {
          "building_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Building Count"
          },
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "last_sync": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Sync"
          },
          "license_id": {
            "title": "License Id",
            "type": "string"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "snapshot_date": {
            "anyOf": [
              {
                "format": "date",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Snapshot Date"
          },
          "source_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source Url"
          },
          "validation_pass_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Validation Pass Pct"
          }
        },
        "required": [
          "id",
          "bundesland_code",
          "name",
          "license_id"
        ],
        "title": "Dataset",
        "type": "object"
      },
      "DatasetListResponse": {
        "description": "Response envelope for GET /v1/datasets.",
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "datasets": {
            "items": {
              "$ref": "#/components/schemas/Dataset"
            },
            "title": "Datasets",
            "type": "array"
          }
        },
        "required": [
          "datasets",
          "count"
        ],
        "title": "DatasetListResponse",
        "type": "object"
      },
      "DatasetRegisterRequest": {
        "description": "Request body for POST /v1/admin/datasets.",
        "properties": {
          "building_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Building Count"
          },
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "import_finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Import Finished At"
          },
          "import_started_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Import Started At"
          },
          "schema_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Schema Name"
          },
          "snapshot_date": {
            "format": "date",
            "title": "Snapshot Date",
            "type": "string"
          },
          "source_hash": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source Hash"
          },
          "source_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source Url"
          },
          "tilesets": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/TilesetIn"
            },
            "title": "Tilesets",
            "type": "array"
          },
          "validation_pass_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Validation Pass Pct"
          }
        },
        "required": [
          "bundesland_code",
          "snapshot_date"
        ],
        "title": "DatasetRegisterRequest",
        "type": "object"
      },
      "DatasetRegisterResponse": {
        "description": "Response for POST /v1/admin/datasets — the upserted dataset + its tilesets.",
        "properties": {
          "building_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Building Count"
          },
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "dataset_id": {
            "title": "Dataset Id",
            "type": "string"
          },
          "import_finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Import Finished At"
          },
          "import_started_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Import Started At"
          },
          "snapshot_date": {
            "format": "date",
            "title": "Snapshot Date",
            "type": "string"
          },
          "tilesets": {
            "items": {
              "$ref": "#/components/schemas/TilesetRegistered"
            },
            "title": "Tilesets",
            "type": "array"
          },
          "validation_pass_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Validation Pass Pct"
          }
        },
        "required": [
          "dataset_id",
          "bundesland_code",
          "snapshot_date",
          "tilesets"
        ],
        "title": "DatasetRegisterResponse",
        "type": "object"
      },
      "ElevationResponse": {
        "description": "Response model for GET /v1/terrain/elevation.",
        "properties": {
          "attribution": {
            "title": "Attribution",
            "type": "string"
          },
          "datum": {
            "title": "Datum",
            "type": "string"
          },
          "elevation_m": {
            "title": "Elevation M",
            "type": "number"
          },
          "license": {
            "title": "License",
            "type": "string"
          },
          "snapshot": {
            "title": "Snapshot",
            "type": "string"
          },
          "source_bl": {
            "title": "Source Bl",
            "type": "string"
          },
          "tile_id": {
            "title": "Tile Id",
            "type": "string"
          }
        },
        "required": [
          "elevation_m",
          "datum",
          "source_bl",
          "snapshot",
          "license",
          "attribution",
          "tile_id"
        ],
        "title": "ElevationResponse",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "HealthProbe": {
        "description": "Einzelne Komponenten-Probe im Admin-Health-Report.",
        "properties": {
          "component": {
            "title": "Component",
            "type": "string"
          },
          "detail": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Detail"
          },
          "ok": {
            "title": "Ok",
            "type": "boolean"
          }
        },
        "required": [
          "component",
          "ok"
        ],
        "title": "HealthProbe",
        "type": "object"
      },
      "HealthResponse": {
        "description": "Response model for GET /healthz.",
        "properties": {
          "service": {
            "title": "Service",
            "type": "string"
          },
          "status": {
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status",
          "service"
        ],
        "title": "HealthResponse",
        "type": "object"
      },
      "OsmCoverDataset": {
        "description": "Ein Cover-Dataset-Eintrag (ein BL) in der Discovery-Antwort.",
        "properties": {
          "attribution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Attribution"
          },
          "bbox": {
            "anyOf": [
              {
                "items": {
                  "type": "number"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "WGS84 bbox [minLon, minLat, maxLon, maxLat]",
            "title": "Bbox"
          },
          "built_at": {
            "description": "ISO timestamp",
            "title": "Built At",
            "type": "string"
          },
          "bundesland_code": {
            "examples": [
              "be"
            ],
            "title": "Bundesland Code",
            "type": "string"
          },
          "license": {
            "examples": [
              "odbl-1.0"
            ],
            "title": "License",
            "type": "string"
          },
          "snapshot_date": {
            "description": "ISO date",
            "examples": [
              "2026-06-15"
            ],
            "title": "Snapshot Date",
            "type": "string"
          },
          "themes": {
            "description": "Thematische Cluster-Tilesets",
            "items": {
              "$ref": "#/components/schemas/OsmCoverTheme"
            },
            "title": "Themes",
            "type": "array"
          }
        },
        "required": [
          "bundesland_code",
          "snapshot_date",
          "themes",
          "license",
          "built_at"
        ],
        "title": "OsmCoverDataset",
        "type": "object"
      },
      "OsmCoverDatasetListResponse": {
        "properties": {
          "datasets": {
            "items": {
              "$ref": "#/components/schemas/OsmCoverDataset"
            },
            "title": "Datasets",
            "type": "array"
          }
        },
        "required": [
          "datasets"
        ],
        "title": "OsmCoverDatasetListResponse",
        "type": "object"
      },
      "OsmCoverTheme": {
        "description": "Ein thematisches Cluster-Tileset (surface/transport/canopy/sea).",
        "properties": {
          "name": {
            "examples": [
              "surface"
            ],
            "title": "Name",
            "type": "string"
          },
          "tile_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tile Count"
          },
          "tileset_url": {
            "description": "Public URL zum tileset.json",
            "title": "Tileset Url",
            "type": "string"
          }
        },
        "required": [
          "name",
          "tileset_url"
        ],
        "title": "OsmCoverTheme",
        "type": "object"
      },
      "PipelineRun": {
        "description": "Ein Pipeline-Lauf (Import/Tiling/Finalize) je Bundesland.",
        "properties": {
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "failed_tiles": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Failed Tiles"
          },
          "finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Finished At"
          },
          "host": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Host"
          },
          "processed_tiles": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Processed Tiles"
          },
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "slots": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Slots"
          },
          "stage": {
            "title": "Stage",
            "type": "string"
          },
          "started_at": {
            "format": "date-time",
            "title": "Started At",
            "type": "string"
          },
          "status": {
            "title": "Status",
            "type": "string"
          },
          "total_tiles": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Total Tiles"
          }
        },
        "required": [
          "run_id",
          "bundesland_code",
          "stage",
          "status",
          "started_at"
        ],
        "title": "PipelineRun",
        "type": "object"
      },
      "PipelineRunsResponse": {
        "description": "Response für GET /v1/admin/pipeline-runs — laufende + letzte Läufe.",
        "properties": {
          "checked_at": {
            "format": "date-time",
            "title": "Checked At",
            "type": "string"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "running": {
            "title": "Running",
            "type": "integer"
          },
          "runs": {
            "items": {
              "$ref": "#/components/schemas/PipelineRun"
            },
            "title": "Runs",
            "type": "array"
          }
        },
        "required": [
          "runs",
          "count",
          "running",
          "checked_at"
        ],
        "title": "PipelineRunsResponse",
        "type": "object"
      },
      "TerrainDataset": {
        "description": "One terrain dataset entry — most-recent snapshot per Bundesland.",
        "properties": {
          "attribution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Attribution"
          },
          "bl": {
            "title": "Bl",
            "type": "string"
          },
          "coverage_pct": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Coverage Pct"
          },
          "crs": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Crs"
          },
          "format": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Format"
          },
          "license": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "License"
          },
          "snapshot": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Snapshot"
          },
          "source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source"
          },
          "tile_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tile Count"
          },
          "vertical_datum": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Vertical Datum"
          }
        },
        "required": [
          "bl"
        ],
        "title": "TerrainDataset",
        "type": "object"
      },
      "TerrainDatasetListResponse": {
        "description": "Response envelope for GET /v1/terrain/datasets.",
        "properties": {
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "datasets": {
            "items": {
              "$ref": "#/components/schemas/TerrainDataset"
            },
            "title": "Datasets",
            "type": "array"
          }
        },
        "required": [
          "datasets",
          "count"
        ],
        "title": "TerrainDatasetListResponse",
        "type": "object"
      },
      "TerrainMeshDataset": {
        "description": "Ein Mesh-Tileset-Eintrag in der Discovery-Antwort.",
        "properties": {
          "attribution": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Attribution"
          },
          "built_at": {
            "description": "ISO timestamp",
            "title": "Built At",
            "type": "string"
          },
          "bundesland_code": {
            "examples": [
              "be"
            ],
            "title": "Bundesland Code",
            "type": "string"
          },
          "license": {
            "examples": [
              "dl-de-zero-2.0"
            ],
            "title": "License",
            "type": "string"
          },
          "rtin_tolerance_m": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "RTIN-Toleranz aus build_params",
            "title": "Rtin Tolerance M"
          },
          "snapshot_date": {
            "description": "ISO date",
            "examples": [
              "2024-05-12"
            ],
            "title": "Snapshot Date",
            "type": "string"
          },
          "tile_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tile Count"
          },
          "tileset_url": {
            "description": "Public URL zum tileset.json",
            "title": "Tileset Url",
            "type": "string"
          },
          "tiling_scheme": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tiling Scheme"
          }
        },
        "required": [
          "bundesland_code",
          "snapshot_date",
          "tileset_url",
          "license",
          "built_at"
        ],
        "title": "TerrainMeshDataset",
        "type": "object"
      },
      "TerrainMeshDatasetListResponse": {
        "properties": {
          "datasets": {
            "items": {
              "$ref": "#/components/schemas/TerrainMeshDataset"
            },
            "title": "Datasets",
            "type": "array"
          }
        },
        "required": [
          "datasets"
        ],
        "title": "TerrainMeshDatasetListResponse",
        "type": "object"
      },
      "TerrainProfileProperties": {
        "description": "Top-level properties block of the profile FeatureCollection.",
        "properties": {
          "coords_count": {
            "title": "Coords Count",
            "type": "integer"
          },
          "datum": {
            "title": "Datum",
            "type": "string"
          },
          "failed_tile_ids": {
            "default": [],
            "items": {
              "type": "string"
            },
            "title": "Failed Tile Ids",
            "type": "array"
          },
          "partial": {
            "title": "Partial",
            "type": "boolean"
          },
          "samples": {
            "title": "Samples",
            "type": "integer"
          },
          "total_length_m": {
            "title": "Total Length M",
            "type": "number"
          }
        },
        "required": [
          "datum",
          "coords_count",
          "samples",
          "total_length_m",
          "partial"
        ],
        "title": "TerrainProfileProperties",
        "type": "object"
      },
      "TerrainProfileResponse": {
        "description": "GET /v1/terrain/profile — GeoJSON-FeatureCollection mit Sample-Points.\n\nJede Feature ist ein Point in WGS84 mit `elevation_m`, `distance_m`,\n`source_bl`, `tile_id` in den properties. Wir typisieren die Features\nals `list[dict]`, weil GeoJSON-Feature-Schema polymorphisch ist und\nPydantic-Strenge hier mehr Last als Wert wäre.",
        "properties": {
          "features": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Features",
            "type": "array"
          },
          "properties": {
            "$ref": "#/components/schemas/TerrainProfileProperties"
          },
          "type": {
            "default": "FeatureCollection",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "properties",
          "features"
        ],
        "title": "TerrainProfileResponse",
        "type": "object"
      },
      "Tileset": {
        "description": "A single 3D-Tiles tileset entry, with WGS84 bbox-Polygon as GeoJSON.",
        "properties": {
          "bounding_volume": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Bounding Volume"
          },
          "building_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Building Count"
          },
          "bundesland_code": {
            "title": "Bundesland Code",
            "type": "string"
          },
          "generated_at": {
            "format": "date-time",
            "title": "Generated At",
            "type": "string"
          },
          "region_code": {
            "title": "Region Code",
            "type": "string"
          },
          "s3_key": {
            "title": "S3 Key",
            "type": "string"
          },
          "snapshot_date": {
            "anyOf": [
              {
                "format": "date",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Snapshot Date"
          },
          "tile_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tile Count"
          },
          "tileset_id": {
            "title": "Tileset Id",
            "type": "string"
          },
          "tileset_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tileset Url"
          },
          "total_bytes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Total Bytes"
          }
        },
        "required": [
          "tileset_id",
          "region_code",
          "bundesland_code",
          "s3_key",
          "generated_at"
        ],
        "title": "Tileset",
        "type": "object"
      },
      "TilesetIn": {
        "description": "One tileset entry within an admin dataset-registration request.",
        "properties": {
          "bbox_wgs84": {
            "anyOf": [
              {
                "items": {
                  "type": "number"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Bbox Wgs84"
          },
          "region_code": {
            "title": "Region Code",
            "type": "string"
          },
          "s3_key": {
            "title": "S3 Key",
            "type": "string"
          },
          "tile_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tile Count"
          },
          "total_bytes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Total Bytes"
          }
        },
        "required": [
          "region_code",
          "s3_key"
        ],
        "title": "TilesetIn",
        "type": "object"
      },
      "TilesetListLodapiMeta": {
        "description": "lodapi-Block der /v1/tilesets-Antwort — nur Attribution (kein ``next``).\n\nReuses ``BuildingsBboxAttribution`` (identical 6-key entity, same\n``_build_attribution_block`` source). Doc-only model — see\n``TilesetListResponse.lodapi``.",
        "properties": {
          "attribution": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/BuildingsBboxAttribution"
            },
            "title": "Attribution",
            "type": "array"
          }
        },
        "title": "TilesetListLodapiMeta",
        "type": "object"
      },
      "TilesetListResponse": {
        "description": "Response envelope for GET /v1/tilesets?bbox=…",
        "properties": {
          "bbox": {
            "items": {
              "type": "number"
            },
            "title": "Bbox",
            "type": "array"
          },
          "count": {
            "title": "Count",
            "type": "integer"
          },
          "lodapi": {
            "$ref": "#/components/schemas/TilesetListLodapiMeta"
          },
          "tilesets": {
            "items": {
              "$ref": "#/components/schemas/Tileset"
            },
            "title": "Tilesets",
            "type": "array"
          }
        },
        "required": [
          "tilesets",
          "count",
          "bbox",
          "lodapi"
        ],
        "title": "TilesetListResponse",
        "type": "object"
      },
      "TilesetRegistered": {
        "description": "A tileset row as returned by the admin endpoint.",
        "properties": {
          "generated_at": {
            "format": "date-time",
            "title": "Generated At",
            "type": "string"
          },
          "region_code": {
            "title": "Region Code",
            "type": "string"
          },
          "s3_key": {
            "title": "S3 Key",
            "type": "string"
          },
          "tile_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tile Count"
          },
          "tileset_id": {
            "title": "Tileset Id",
            "type": "string"
          },
          "total_bytes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Total Bytes"
          }
        },
        "required": [
          "tileset_id",
          "region_code",
          "s3_key",
          "generated_at"
        ],
        "title": "TilesetRegistered",
        "type": "object"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      }
    }
  },
  "info": {
    "description": "Custom REST endpoints for Lodapi. OGC API Features / 3D GeoVolumes are served by ldproxy (separate service).",
    "title": "Lodapi API",
    "version": "0.1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/healthz": {
      "get": {
        "description": "Return service liveness. Used by Caddy and docker-compose healthchecks.",
        "operationId": "health_check",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Health check",
        "tags": [
          "ops"
        ]
      }
    },
    "/v1/admin/api-keys": {
      "get": {
        "description": "Returns all active customer API keys (revoked ones excluded by default; pass `include_revoked=true` to include them). Plaintext is never returned — only `key_prefix` for human display.",
        "operationId": "admin_list_api_keys",
        "parameters": [
          {
            "in": "query",
            "name": "include_revoked",
            "required": false,
            "schema": {
              "default": false,
              "title": "Include Revoked",
              "type": "boolean"
            }
          },
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List customer API keys",
        "tags": [
          "admin"
        ]
      },
      "post": {
        "description": "Generates a fresh `lod_*` key, stores its SHA-256 hash + prefix, and returns the plaintext **once**. The plaintext is never recoverable — store it immediately or revoke + reissue.\n\nBearer auth via `LODAPI_ADMIN_TOKEN` env-var.",
        "operationId": "admin_create_api_key",
        "parameters": [
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreatedResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Mint a new customer API key (Concierge-Billing-MVP)",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/admin/api-keys/{api_key_id}": {
      "delete": {
        "description": "Marks `revoked_at = now()`. The key stops authenticating immediately (active-only index excludes revoked rows). Idempotent: revoking an already-revoked key returns 404.",
        "operationId": "admin_revoke_api_key",
        "parameters": [
          {
            "in": "path",
            "name": "api_key_id",
            "required": true,
            "schema": {
              "title": "Api Key Id",
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyRevokedResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Revoke a customer API key",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/admin/api-keys/{api_key_id}/usage": {
      "get": {
        "description": "Returns per-calendar-month call count, error count and byte volume for a single key. `current_month_calls` is the running count for the in-progress month (useful to compare against `monthly_call_quota`).",
        "operationId": "admin_api_key_usage",
        "parameters": [
          {
            "in": "path",
            "name": "api_key_id",
            "required": true,
            "schema": {
              "title": "Api Key Id",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "months",
            "required": false,
            "schema": {
              "default": 6,
              "title": "Months",
              "type": "integer"
            }
          },
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyUsageReport"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Per-key monthly usage report (Concierge-Billing-MVP)",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/admin/data-freshness": {
      "get": {
        "description": "Pro Bundesland und Produkt der jüngste Snapshot mit Feature-Count, Lizenz, Import-/Build-Zeitstempel und Staleness-Flag (Tag-1: > 180 Tage = stale). Die Pflege-Kernsicht des Admin-Dashboards. Hinter LODAPI_ADMIN_TOKEN.",
        "operationId": "admin_data_freshness",
        "parameters": [
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataFreshnessResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "BL × Produkt-Frische-Matrix (LoD2 / Terrain / Mesh)",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/admin/datasets": {
      "post": {
        "description": "UPSERT one dataset row + N tileset rows in lodapi_meta. Idempotent: posting the same (bundesland_code, snapshot_date) again updates the existing row rather than creating a duplicate. Requires Bearer token matching LODAPI_ADMIN_TOKEN env-var.",
        "operationId": "admin_register_dataset",
        "parameters": [
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DatasetRegisterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatasetRegisterResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Register (upsert) a dataset and its tilesets",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/admin/health": {
      "get": {
        "description": "Aggregierte Liveness-Probe für das interne Admin-Dashboard: DB-Roundtrip + aktive-BL-Counts je Produkt, HEAD-Probe gegen das Tile-CDN und die Terrain-Storage-Box. `status` = ok / degraded (eine Probe rot) / down (DB rot). Hinter LODAPI_ADMIN_TOKEN.",
        "operationId": "admin_health",
        "parameters": [
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminHealthResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Service-Health der Kernkomponenten (DB / Tiles / Storage-Box)",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/admin/pipeline-runs": {
      "get": {
        "description": "Jüngste Pipeline-Läufe je Bundesland — laufende zuerst. Gespeist von den bin/-Launchern (best-effort via LODAPI_META_DSN). Leere Liste, wenn die pipeline_run-Tabelle (Migration 08_*) noch nicht angelegt ist. Hinter LODAPI_ADMIN_TOKEN.",
        "operationId": "admin_pipeline_runs",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PipelineRunsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Laufende + letzte Pipeline-Läufe (Import / Tiling / Finalize)",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/admin/usage/anonymous": {
      "get": {
        "description": "Aggregates traffic that arrived without an `X-API-Key` header (ADR-0014 anonymous access). Same buckets as the per-key report minus quota fields. `unique_ips` is a rough distinct-caller proxy.",
        "operationId": "admin_anonymous_usage",
        "parameters": [
          {
            "in": "query",
            "name": "months",
            "required": false,
            "schema": {
              "default": 6,
              "title": "Months",
              "type": "integer"
            }
          },
          {
            "in": "header",
            "name": "authorization",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Authorization"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnonymousUsageReport"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Keyless / free-tier usage report (api_key_id IS NULL)",
        "tags": [
          "admin"
        ]
      }
    },
    "/v1/buildings": {
      "get": {
        "description": "GeoJSON FeatureCollection of LoD2 buildings inside a WGS84 bbox, federated across all Bundesländer with `import_finished_at IS NOT NULL`. Cursor pagination via the `cursor` param (opaque, returned in `lodapi.next`); limit max 1000. Each feature has `properties.bundesland`, `surface_id`, `building_id`, `gmlid`, `surface_class`. Top-level `lodapi.attribution[]` carries per-BL license info.\n\n**Note:** Attribute wie Dachform/LoD/ADE-Properties sind im aktuellen Slim-Datenbestand nicht verfügbar. Jede Feature-Row entspricht einer Surface (nicht einem Building-Objekt); `building_id` gruppiert zusammengehörige Surfaces.",
        "operationId": "list_buildings_bbox",
        "parameters": [
          {
            "description": "WGS84 bbox `minLon,minLat,maxLon,maxLat`",
            "in": "query",
            "name": "bbox",
            "required": true,
            "schema": {
              "description": "WGS84 bbox `minLon,minLat,maxLon,maxLat`",
              "examples": [
                "6.95,50.93,6.97,50.94"
              ],
              "title": "Bbox",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Opaque cursor from a previous response's `next` field.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Opaque cursor from a previous response's `next` field.",
              "title": "Cursor"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingsBboxResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Query buildings by bounding box (federated über alle aktiven BL)",
        "tags": [
          "buildings"
        ]
      }
    },
    "/v1/buildings/3d.glb": {
      "get": {
        "description": "Returns one binary glTF (`model/gltf-binary`) that bundles all LoD2 buildings in a WGS84 bbox into a single mesh, ready for Three.js, Blender or any glTF 2.0 renderer.\n\n**Frame:** `target_frame=utm` (Default) liefert UTM-Meter, `target_frame=mercator` liefert MapLibre-Mercator-Scene-Units am Anchor — frame-konsistent zu MapLibre/Mapbox-Renderern, eliminiert Mercator-vs-UTM-Drift und Meridian-Konvergenz.\n\n**Origin:** `origin=center` (Default) = bbox-Mitte; `origin=corner` = bbox-(minLon,minLat). Bei corner bleiben überstehende Gebäude relativ korrekt (leicht negative Vertex-Koordinaten).\n\n**Rotation:** `rotate_x` und `rotate_z` (Grad, intrinsisch) — z. B. `rotate_x=-90&rotate_z=-90` für Blender-Y-up-Workflow.\n\n**Limits:** bbox ≤ 5 km², max 20000 Buildings. Mit `X-Lodapi-Admin-Token`-Header: bbox ≤ 2000 km², max 1000000 Buildings.",
        "operationId": "get_buildings_glb",
        "parameters": [
          {
            "description": "WGS84 bbox `minLon,minLat,maxLon,maxLat`",
            "in": "query",
            "name": "bbox",
            "required": true,
            "schema": {
              "description": "WGS84 bbox `minLon,minLat,maxLon,maxLat`",
              "examples": [
                "13.40,52.51,13.41,52.52"
              ],
              "title": "Bbox",
              "type": "string"
            }
          },
          {
            "description": "Z-Normalisierung: per_building, bbox_min, absolute",
            "in": "query",
            "name": "z_base",
            "required": false,
            "schema": {
              "default": "per_building",
              "description": "Z-Normalisierung: per_building, bbox_min, absolute",
              "title": "Z Base",
              "type": "string"
            }
          },
          {
            "description": "Mesh-Kompression: none, draco",
            "in": "query",
            "name": "compression",
            "required": false,
            "schema": {
              "default": "none",
              "description": "Mesh-Kompression: none, draco",
              "title": "Compression",
              "type": "string"
            }
          },
          {
            "description": "Wall/Ground/Roof in separate Materials einfärben (Roof ziegelrot)",
            "in": "query",
            "name": "colorize_roofs",
            "required": false,
            "schema": {
              "default": false,
              "description": "Wall/Ground/Roof in separate Materials einfärben (Roof ziegelrot)",
              "title": "Colorize Roofs",
              "type": "boolean"
            }
          },
          {
            "description": "Alle Buildings in 1 Mesh + Primitive-pro-Material (drastisch weniger DrawCalls; False für 1 Mesh pro gmlid)",
            "in": "query",
            "name": "merge_buildings",
            "required": false,
            "schema": {
              "default": true,
              "description": "Alle Buildings in 1 Mesh + Primitive-pro-Material (drastisch weniger DrawCalls; False für 1 Mesh pro gmlid)",
              "title": "Merge Buildings",
              "type": "boolean"
            }
          },
          {
            "description": "Zielkoordinaten-Frame: utm, mercator. mercator = MapLibre-Scene-Units am Anchor.",
            "in": "query",
            "name": "target_frame",
            "required": false,
            "schema": {
              "default": "utm",
              "description": "Zielkoordinaten-Frame: utm, mercator. mercator = MapLibre-Scene-Units am Anchor.",
              "title": "Target Frame",
              "type": "string"
            }
          },
          {
            "description": "Anchor-Punkt: center, corner.",
            "in": "query",
            "name": "origin",
            "required": false,
            "schema": {
              "default": "center",
              "description": "Anchor-Punkt: center, corner.",
              "title": "Origin",
              "type": "string"
            }
          },
          {
            "description": "Rotation um X-Achse (Grad, intrinsisch vor Z).",
            "in": "query",
            "name": "rotate_x",
            "required": false,
            "schema": {
              "default": 0.0,
              "description": "Rotation um X-Achse (Grad, intrinsisch vor Z).",
              "title": "Rotate X",
              "type": "number"
            }
          },
          {
            "description": "Rotation um Z-Achse (Grad, intrinsisch nach X).",
            "in": "query",
            "name": "rotate_z",
            "required": false,
            "schema": {
              "default": 0.0,
              "description": "Rotation um Z-Achse (Grad, intrinsisch nach X).",
              "title": "Rotate Z",
              "type": "number"
            }
          },
          {
            "description": "GroundSurface (class 710) im Output behalten. False = weglassen (vermeidet Z-Fighting mit Boden-Layer).",
            "in": "query",
            "name": "include_ground",
            "required": false,
            "schema": {
              "default": true,
              "description": "GroundSurface (class 710) im Output behalten. False = weglassen (vermeidet Z-Fighting mit Boden-Layer).",
              "title": "Include Ground",
              "type": "boolean"
            }
          },
          {
            "description": "Vertex-Merge-Toleranz in Metern (Quantize). 0 = aus; 0.01 schrumpft typische Stadt-Meshes um ~40 %.",
            "in": "query",
            "name": "weld_tolerance_m",
            "required": false,
            "schema": {
              "default": 0.0,
              "description": "Vertex-Merge-Toleranz in Metern (Quantize). 0 = aus; 0.01 schrumpft typische Stadt-Meshes um ~40 %.",
              "maximum": 1.0,
              "minimum": 0.0,
              "title": "Weld Tolerance M",
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              },
              "model/gltf-binary": {}
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Single GLB für eine bbox (z-normalisiert, federated)",
        "tags": [
          "buildings"
        ]
      }
    },
    "/v1/buildings/{gmlid}": {
      "get": {
        "description": "Returns one building with aggregated geometry (all surfaces of the building collected to a single GeoJSON geometry, WGS84). Federated: the BL is inferred from the gmlid prefix; fallback probes all active BL if the prefix is ambiguous.\n\n**Note:** In the current Slim data model, `gmlid` identifies an individual surface rather than a top-level building object. The query aggregates all surfaces sharing the same `building_id`. Attribute wie Dachform/LoD/ADE-Properties sind im aktuellen Slim-Datenbestand nicht verfügbar.",
        "operationId": "get_building_detail",
        "parameters": [
          {
            "in": "path",
            "name": "gmlid",
            "required": true,
            "schema": {
              "title": "Gmlid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingDetail"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Single building detail by GML id (federated)",
        "tags": [
          "buildings"
        ]
      }
    },
    "/v1/buildings/{gmlid}/roof": {
      "get": {
        "description": "Returns a GeoJSON FeatureCollection of all `con:RoofSurface` polygons (objectclass_id 712 in 3DCityDB v5) for a single building, identified by its GML id.\n\nEach feature carries solar attributes derived from the LoD2 geometry:\n- `tilt_deg` — inclination from horizontal (0–90°)\n- `azimuth_deg` — compass bearing of the outward normal (0=N, 90=E, 180=S, 270=W); `null` for near-flat surfaces (tilt < 5°)\n- `area_m2` — projected roof area in m² (UTM, not inclined area)\n\nAn empty FeatureCollection (`count: 0`) is returned for buildings that exist in the dataset but have no RoofSurface features (e.g. SL INSPIRE import, which delivers wall + ground surfaces only).\n\n**404** is returned when the gmlid is not found in any active BL.",
        "operationId": "get_building_roof_surfaces",
        "parameters": [
          {
            "description": "GML id of the building (e.g. 'DEBBAL0100000001')",
            "in": "path",
            "name": "gmlid",
            "required": true,
            "schema": {
              "description": "GML id of the building (e.g. 'DEBBAL0100000001')",
              "title": "Gmlid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingRoofResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "RoofSurfaces with tilt, azimuth and area for solar analysis",
        "tags": [
          "buildings"
        ]
      }
    },
    "/v1/datasets": {
      "get": {
        "description": "Returns one row per active Bundesland with snapshot date, building count, license, source URL and last sync timestamp. Use this to discover which BL are live before issuing bbox queries. `ETag` + `Cache-Control: max-age=300` are emitted; pass `If-None-Match` for 304 short-circuit.",
        "operationId": "list_datasets",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatasetListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List available LoD2 datasets",
        "tags": [
          "datasets"
        ]
      }
    },
    "/v1/osm-cover/datasets": {
      "get": {
        "description": "Pro Bundesland der jüngste Snapshot. Jeder Eintrag bündelt die thematischen Cluster-Tilesets (surface, transport, canopy[, sea]) mit ihren Public-URLs. Tilesets werden statisch unter tiles.lodapi.de/osm-cover/<bl>/<snapshot>/<theme>/tileset.json ausgeliefert. ADR-0017.",
        "operationId": "list_osm_cover_datasets",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OsmCoverDatasetListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Liste aller verfügbaren OSM-Cover-Tilesets (3D-Tiles, thematisch geclustert)",
        "tags": [
          "osm-cover"
        ]
      }
    },
    "/v1/terrain-mesh/datasets": {
      "get": {
        "description": "Pro Bundesland der jüngste Snapshot. Tileset-URLs zeigen auf statisch ausgelieferte tileset.json-Files unter tiles.lodapi.de/terrain-mesh/. ADR-0009 Phase 2 (Amendment 2026-05-13-02).",
        "operationId": "list_terrain_mesh_datasets",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerrainMeshDatasetListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Liste aller verfügbaren Terrain-Mesh-Tilesets (3D-Tiles, b3dm/Draco)",
        "tags": [
          "terrain-mesh"
        ]
      }
    },
    "/v1/terrain/datasets": {
      "get": {
        "description": "Returns one row per Bundesland with the most recent DGM1 snapshot. Use this to discover which BL are live for terrain queries before issuing /elevation or /profile calls. `coverage_pct` is null in Phase-2a (planned for Phase-3 once coverage_polygon analysis is in the pipeline).",
        "operationId": "list_terrain_datasets",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerrainDatasetListResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List available terrain datasets (one per Bundesland, most-recent snapshot)",
        "tags": [
          "terrain"
        ]
      }
    },
    "/v1/terrain/elevation": {
      "get": {
        "description": "Returns the terrain elevation (DHHN2016, m) at a given WGS84 point from the corresponding DGM1 Cloud-Optimized GeoTIFF. PostGIS routes the request to the responsible tile via spatial index across all ingested Bundesländer — no BL is hardcoded. Sampling uses GDAL/rasterio with HTTP Range Requests on the COG (10-50 KB per query). Response includes the responsible BL, snapshot date, license and tile id for caching/audit.",
        "operationId": "get_terrain_elevation",
        "parameters": [
          {
            "description": "Latitude in WGS84 degrees",
            "in": "query",
            "name": "lat",
            "required": true,
            "schema": {
              "description": "Latitude in WGS84 degrees",
              "maximum": 90.0,
              "minimum": -90.0,
              "title": "Lat",
              "type": "number"
            }
          },
          {
            "description": "Longitude in WGS84 degrees",
            "in": "query",
            "name": "lon",
            "required": true,
            "schema": {
              "description": "Longitude in WGS84 degrees",
              "maximum": 180.0,
              "minimum": -180.0,
              "title": "Lon",
              "type": "number"
            }
          },
          {
            "description": "Spatial reference of lat/lon. Currently only EPSG:4326 is supported.",
            "in": "query",
            "name": "srs",
            "required": false,
            "schema": {
              "default": "EPSG:4326",
              "description": "Spatial reference of lat/lon. Currently only EPSG:4326 is supported.",
              "title": "Srs",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ElevationResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Point elevation query from DGM1 COG (BL-agnostic)",
        "tags": [
          "terrain"
        ]
      }
    },
    "/v1/terrain/profile": {
      "get": {
        "description": "Return a GeoJSON FeatureCollection of elevation samples along a polyline.\n\nOutput structure (RFC 7946):\n{\n  \"type\": \"FeatureCollection\",\n  \"properties\": {\n    \"datum\":          \"DHHN2016\",\n    \"coords_count\":   <int>,      // number of input waypoints\n    \"samples\":        <int>,      // actual sample count (= samples param)\n    \"total_length_m\": <float>,    // polyline length in metres (UTM32N Euclidean)\n    \"partial\":        <bool>,     // true if any tile read failed or has NoData\n    \"failed_tile_ids\":[<str>]     // tile_ids where rasterio raised an exception\n  },\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"geometry\": {\"type\": \"Point\", \"coordinates\": [lon, lat]},\n      \"properties\": {\n        \"elevation_m\": <float|null>,  // null = NoData or no tile coverage\n        \"distance_m\":  <float>,       // cumulative metres from start of line\n        \"source_bl\":   <str|null>,    // Bundesland code of the tile, or null\n        \"tile_id\":     <str|null>     // tile identifier, or null\n      }\n    }, ...\n  ]\n}\n\nError behaviour:\n- coords malformed / < 2 points → 400.\n- samples out of range → 422 (FastAPI Query validation).\n- all sample points outside any terrain coverage → 404.\n- partial coverage (≥ 1 valid elevation) → 200 with partial:true.\n- rasterio error on one tile → those points get elevation_m:null,\n  tile_id added to failed_tile_ids, request continues (partial:true).\n- pool/DB unreachable → propagates as 502 from asyncpg.",
        "operationId": "get_terrain_profile",
        "parameters": [
          {
            "description": "Semicolon-separated WGS84 waypoints 'lon1,lat1;lon2,lat2[;...]' (at least 2 points, lon and lat in decimal degrees).",
            "in": "query",
            "name": "coords",
            "required": true,
            "schema": {
              "description": "Semicolon-separated WGS84 waypoints 'lon1,lat1;lon2,lat2[;...]' (at least 2 points, lon and lat in decimal degrees).",
              "title": "Coords",
              "type": "string"
            }
          },
          {
            "description": "Total sample points along the polyline (2–512).",
            "in": "query",
            "name": "samples",
            "required": false,
            "schema": {
              "default": 64,
              "description": "Total sample points along the polyline (2–512).",
              "maximum": 512,
              "minimum": 2,
              "title": "Samples",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerrainProfileResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Line-profile elevation sampling",
        "tags": [
          "terrain"
        ]
      }
    },
    "/v1/tilesets": {
      "get": {
        "description": "Spatial query against `lodapi_meta.tileset.bounding_volume`. Returns 3D-Tiles 1.1 tileset roots with `tileset_url` ready for Cesium `Cesium3DTileset.fromUrl(...)`. Each row includes attribution, building count and snapshot. The top-level `lodapi.attribution[]` array aggregates per-BL license strings.",
        "operationId": "list_tilesets_bbox",
        "parameters": [
          {
            "description": "WGS84 bbox `minLon,minLat,maxLon,maxLat` (degrees)",
            "in": "query",
            "name": "bbox",
            "required": true,
            "schema": {
              "description": "WGS84 bbox `minLon,minLat,maxLon,maxLat` (degrees)",
              "examples": [
                "6.93,50.92,7.02,50.96"
              ],
              "title": "Bbox",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TilesetListResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List 3D-Tiles tilesets intersecting a WGS84 bbox",
        "tags": [
          "tilesets"
        ]
      }
    },
    "/v1/tilesets/{tileset_id}": {
      "get": {
        "operationId": "get_tileset",
        "parameters": [
          {
            "in": "path",
            "name": "tileset_id",
            "required": true,
            "schema": {
              "title": "Tileset Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tileset"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Single 3D-Tiles tileset by id",
        "tags": [
          "tilesets"
        ]
      }
    },
    "/v1/tilesets/{tileset_id}/tileset.json": {
      "get": {
        "description": "302 redirect to the canonical `tileset.json` on `tiles.lodapi.de`. Cesium and three.js 3D-Tiles renderers follow the redirect transparently. Use this when you need a stable API-level URL; for direct asset access, use the `tileset_url` returned by `/v1/tilesets` instead.",
        "operationId": "redirect_tileset_json",
        "parameters": [
          {
            "in": "path",
            "name": "tileset_id",
            "required": true,
            "schema": {
              "title": "Tileset Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Redirect to Cesium 3D-Tiles tileset.json on CDN",
        "tags": [
          "tilesets"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "Production",
      "url": "https://api.lodapi.de"
    },
    {
      "description": "Local development",
      "url": "http://localhost:8000"
    }
  ]
}
