Skip to main content

API Reference

FastGIS exposes a REST API consumed by the browser frontend and the mobile app. All endpoints require an X-API-Key header.

Authentication

X-API-Key: <your-token>

Tokens are generated in the user profile page or bootstrapped via the ADMIN_API_KEY environment variable.

:::tip Interactive docs FastAPI auto-generates live API docs at /docs (Swagger UI) and /redoc (ReDoc) on any running server. These are the authoritative source for exact request/response shapes. :::


Environments

All routes are under /grass/env.

MethodPathDescription
GET/grass/envList all environments for the authenticated user
POST/grass/envCreate a new GRASS environment
GET/grass/env/{env_id}Get environment metadata
DELETE/grass/env/{env_id}Delete an environment
GET/grass/env/{env_id}/layersList raster and vector layers in the environment

Create environment

POST /grass/env
Content-Type: application/json

{
"name": "my-project",
"epsg": 32632,
"db_driver": "pg"
}

db_driver is optional (sqlite or pg); defaults to GRASS_DB_DEFAULT_DRIVER.


GRASS module execution

Modules are executed via individual route modules per domain (raster, vector, imagery, …). The SSE task stream pattern is shared:

POST /grass/env/{env_id}/<module-path>
→ returns { "task_id": "..." }

GET /grass/tasks/{task_id}/stream
Accept: text/event-stream
→ streams stdout lines until done

See /docs on the running server for the full module schema per endpoint.


Profile tool

POST /grass/env/{env_id}/profile
Content-Type: application/json

{
"map": "elevation",
"coordinates": [[lon1, lat1], [lon2, lat2]],
"resolution": 100
}

Returns sampled profile values plus statistics.


Histogram tool

POST /grass/env/{env_id}/histogram
Content-Type: application/json

{
"map": "elevation",
"nsteps": 255,
"region": { "west": 10.0, "south": 59.0, "east": 11.0, "north": 60.0 }
}

Analysis artifacts

Save profile/histogram results as named artifacts.

MethodPathDescription
GET/grass/artifactsList all saved artifacts for the authenticated user
POST/grass/artifactsSave a new artifact
GET/grass/artifacts/{artifact_id}Load an artifact
DELETE/grass/artifacts/{artifact_id}Delete an artifact

Terrain

MethodPathDescription
POST/grass/env/{env_id}/terrain/buildBuild a quantized-mesh terrain dataset
GET/grass/terrainList built terrain datasets
DELETE/grass/terrain/{name}Delete a terrain dataset

Build terrain

POST /grass/env/{env_id}/terrain/build
Content-Type: application/json

{
"map": "elevation",
"name": "my_terrain",
"max_depth": 14,
"overwrite": false
}

Streams SSE build progress:

{"stage": "export", "msg": "Exporting …", "done": false}
{"stage": "reproject", "msg": "Reprojecting …", "done": false}
{"stage": "terrain", "msg": "Generating …", "done": false}
{"stage": "done", "msg": "Ready", "done": true, "terrain_url": "/terrain/my_terrain"}

Vector editing

MethodPathDescription
POST/grass/env/{env_id}/veditCommit new features
DELETE/grass/env/{env_id}/vedit/{map}/featureDelete features by category
PATCH/grass/env/{env_id}/vedit/{map}/feature/moveTranslate features
PATCH/grass/env/{env_id}/vedit/{map}/feature/replaceReplace feature geometry
PATCH/grass/env/{env_id}/vedit/{map}/feature/{cat}/attributesUpdate feature attributes
POST/grass/env/{env_id}/vedit/{map}/attributes/repairRepair missing attribute rows

Commit new features

POST /grass/env/{env_id}/vedit
Content-Type: application/json

{
"map": "my_points",
"features": [
{ "type": "point", "coordinates": [[lon, lat]], "cat": 1, "attributes": {"name": "A"} },
{ "type": "line", "coordinates": [[x1,y1],[x2,y2]], "cat": 2 },
{ "type": "polygon", "coordinates": [[x1,y1],[x2,y2],[x3,y3]], "cat": 3 }
],
"overwrite": false
}

Coordinates must be in the map's native GRASS CRS (not WGS84). attributes per feature is optional; new columns are created automatically.

Update attributes

PATCH /grass/env/{env_id}/vedit/{map}/feature/{cat}/attributes
Content-Type: application/json

{ "attributes": { "name": "Station A", "depth": 42.5 } }

Routing (Valhalla)

All routing endpoints are under /grass/routing/.

MethodPathDescription
GET/grass/routing/statusValhalla health + tile info
POST/grass/routing/routeTurn-by-turn directions
POST/grass/routing/isochroneReachability contours
POST/grass/routing/matrixTime/distance table
POST/grass/routing/circularLoop route (start = end)
GET/grass/routing/speed-camerasSpeed cameras in bbox
GET/grass/routing/poi-categoriesSupported POI category IDs
GET/grass/routing/nearbyPOI within radius
GET/grass/routing/routesList saved routes
POST/grass/routing/routesSave a route
GET/grass/routing/routes/{id}Load a route
DELETE/grass/routing/routes/{id}Delete a route
GET/grass/routing/routes/{id}/export/gpxExport GPX
GET/grass/routing/routes/{id}/export/geojsonExport GeoJSON
POST/grass/routing/locationPublish position to a group
GET/grass/routing/location/group/{group_id}Get group member positions
DELETE/grass/routing/location/{group_id}/{user_id}Leave group

