Files
geointel/docs/API_CONTRACTS.md
T
Codex 01f063b921
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled
Add vector change detection foundation
2026-06-16 23:55:52 +02:00

28 KiB

API Contracts v1

This document freezes the first API shape. Codex may add implementation details but must not rename these routes without updating this file and the frontend API client.

API principles

  • Base path: /api/v1.
  • JSON by default.
  • GeoJSON accepted for geometries where possible.
  • Long processing tasks return a job or analysis run record instead of blocking.
  • Error responses use the shared ApiError schema.

Shared schemas

ApiError

{
  "error": "string",
  "message": "human readable message",
  "details": {},
  "request_id": "optional string"
}

GeoJsonGeometry

Any valid GeoJSON geometry object. V1 primarily expects Polygon and MultiPolygon for areas.

BoundingBox

{
  "min_x": 0.0,
  "min_y": 0.0,
  "max_x": 0.0,
  "max_y": 0.0,
  "crs": "EPSG:4326"
}

Health

GET /health

Returns service status.

{
  "status": "ok",
  "service": "geointel-backend",
  "version": "0.1.0"
}

GET /api/v1/system/capabilities

Returns enabled feature flags and tool availability.

{
  "postgis": true,
  "rasterio": true,
  "geopandas": true,
  "yolo": false,
  "sam": false,
  "grb": "planned",
  "sentinel": "planned",
  "providers": [
    {
      "provider_name": "grb",
      "display_name": "GRB",
      "authority_level": "authoritative",
      "supported_layers": ["buildings", "roads", "parcels"],
      "supported_geometry_types": ["Polygon", "MultiPolygon", "LineString", "MultiLineString"],
      "supported_query_modes": ["area"],
      "fetch_signature": "POST /api/v1/external/grb/fetch",
      "configured": false,
      "status": "not_configured",
      "limitation_message": "GRB live WFS/download integration is not configured in Sprint 7B.",
      "attribution": "Digitaal Vlaanderen - Basiskaart Vlaanderen (GRB)",
      "license_note": "Use must follow Digitaal Vlaanderen open data and attribution terms.",
      "not_configured_reason": "Provider integration is not configured yet"
    }
  ]
}

Projects

GET /api/v1/projects

Returns all projects.

POST /api/v1/projects

Request:

{
  "name": "Geel building detection demo",
  "description": "Detect buildings and validate against GRB",
  "region": "Kempen"
}

Response: ProjectRead.

GET /api/v1/projects/{project_id}

Returns one project with summary counts.

PATCH /api/v1/projects/{project_id}

Updates name/description/region.

DELETE /api/v1/projects/{project_id}

Soft-delete in V1 preferred. Hard-delete only if storage cleanup is also implemented.

Areas

GET /api/v1/projects/{project_id}/areas

Returns areas for a project.

POST /api/v1/projects/{project_id}/areas

Request:

{
  "name": "Geel Centrum AOI",
  "geometry": {"type": "Polygon", "coordinates": []},
  "crs": "EPSG:4326"
}

Backend responsibilities:

  • Validate geometry.
  • Repair trivial polygon issues if safe.
  • Store geometry in PostGIS.
  • Calculate area in square meters using projected CRS.
  • Store bbox.

Datasets

POST /api/v1/projects/{project_id}/datasets/upload

Multipart upload.

Fields:

  • file: dataset file.
  • dataset_type: vector, geojson (legacy), raster.
  • source: free text, e.g. user_upload, grb, osm.
  • dataset_role: source, derived, or reference (default source).
  • source_name: optional source identity, e.g. manual, grb, osm; reference uploads default to manual when omitted.
  • reference_layer_name: optional reference layer label, e.g. buildings; only retained for reference datasets.
  • area_id: optional.

Response: DatasetRead with extracted metadata if supported.

Vector uploads remain stored as original files and are also persisted into vector_features as queryable PostGIS state.

GET /api/v1/projects/{project_id}/datasets

List datasets.

GET /api/v1/projects/{project_id}/datasets/{dataset_id}

Return metadata.

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/metadata/refresh

Re-extract metadata.

GET /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/inspect

Return a wrapped vector inspection payload with metadata, storage summary and feature summary.

GET /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/summary

Return vector summary data only.

GET /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/metadata

Return raster metadata profile for supported raster uploads.

If raster processing is unavailable:

code: RASTER_PROCESSING_UNAVAILABLE
message: Raster processing unavailable. Install rasterio and GDAL-compatible drivers to enable raster metadata extraction.

