{
  "components": {
    "schemas": {}
  },
  "info": {
    "description": "Incident reports, normalised, geocoded through a local Nominatim and\nstored with PostGIS geography. Every operation below is a projection of\nthe same capability registry that backs the CLI, the MCP server and the\ncockpit.\n",
    "title": "Crisis Map",
    "version": "0.1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/capabilities": {
      "get": {
        "description": "Lists every capability with its parameters, its route, its CLI form and\nits MCP tool name. This is the registry describing itself: the same\nlist that generated the routes, the OpenAPI document and the\ndocumentation is readable at run time.",
        "operationId": "system.capabilities",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Everything this system can do",
                    "value": {
                      "params": {}
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An array of capability descriptions."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An array of capability descriptions."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "The registry itself",
        "tags": [
          "system"
        ]
      }
    },
    "/api/v1/catalog/event-types": {
      "get": {
        "description": "The canonical vocabulary of event types, with the label and map colour\neach one is drawn with. Every other surface reads this list rather than\nkeeping its own copy, which is why a new type appears in the filter,\nthe map legend and the API documentation at once.",
        "operationId": "catalog.event_types",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "The whole vocabulary",
                    "value": {
                      "params": {}
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An array of event type objects."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An array of event type objects."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "The declared event types",
        "tags": [
          "catalog"
        ]
      }
    },
    "/api/v1/catalog/geocoding-statuses": {
      "get": {
        "description": "Every state a position can be in, and whether that state means the\ncoordinates are known. The schema enforces the pairing: a status that\nclaims coordinates exist cannot be stored without a point, and one that\nclaims they do not cannot be stored with one.",
        "operationId": "catalog.geocoding_statuses",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Every status",
                    "value": {
                      "params": {}
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An array of geocoding status objects."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An array of geocoding status objects."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "The declared geocoding statuses",
        "tags": [
          "catalog"
        ]
      }
    },
    "/api/v1/events": {
      "get": {
        "description": "Returns stored events, newest first. Filters are combinable: `type`\nrestricts to one catalog type, `located` keeps only events whose\nposition is known, and `limit` caps the number of rows. Events whose\nposition could not be resolved are returned like any other — they\nsimply carry no coordinates.",
        "operationId": "events.list",
        "parameters": [
          {
            "description": "Restrict to one catalog event type. Omit or pass `all` for every type.",
            "example": "fire",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Keep only events with known coordinates.",
            "example": true,
            "in": "query",
            "name": "located",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Maximum number of rows.",
            "example": 50,
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Every stored event",
                    "value": {
                      "params": {}
                    }
                  },
                  "example2": {
                    "summary": "Only fires that can be drawn on the map",
                    "value": {
                      "params": {
                        "located": true,
                        "type": "fire"
                      }
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An array of event objects."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An array of event objects."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "List stored events",
        "tags": [
          "events"
        ]
      }
    },
    "/api/v1/events/near": {
      "get": {
        "description": "The spatial query proper. Answers \"what happened within X km of here\"\nusing PostGIS `ST_DWithin` on the geography column, which measures on\nthe spheroid in metres and is answered from the GiST index rather than\nby scanning the table. Every returned event also carries its\n`distance_m`, computed by `ST_Distance` in the same query.",
        "operationId": "events.near",
        "parameters": [
          {
            "description": "Latitude of the centre.",
            "example": 50.0755,
            "in": "query",
            "name": "lat",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Longitude of the centre.",
            "example": 14.4378,
            "in": "query",
            "name": "lon",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Search radius in kilometres.",
            "example": 50.0,
            "in": "query",
            "name": "radius_km",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Restrict to one catalog event type. Omit or pass `all` for every type.",
            "example": "fire",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Everything within 50 km of Prague",
                    "value": {
                      "params": {
                        "lat": 50.0755,
                        "lon": 14.4378,
                        "radius_km": 50
                      }
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An array of event objects, each with `distance_m` set."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An array of event objects, each with `distance_m` set."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "Events within a radius of a point",
        "tags": [
          "events",
          "spatial"
        ]
      }
    },
    "/api/v1/events/stats": {
      "get": {
        "description": "The shape of the stored data at a glance: how many events of each\ncatalog type, and how many positions are known, unknown, not found or\nfailed. This is the number the operator looks at after an import.",
        "operationId": "events.stats",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Current counts",
                    "value": {
                      "params": {}
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An object with `total`, `by_type` and `by_geocoding_status`."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An object with `total`, `by_type` and `by_geocoding_status`."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "Counts by type and by geocoding status",
        "tags": [
          "events"
        ]
      }
    },
    "/api/v1/events/{id}": {
      "get": {
        "description": "Returns one event by its database id or by the source's own\n`external_id`. Both are accepted because the two identities answer\ndifferent questions: the database id is ours, the external id is the\nsource's and is what makes the import idempotent.",
        "operationId": "events.get",
        "parameters": [
          {
            "description": "Database id or source external_id.",
            "example": "event-101",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "By source identifier",
                    "value": {
                      "params": {
                        "id": "event-101"
                      }
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "One event object."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One event object."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "Fetch one event",
        "tags": [
          "events"
        ]
      }
    },
    "/api/v1/geocoding/lookup": {
      "get": {
        "description": "Forward geocoding through the configured geocoder, without storing\nanything. The answer carries `candidates` — how many places the\ngeocoder found plausible — and `confidence`, its ranking score for the\none returned, so an ambiguous name is visibly ambiguous.\n\nA place the geocoder does not know is `not_found`, which is an answer,\nnot a failure. A geocoder that cannot be reached is an `error`.",
        "operationId": "geocoding.lookup",
        "parameters": [
          {
            "description": "Place name to resolve.",
            "example": "Beroun",
            "in": "query",
            "name": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "A place the local dataset knows",
                    "value": {
                      "params": {
                        "query": "Beroun"
                      }
                    }
                  },
                  "example2": {
                    "summary": "A place nobody knows",
                    "value": {
                      "params": {
                        "query": "Neznámé místečko u lesa"
                      }
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An object with `status` and, when found, `latitude`, `longitude`, `candidates` and `confidence`."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An object with `status` and, when found, `latitude`, `longitude`, `candidates` and `confidence`."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "Resolve a place name",
        "tags": [
          "geocoding"
        ]
      }
    },
    "/api/v1/geocoding/status": {
      "get": {
        "description": "Asks the configured geocoder whether it is up and which adapter is in\nuse. Returns a reason instead of raising when it is not, so this is\nsafe to call from a status page or a monitor.",
        "operationId": "geocoding.status",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Is the geocoder up?",
                    "value": {
                      "params": {}
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An object describing the geocoder and its reachability."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An object describing the geocoder and its reachability."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "Health of the geocoder",
        "tags": [
          "geocoding",
          "system"
        ]
      }
    },
    "/api/v1/health": {
      "get": {
        "description": "One call that answers whether the parts are there: the database, the\nPostGIS extension and its version, the number of stored events and\nwhether the geocoder answers.",
        "operationId": "system.health",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Is everything up?",
                    "value": {
                      "params": {}
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "An object with the database, PostGIS, event and geocoder status."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An object with the database, PostGIS, event and geocoder status."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "Health of the system",
        "tags": [
          "system"
        ]
      }
    },
    "/api/v1/ingest/import": {
      "post": {
        "description": "Runs the ingest pipeline over a JSON dataset: normalise, upsert on\n`external_id`, geocode what needs geocoding, store the outcome.\n\nRunning it twice is safe and is the point: the second run updates the\nsame rows and reuses positions that are already resolved, so the report\nshows updates and reuses rather than inserts. Pass `regeocode` to\nresolve positions again anyway.",
        "operationId": "ingest.import",
        "parameters": [
          {
            "description": "Dataset to read. Defaults to the bundled dataset.",
            "example": "priv/data/events.json",
            "in": "query",
            "name": "path",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Resolve positions again even when they are already known.",
            "example": null,
            "in": "query",
            "name": "regeocode",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "examples": {
                  "example1": {
                    "summary": "Import the bundled dataset",
                    "value": {
                      "params": {}
                    }
                  }
                },
                "schema": {
                  "properties": {
                    "capability": {
                      "type": "string"
                    },
                    "data": {
                      "description": "The import report: counts per outcome and the errors encountered."
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The import report: counts per outcome and the errors encountered."
          },
          "400": {
            "description": "A parameter failed the capability's own validation."
          },
          "404": {
            "description": "No such capability or no such record."
          }
        },
        "summary": "Import the dataset",
        "tags": [
          "ingest"
        ]
      }
    }
  },
  "tags": [
    {
      "name": "catalog"
    },
    {
      "name": "events"
    },
    {
      "name": "geocoding"
    },
    {
      "name": "ingest"
    },
    {
      "name": "spatial"
    },
    {
      "name": "system"
    }
  ]
}