Route

POST /grass/routing/route
Content-Type: application/json

{
"locations": [{"lon": 10.7, "lat": 59.9}, {"lon": 14.2, "lat": 66.3}],
"costing": "motorcycle",
"units": "km",
"alternates": 0
}

Loop route

POST /grass/routing/circular
Content-Type: application/json

{
"lat": 59.9, "lon": 10.7,
"distance_km": 150,
"direction": "N",
"costing": "motorcycle",
"avg_speed_kmh": 50,
"num_waypoints": 6,
"waypoint_percentile": 0.70,
"through_waypoints": true,
"use_heading": true
}

Response includes trip, waypoints, isochrone, actual_distance_km, actual_duration_s.


Curvature (curvy-road discovery)

MethodPathDescription
GET/grass/curvature/db-statusCheck whether roads.db exists and its row count
GET/grass/curvature/road-classesList OSM highway classes in the DB
POST/grass/curvature/analyze-precomputedQuery pre-ingested curvature DB by bbox/filters
GET/grass/curvature/nearbyFind curvy roads near a lat/lon (server-side, uses DB or live Overpass)

Query nearby curvy roads

GET /grass/curvature/nearby?lat=59.9&lon=10.7&radius_km=50&preset=twisty&limit=50

Parameters:

FieldTypeDefaultDescription
lat, lonfloatSearch centre
radius_kmfloat50Search radius
presetstringtwistyScoring preset (twisty, scenic, balanced)
limitint50Maximum roads returned
min_curvaturefloatOverride minimum curvature score
min_sinuosityfloatOverride minimum sinuosity
road_classesstringComma-separated OSM highway classes
exclude_loopsbooltrueExclude loop roads

Returns GeoJSON FeatureCollection with LineString road segments and Point name markers.


Offline data tiles

1°×1° SQLite tile files for offline POI search and curvature — downloaded by the mobile app.

MethodPathDescription
GET/grass/tiles/catalogList all available tiles with per-tile availability and file size
GET/grass/tiles/poi/{tile_id}Download a POI SQLite tile (e.g. tile_id=47_8)
GET/grass/tiles/curvature/{tile_id}Download a curvature SQLite tile

Tile catalog response

{
"tiles": [
{
"id": "47_8",
"label": "47°N 8°E",
"bbox": { "west": 8, "south": 47, "east": 9, "north": 48 },
"poi": { "available": true, "size_bytes": 2400000 },
"curvature": { "available": true, "size_bytes": 890000 }
},
{
"id": "48_9",
"label": "48°N 9°E",
"bbox": { "west": 9, "south": 48, "east": 10, "north": 49 },
"poi": { "available": true, "size_bytes": 1100000 },
"curvature": { "available": false, "size_bytes": null }
}
]
}

Tile IDs encode the SW corner: {lat}_{lon}, e.g. 47_8 = SW corner 47°N 8°E.


Data packs (server admin)

Manage region configuration and trigger background data rebuild jobs.

MethodPathDescription
GET/grass/data-packs/statusRegion list with per-region pack availability
POST/grass/data-packs/regions/enableEnable / disable regions in regions.yml
POST/grass/data-packs/rebuildStart a background rebuild job
GET/grass/data-packs/rebuild/{job_id}Poll job status and log tail

Enable regions

POST /grass/data-packs/regions/enable
Content-Type: application/json

{
"enable": ["norway", "sweden", "austria"],
"disable": ["denmark"]
}

Trigger rebuild

POST /grass/data-packs/rebuild
Content-Type: application/json

{
"regions": ["norway", "sweden", "austria", "italy", "spain", "poland", "czech-republic"],
"pack_types": ["curvature"],
"force": true,
"curvature_threshold": "both"
}
FieldTypeDefaultDescription
regionsstring[]Region names to rebuild. Empty list [] uses all enabled regions (for tiles step).
pack_typesstring[]["poi","curvature"]Steps to run: curvature, poi, tiles. Run curvature before tiles.
forceboolfalseRe-build even if output already exists.
curvature_threshold"300" | "1000" | "both""both"Which KMZ score-band(s) to download and ingest. "both" produces the most complete DB and is the recommended default.

pack_types order: curvature first, then tiles (tiles depend on the curvature DB being ready).

Poll job status

GET /grass/data-packs/rebuild/{job_id}
{
"job_id": "a1b2c3d4",
"status": "done",
"pack_types": ["curvature"],
"started_at": 1715000000.0,
"finished_at": 1715003600.0,
"errors": [],
"logs": ["[09:00:01] $ python3 scripts/fetch_region_data.py …", "…"]
}

status values: running, done, error.


Field sessions (mobile)

Server-side registry for mobile field sessions, backed by Redis.

MethodPathDescription
POST/grass/env/{env_id}/field-sessionsRegister a field session (upsert)
GET/grass/env/{env_id}/field-sessionsList registered field sessions for an environment
DELETE/grass/env/{env_id}/field-sessions/{track_layer}Delete a field session registration

Published projects

MethodPathDescription
GET/grass/publishedList published project snapshots for the authenticated user
GET/grass/published/{project_id}Load a published project
DELETE/grass/published/{project_id}Delete a published project

Location sharing

POST /grass/routing/location
Content-Type: application/json

{
"group_id": "trip-2025",
"user_id": "alice",
"lat": 59.9,
"lon": 10.7,
"heading": 45.0,
"speed": 13.8
}

Positions expire after 5 minutes of inactivity.