GET /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/inspect

Return raster inspect wrapper payload.

GET /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/stats

Return raster band statistics payload.

If raster processing dependencies are unavailable:

  • code: RASTER_PROCESSING_UNAVAILABLE
  • message: dependency-specific unavailable message.

GET /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/preview

Preview readiness for raster layers.

If preview dependencies are unavailable:

  • code: RASTER_PROCESSING_UNAVAILABLE
  • message: Raster preview unavailable...

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/clip

Clip raster by selected area. Returns a 202-style accepted job payload through the job wrapper (jobs create/read flow).

If raster processing dependencies are unavailable:

  • code: RASTER_PROCESSING_UNAVAILABLE
  • message: Raster processing unavailable. Install rasterio and GDAL-compatible drivers to enable raster processing operations.

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/reproject

Reproject raster dataset to another CRS.

Input:

  • target_crs (default: EPSG:31370)
  • resampling (nearest, bilinear, cubic; default nearest)
  • output_name

Returns a job payload with derived dataset id in result.output_dataset_id.

Failure modes:

  • code: INVALID_PARAMETERS for bad CRS or resampling
  • code: INVALID_DATASET_CRS when source raster CRS is missing
  • code: RASTER_PROCESSING_UNAVAILABLE when rasterio is unavailable

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/indices/ndvi

Compute NDVI from raster band pairs.

Input:

  • nir_band (positive integer, 1-based)
  • red_band (positive integer, 1-based)
  • output_name (optional)

Returns a job payload with derived dataset id in result.output_dataset_id.

Failure modes:

  • code: INVALID_PARAMETERS for non-positive/non-integer band indices
  • code: INVALID_PARAMETERS for band index outside source band count
  • code: INVALID_DATASET_TYPE when source is not raster
  • code: RASTER_PROCESSING_UNAVAILABLE when rasterio or numpy is unavailable

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/indices/ndwi

Compute NDWI from raster band pairs.

Input:

  • nir_band (positive integer, 1-based)
  • green_band (positive integer, 1-based)
  • output_name (optional)

Returns a job payload with derived dataset id in result.output_dataset_id.

Failure modes:

  • code: INVALID_PARAMETERS for non-positive/non-integer band indices
  • code: INVALID_PARAMETERS for band index outside source band count
  • code: INVALID_DATASET_TYPE when source is not raster
  • code: RASTER_PROCESSING_UNAVAILABLE when rasterio or numpy is unavailable

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/indices/ndbi

Compute NDBI from raster band pairs.

Input:

  • nir_band (positive integer, 1-based)
  • swir_band (positive integer, 1-based)
  • output_name (optional)

Returns a job payload with derived dataset id in result.output_dataset_id.

Failure modes:

  • code: INVALID_PARAMETERS for non-positive/non-integer band indices
  • code: INVALID_PARAMETERS for band index outside source band count
  • code: INVALID_DATASET_TYPE when source is not raster
  • code: RASTER_PROCESSING_UNAVAILABLE when rasterio or numpy is unavailable

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/raster/tile

Generate raster tiles and a manifest for downstream processing. Returns a job payload with tile_set_id and manifest metadata.

If raster processing dependencies are unavailable:

  • code: RASTER_PROCESSING_UNAVAILABLE
  • message: Raster processing unavailable. Install rasterio and GDAL-compatible drivers to enable raster processing operations.

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/clip

Clip vector dataset to selected area.

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/buffer

Apply buffer distance to vector features.

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/intersect

Intersect source vector dataset with another vector dataset.

POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/stats

Return vector stats (feature counts and geometry summary).

GET /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/bbox

Return vector bounds and feature count.

Jobs

POST /api/v1/projects/{project_id}/jobs

Create a job.

GET /api/v1/projects/{project_id}/jobs

List jobs.

GET /api/v1/projects/{project_id}/jobs/{job_id}

Read job detail.

GET /api/v1/projects/{project_id}/jobs/{job_id}/status

Read simplified job status payload.

Provider registry

GET /api/v1/external/providers

Returns all configured provider capability descriptors.

GET /api/v1/external/providers/capabilities

Compatibility alias for listing provider capability descriptors.

GET /api/v1/external/providers/{provider_name}

Returns one provider capability descriptor.

GET /api/v1/external/providers/{provider_name}/layers

Returns the supported provider layers.

GET /api/v1/external/providers/{provider_name}/status

Returns configured/status/limitation fields.

POST /api/v1/external/providers/{provider_name}/import

Defines the future provider import contract. Sprint 7B does not perform live imports or write datasets.

Request:

{
  "project_id": "uuid-or-local-id",
  "area_id": "optional uuid-or-local-id",
  "layers": ["buildings"],
  "dataset_role": "optional source|reference"
}

GRB/OSM response:

{
  "provider_name": "grb",
  "status": "not_configured",
  "message": "No live GRB import is configured in Sprint 7B.",
  "requested_layers": ["buildings"],
  "dataset_id": null,
  "dataset_role": "reference",
  "source_name": "grb"
}

Manual and fixture providers point callers to existing upload/fixture flows. No provider writes directly to vector_features; all future provider output must flow through DatasetService and VectorFeatureService.

External data fetchers

POST /api/v1/external/osm/fetch

Request:

{
  "project_id": "uuid",
  "area_id": "uuid",
  "layers": ["buildings", "roads", "water", "green"]
}

POST /api/v1/external/grb/fetch

Request:

{
  "project_id": "uuid",
  "area_id": "uuid",
  "layers": ["buildings"]
}

V1 may initially implement this as a service interface with a clear not_configured response until the exact WFS endpoint is wired.

Sprint 7B provider contract responses expose capabilities only. Providers must report:

{
  "provider_name": "osm",
  "display_name": "OpenStreetMap",
  "authority_level": "contextual",
  "supported_layers": ["buildings", "roads", "water", "landuse"],
  "supported_geometry_types": ["Polygon", "MultiPolygon", "LineString", "MultiLineString"],
  "supported_query_modes": ["area"],
  "configured": false,
  "status": "not_configured",
  "limitation_message": "OSM live Overpass/download integration is not configured in Sprint 7B.",
  "attribution": "OpenStreetMap contributors",
  "license_note": "OpenStreetMap data is available under ODbL; attribution is required."
}

No GRB WFS, OSM Overpass or provider downloads are implemented in Sprint 7B.

Demo workflow

POST /api/v1/demo/workflow

Seeds an explicit offline demo workflow from local fixture files. This endpoint does not fetch live GRB/OSM data and does not run AI inference. It creates or returns:

  • one demo project
  • one demo AOI
  • one fixture reference building dataset
  • one fixture candidate/predicted building dataset
  • one persisted QA/QC result with metric rows

The endpoint is idempotent for the named demo project.

Response:

{
  "project_id": "uuid",
  "area_id": "uuid",
  "reference_dataset_id": "uuid",
  "candidate_dataset_id": "uuid",
  "quality_check_id": "uuid",
  "metric_count": 6,
  "status": "ready",
  "message": "Demo workflow seeded from explicit local fixtures.",
  "created": true
}

Analysis

Detection Lab

Sprint 8 implements Detection Lab foundation only. YOLO/PyTorch real inference is not enabled, no model is downloaded, and fixture detections require explicit fixture mode.

GET /api/v1/detection/models

Returns object-detection model capability descriptors.

{
  "models": [
    {
      "model_id": "yolo-placeholder",
      "display_name": "YOLO detector placeholder",
      "framework": "ultralytics/pytorch",
      "task_type": "object_detection",
      "supported_classes": ["building", "road", "water", "landuse"],
      "configured": false,
      "status": "not_configured",
      "limitation_message": "YOLO/PyTorch inference is not configured in Sprint 8; no model is downloaded or executed.",
      "version": null
    },
    {
      "model_id": "yolo-configured",
      "display_name": "Configured YOLO detector",
      "framework": "ultralytics/pytorch",
      "task_type": "object_detection",
      "supported_classes": ["building", "road", "water", "landuse"],
      "configured": false,
      "status": "not_configured",
      "limitation_message": "YOLO is disabled. Set YOLO_ENABLED=true and YOLO_MODEL_PATH to a local model file to enable inference.",
      "version": null
    }
  ]
}

POST /api/v1/detection/run

Creates a detection job and detection analysis run. If the requested model is unavailable, the job and analysis run are marked failed with DETECTION_MODEL_UNAVAILABLE or DETECTION_DEPENDENCY_UNAVAILABLE.

Request:

{
  "project_id": "uuid",
  "dataset_id": "uuid",
  "model_id": "yolo-placeholder",
  "confidence_threshold": 0.5,
  "class_filter": ["building"],
  "tile_manifest_path": null,
  "parameters_json": {}
}

Sprint 8B configured YOLO mode uses model_id: "yolo-configured". It requires:

  • YOLO_ENABLED=true
  • YOLO_MODEL_PATH pointing to an existing local model file
  • backend optional AI dependencies installed with geointel-backend[ai]
  • tile_manifest_path pointing to an existing raster tile manifest generated by the raster tile operation

GeoIntel does not download model weights automatically. Configured YOLO runs read existing tile files from the manifest, convert YOLO pixel-space boxes to EPSG:4326 detection polygons and persist detections as first-class records.

Unavailable model response:

{
  "analysis_run_id": "uuid",
  "job_id": "uuid",
  "project_id": "uuid",
  "dataset_id": "uuid",
  "model_id": "yolo-placeholder",
  "status": "failed",
  "detection_count": 0,
  "error_code": "DETECTION_MODEL_UNAVAILABLE",
  "message": "YOLO/PyTorch inference is not configured in Sprint 8; no model is downloaded or executed."
}

Validation errors:

  • INVALID_DATASET_TYPE when the dataset is not raster.
  • DETECTION_MODEL_NOT_FOUND when the model id is unknown.
  • FIXTURE_MODE_REQUIRED when manual-fixture-detector is requested without parameters_json.fixture_mode=true.
  • DETECTION_TILE_MANIFEST_REQUIRED when yolo-configured is requested without tile_manifest_path.
  • DETECTION_TILE_MANIFEST_NOT_FOUND when the provided manifest path does not exist.
  • DETECTION_TILE_MANIFEST_INVALID when the manifest cannot be parsed or lacks tile metadata.
  • DETECTION_TILE_LIMIT_EXCEEDED when the manifest exceeds YOLO_MAX_TILES.
  • DETECTION_DEPENDENCY_UNAVAILABLE when YOLO dependencies are not installed.
  • DETECTION_MODEL_LOAD_FAILED when the local model file exists but cannot be loaded.

Fixture detector mode is test/demo-only. It persists only explicit parameters_json.fixture_detections entries and is never invoked automatically.

GET /api/v1/detection/runs/{analysis_run_id}

Returns one detection analysis run.

GET /api/v1/detection/runs

Returns detection analysis runs, optionally filtered by project_id and dataset_id.

GET /api/v1/detection/runs/{analysis_run_id}/detections

Returns persisted detections for a detection analysis run. Optional filters:

  • dataset_id
  • class_name
  • min_confidence

GET /api/v1/detection/datasets/{dataset_id}/detections

Returns persisted detections for a raster dataset. Optional filters:

  • analysis_run_id
  • class_name
  • min_confidence

GET /api/v1/detection/detections/{detection_id}

Returns one persisted detection.

GET /api/v1/detection/runs/{analysis_run_id}/geojson

Returns persisted detections for a run as a GeoJSON FeatureCollection. Geometry comes from persisted PostGIS detection geometry in EPSG:4326.

Each feature includes:

  • detection_id
  • class_name
  • confidence
  • model_name
  • model_version
  • analysis_run_id
  • dataset_id
  • job_id
  • source_tile_path
  • bbox_json

GET /api/v1/detection/datasets/{dataset_id}/geojson

Returns persisted detections for a dataset as a GeoJSON FeatureCollection. Optional filters match the detection list endpoint.

POST /api/v1/detection/runs/{analysis_run_id}/qa/reference

Compares persisted detection geometries from an analysis run against persisted vector_features from a reference vector dataset.

Request:

{
  "reference_dataset_id": "uuid",
  "iou_threshold": 0.5,
  "class_name": "building",
  "min_confidence": 0.5
}

Response persists a quality_check and metrics rows through the existing QA/QC persistence architecture and returns:

  • precision
  • recall
  • f1_score
  • mean_iou
  • false_positives
  • false_negatives
  • quality_check_id

If the reference dataset has no persisted vector features, the endpoint returns REFERENCE_FEATURES_NOT_FOUND. It does not calculate fake QA metrics.

POST /api/v1/analysis/building-stats

Input: area + vector building layer.

POST /api/v1/analysis/object-detection

Request:

{
  "project_id": "uuid",
  "area_id": "uuid",
  "dataset_id": "uuid",
  "model_id": "optional uuid",
  "classes": ["building"],
  "confidence_threshold": 0.35,
  "tile_size": 640,
  "overlap": 64
}

Response: AnalysisRunRead.

POST /api/v1/analysis/segmentation

Same pattern as object detection, but output includes masks and polygonized geometries.

Segmentation Lab

Sprint 9 implements Segmentation Lab foundation only. Real SAM and YOLO-seg inference are not enabled, no model is downloaded, and fixture segmentations require explicit fixture mode.

GET /api/v1/segmentation/models

Returns segmentation model capability descriptors:

  • segmentation-placeholder: not_configured
  • fixture-segmenter: configured for explicit test/demo fixtures only
  • yolo-seg-configured: not_configured
  • sam-configured: not_configured

POST /api/v1/segmentation/run

Creates a segmentation job and segmentation analysis run. If the requested model is unavailable, the job and analysis run are marked failed with SEGMENTATION_MODEL_UNAVAILABLE.

Request:

{
  "project_id": "uuid",
  "dataset_id": "uuid",
  "model_id": "segmentation-placeholder",
  "confidence_threshold": 0.5,
  "class_filter": ["vegetation"],
  "tile_manifest_path": null,
  "parameters_json": {}
}

Fixture segmenter mode is test/demo-only. It persists only explicit parameters_json.fixture_segmentations entries when parameters_json.fixture_mode=true; it is never invoked automatically and does not represent production inference.

Validation errors:

  • INVALID_DATASET_TYPE when the dataset is not raster.
  • SEGMENTATION_MODEL_NOT_FOUND when the model id is unknown.
  • FIXTURE_MODE_REQUIRED when fixture-segmenter is requested without parameters_json.fixture_mode=true.
  • INVALID_FIXTURE_SEGMENTATIONS when fixture payloads are not a list.
  • INVALID_FIXTURE_GEOMETRY when fixture geometry is empty, invalid or not Polygon/MultiPolygon.

GET /api/v1/segmentation/runs

Returns segmentation analysis runs, optionally filtered by project_id and dataset_id.

GET /api/v1/segmentation/runs/{analysis_run_id}

Returns one segmentation analysis run.

GET /api/v1/segmentation/runs/{analysis_run_id}/segmentations

Returns persisted segmentation records for a segmentation analysis run. Optional filters:

  • dataset_id
  • class_name
  • min_confidence

GET /api/v1/segmentation/datasets/{dataset_id}/segmentations

Returns persisted segmentation records for a raster dataset. Optional filters:

  • analysis_run_id
  • class_name
  • min_confidence

GET /api/v1/segmentation/segmentations/{segmentation_id}

Returns one persisted segmentation record.

GET /api/v1/segmentation/runs/{analysis_run_id}/geojson

Returns persisted segmentations for a run as a GeoJSON FeatureCollection. Geometry comes from persisted PostGIS segmentation geometry in EPSG:4326.

Each feature includes:

  • segmentation_id
  • class_name
  • confidence
  • area_m2
  • model_name
  • model_version
  • analysis_run_id
  • dataset_id
  • job_id
  • source_tile_path
  • tile_index
  • mask_path
  • bbox_json
  • provenance_json

GET /api/v1/segmentation/datasets/{dataset_id}/geojson

Returns persisted segmentations for a dataset as a GeoJSON FeatureCollection. Optional filters match the segmentation list endpoint.

POST /api/v1/segmentation/runs/{analysis_run_id}/qa/reference

Compares persisted segmentation geometries from an analysis run against persisted vector_features from a reference vector dataset.

Request:

{
  "reference_dataset_id": "uuid",
  "iou_threshold": 0.5,
  "class_name": "vegetation",
  "min_confidence": 0.5
}

Response persists a quality_check and metrics rows through the existing QA/QC persistence architecture and returns precision, recall, F1, mean IoU and false positive/negative counts.

If the segmentation run has no persisted geometries, the endpoint returns SEGMENTATIONS_NOT_FOUND. If the reference dataset has no persisted vector features, it returns REFERENCE_FEATURES_NOT_FOUND. It does not calculate fake QA metrics.

POST /api/v1/analysis/change-detection

Compares two persisted vector datasets in the same project and returns a synchronous job envelope. This is a lightweight V1 foundation for added/removed object review, not a temporal run-history engine.

Request:

{
  "source_dataset_id": "uuid",
  "target_dataset_id": "uuid",
  "iou_threshold": 0.8,
  "include_unchanged": true
}

Response is a canonical API envelope containing a JobRead payload. On success, result_json contains:

{
  "source_dataset_id": "uuid",
  "target_dataset_id": "uuid",
  "source_feature_count": 2,
  "target_feature_count": 2,
  "added_count": 1,
  "removed_count": 1,
  "unchanged_count": 1,
  "iou_threshold": 0.8,
  "warnings": [],
  "generated_at": "2026-06-16T00:00:00Z",
  "geojson": {
    "type": "FeatureCollection",
    "features": []
  }
}

Change detection prefers persisted vector_features. If an older vector dataset has no persisted vector rows, it falls back to the stored GeoJSON artifact and adds a warning to result_json.warnings. Supported comparable geometry types are Polygon and MultiPolygon; point/line geometries return UNSUPPORTED_GEOMETRY.

GeoJSON feature properties include:

  • change_type: added, removed or unchanged
  • source_dataset_id
  • target_dataset_id
  • source_feature_id
  • target_feature_id
  • iou

Limitations:

  • No live GRB/OSM/Sentinel fetching.
  • No fake object lifecycle classification.
  • No changed classification without durable object ids/versioning.
  • No first-class change table yet; the current output is stored in job result_json and rendered in the frontend map.

QA/QC

POST /api/v1/qa/detections-vs-reference

Request:

{
  "candidate_dataset_id": "uuid",
  "reference_dataset_id": "uuid",
  "iou_threshold": 0.5,
  "area_id": "optional uuid"
}

Response is wrapped in the job envelope. On success, result_json includes precision, recall, F1, mean IoU, false positives, false negatives and quality_check_id.

Sprint 7A persists the QA/QC result as:

  • jobs: execution state.
  • quality_checks: domain result.
  • metrics: individual measurements.

Future Detection and Segmentation flows may add an analysis_run_id path without replacing persisted quality checks.

GET /api/v1/projects/{project_id}/quality-checks

Lists persisted QA/QC quality checks for a project with metric rows.

Response:

{
  "items": [
    {
      "id": "uuid",
      "project_id": "uuid",
      "job_id": "uuid-or-null",
      "analysis_run_id": "uuid-or-null",
      "candidate_dataset_id": "uuid-or-null",
      "reference_dataset_id": "uuid",
      "check_type": "demo_candidate_vs_reference",
      "status": "ok",
      "score": 0.5,
      "parameters_json": {},
      "findings_json": {},
      "metrics": [
        {
          "metric_key": "precision",
          "metric_value": 0.5
        }
      ]
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

Exports

POST /api/v1/exports/geojson

Export detections, segmentations or vector layer to GeoJSON.

Dataset vector export request:

{
  "export_kind": "dataset",
  "dataset_id": "uuid",
  "name": "optional-basename"
}

Detection run export request:

{
  "export_kind": "detection_run",
  "analysis_run_id": "uuid",
  "name": "optional-basename"
}

Segmentation run export request:

{
  "export_kind": "segmentation_run",
  "analysis_run_id": "uuid",
  "name": "optional-basename"
}

Response persists an exports row and writes a deterministic JSON artifact:

{
  "export_id": "uuid",
  "path": "storage/exports/{project_id}/datasets/{target}/{name}.geojson",
  "status": "ready",
  "export_type": "dataset_geojson",
  "metadata_json": {
    "source": "dataset",
    "feature_count": 0
  }
}

Vector dataset exports use the stored dataset GeoJSON. Detection and segmentation exports use persisted first-class geometry records and the existing Detection/Segmentation GeoJSON conversion services. Raster datasets are rejected for dataset GeoJSON export.

POST /api/v1/exports/metadata

Exports project metadata JSON for projects, datasets, persisted QA/QC summary rows and existing export history.

{
  "project_id": "uuid",
  "name": "optional-basename"
}

GET /api/v1/exports/projects/{project_id}/exports

Lists persisted export records for a project.

GET /api/v1/exports/{export_id}

Returns one persisted export record.

GET /api/v1/exports/{export_id}/content

Returns the stored JSON artifact content through the standard API envelope.

GET /api/v1/exports/{export_id}/download

Downloads the stored JSON/GeoJSON export artifact as a raw file response with application/json content type and a Content-Disposition attachment filename. This endpoint intentionally does not use the JSON envelope because it is a browser/file-download path; callers that need canonical API JSON should use /content.

POST /api/v1/exports/yolo

Export annotations/detections to YOLO format.

POST /api/v1/exports/report

Creates a lightweight HTML project report artifact from persisted project, dataset, QA/QC summary and export history state. This does not create a PDF and does not introduce a report designer.

{
  "project_id": "uuid",
  "name": "optional-basename"
}

Response persists an exports row with export_type: project_report_html. Download the report through:

GET /api/v1/exports/{export_id}/download

PDF/report-designer functionality can be added after core GeoAI workflows work.