Config & Health API¶
Application configuration, data-level information, Google Sheets sync/validation, and health monitoring. These endpoints provide metadata about the system itself rather than the underlying geospatial data.
Base URL
Production base URL is https://leaf-asrlm.in. All examples below use it. Relative paths (/api/...) work from the same origin as the app.
Get Application Configuration¶
Returns the IWMI brand color palette, the feasibility color scheme, and the map configuration used by the frontend. Useful for building custom clients that match the official LEAF DSS look.
Response¶
{
"colors": {
"dark_blue": "#28537D",
"light_blue": "#5088C6",
"sky_blue": "#46BBD4",
"teal": "#0297A6",
"green": "#22AD7A",
"orange": "#E86933",
"yellow": "#DD9103",
"light_grey": "#E8E7E7"
},
"feasibility_colors": {
"very_high": "#1b5e20",
"high": "#81c784",
"moderate_high": "#c5e1a5",
"moderate": "#ffd700",
"low": "#ff8c00",
"very_low": "#ff0000",
"no_data": "#E0E0E0"
},
"map_config": {
"center": [22.5, 82.5],
"zoom": 5,
"tile_url": "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
"tile_attribution": "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors © <a href=\"https://carto.com/attributions\">CARTO</a>"
}
}
| Field | Type | Description |
|---|---|---|
colors |
object | IWMI brand color palette (keyed by color name: dark_blue, light_blue, sky_blue, teal, green, orange, yellow, light_grey) |
feasibility_colors |
object | Hex color for each feasibility class (very_high … no_data) |
map_config.center |
array | Default map center as [lat, lng] (centered on India) |
map_config.zoom |
number | Default zoom level |
map_config.tile_url |
string | Leaflet base-tile URL template (CARTO light) |
map_config.tile_attribution |
string | Attribution HTML for the base tiles |
Example¶
const r = await fetch('/api/config');
const config = await r.json();
// Initialize map with server-provided config
const map = L.map('map').setView(config.map_config.center, config.map_config.zoom);
L.tileLayer(config.map_config.tile_url, {
attribution: config.map_config.tile_attribution
}).addTo(map);
Get Available Data Levels¶
Returns the data hierarchy levels available for analysis.
Response¶
| Field | Type | Description |
|---|---|---|
levels |
array | Available data levels |
levels[].id |
string | Level identifier (block) |
levels[].name |
string | Display name |
levels[].available |
boolean | Whether the level's data is loaded (block is always true) |
Example¶
Refresh Google Sheets Data¶
Forces a re-fetch of every published Google Sheet (dss_input, block_values, user_update), bypassing the 5-minute TTL cache. The response also re-runs the guardrail validation. POST.
Response¶
{
"refreshed": {
"dss_input": true,
"block_values": true,
"user_update": false
},
"status": {
"block_values": {
"cached": true,
"rows": 220,
"columns": 121,
"age_seconds": 0,
"stale": false,
"source_url": "https://docs.google.com/spreadsheets/d/e/.../pub?output=csv"
}
},
"validation": {
"ok": true,
"issues": []
}
}
| Field | Type | Description |
|---|---|---|
refreshed |
object | Map of sheet key → true if the sheet was re-fetched successfully, false otherwise (e.g. user_update has no URL configured, so it reports false) |
status |
object | Current cache status for each sheet (same shape as GET /api/config/sheets-status) |
validation |
object | Guardrail result — ok flag plus list of issues (same shape as GET /api/config/validate) |
Example¶
Google Sheets Sync Status¶
Returns the current TTL-cache status for every Google Sheet data source, without forcing a refresh.
Response¶
{
"dss_input": {
"cached": true,
"rows": 340,
"columns": 12,
"age_seconds": 42,
"stale": false,
"source_url": "https://docs.google.com/spreadsheets/d/e/.../pub?output=csv"
},
"block_values": {
"cached": true,
"rows": 220,
"columns": 121,
"age_seconds": 42,
"stale": false,
"source_url": "https://docs.google.com/spreadsheets/d/e/.../pub?output=csv"
},
"user_update": {
"cached": false,
"source_url": ""
}
}
| Field | Type | Description |
|---|---|---|
<sheet>.cached |
boolean | Whether a copy is currently in the cache |
<sheet>.rows |
number | Row count of the cached DataFrame (present only when cached is true) |
<sheet>.columns |
number | Column count (present only when cached is true) |
<sheet>.age_seconds |
number | Seconds since the cached copy was fetched (present only when cached is true) |
<sheet>.stale |
boolean | true once age_seconds ≥ 300 (the 5-minute TTL); the next read will re-fetch (present only when cached is true) |
<sheet>.source_url |
string | Published-CSV URL for the sheet (user_update is empty until the end-user sheet is published) |
The keys are the three sheet sources: dss_input (intervention config), block_values (per-block variable overlay), and user_update (LEAF-59 friendly end-user sheet, currently unconfigured).
Example¶
Validate Config Sheets (Guardrails)¶
Runs structural guardrail checks (LEAF-60) on the Google Sheets that power the dashboard, so problems an editor might introduce are caught before they break anything. Read-only. The same result is also returned in the validation field of POST /api/config/refresh.
Response¶
{
"ok": true,
"issues": [
{ "sheet": "block_values", "severity": "error", "message": "Duplicate BLOCK_ID value(s): 42. Each block must have a unique ID." },
{ "sheet": "dss_input", "severity": "warning", "message": "I_variable code(s) not found in the block values sheet: ZZZ." }
]
}
| Field | Type | Description |
|---|---|---|
ok |
boolean | false if any error-severity issue was found |
issues[].sheet |
string | block_values, dss_input, or user_update |
issues[].severity |
string | error (will break the dashboard) or warning (worth a look) |
issues[].message |
string | Plain-language description of the problem |
Checks performed¶
- Block values (
block_values): required ID columns present (BLOCK_ID,STATE_ID,DISTRICT_I,id,Block_name— not renamed/removed), uniqueBLOCK_ID, non-emptyBlock_name, and stray non-numeric text in otherwise-numeric columns. - Intervention config (
dss_input): required columns present (Cluster,I_variable,range_min,range_max), intervention rows have anI_variable,range_min≤range_max, everyI_variableexists as a block-values column, and no variable carries conflicting convergence-card tags (Biophysical vs Infrastructure). - End-user update sheet (
user_update): only checked when a URL has been published — verifies theBlock_namecolumn exists and that extra columns map to a known friendly name. Silent no-op while the sheet is unconfigured.
Example¶
API Information¶
Returns a structured listing of all available API endpoints. Useful for programmatic API discovery.
Response¶
{
"info": "LEAF DSS API v1.0",
"endpoints": {
"blocks": {
"GET /api/blocks": "Get all blocks as GeoJSON",
"GET /api/blocks/geojson": "Get all blocks as GeoJSON (alias)",
"GET /api/blocks/<block_id>": "Get block by BLOCK_ID",
"GET /api/blocks/by-name/<block_name>": "Get block by name"
},
"locations": {
"GET /api/locations": "Get hierarchical list of districts and blocks",
"GET /api/districts": "Get list of all districts with metadata",
"GET /api/districts/<district>/blocks": "Get all blocks in a district"
},
"interventions": {
"GET /api/interventions": "List available interventions",
"GET /api/intervention/<name>/config": "Get intervention configuration"
},
"variables": {
"GET /api/variables": "Get all block-level variables",
"GET /api/variable-groups": "Get variable groups",
"GET /api/variable-stats/<variable>": "Get variable statistics"
},
"feasibility": {
"POST /api/calculate-feasibility": "Calculate block feasibility"
},
"export": {
"POST /api/export/csv": "Export data as CSV"
},
"config": {
"GET /api/config": "Get app configuration",
"GET /api/levels": "Get available data levels",
"GET /api/protected-areas/geojson": "Get protected areas as GeoJSON"
},
"villages": {
"GET /api/villages": "List villages (optionally filtered by block)",
"GET /api/villages/geojson": "Villages as GeoJSON points",
"GET /api/villages/aggregate": "Aggregated counts by district or block (state/district map levels)",
"GET /api/villages/blocks": "Blocks with village data available"
},
"clusters": {
"GET /api/clusters/params": "Default clustering parameters",
"GET /api/clusters": "List stored clusters (filter by block/district/commodity)",
"GET /api/clusters/<cluster_id>": "Get a cluster by ID",
"GET /api/clusters/<cluster_id>/report": "Cluster report card with block-level LEAF variables",
"POST /api/clusters/regenerate": "Run clustering algorithm and replace stored clusters in scope",
"GET /api/clusters/export.csv": "Export clusters as row-per-village CSV",
"POST /api/clusters/import": "Replace stored clusters in scope from uploaded CSV",
"POST /api/clusters/<cluster_id>/finalize": "Mark a cluster as finalised"
},
"infrastructure": {
"GET /api/infrastructure": "List POIs (filter by type/block/district)",
"POST /api/infrastructure/import": "Replace POI dataset from CSV",
"GET /api/infrastructure/nearest": "Nearest POIs to cluster centroid or arbitrary point"
},
"production_tool": {
"GET /api/production-tool/clusters": "Outbound feed of finalised clusters",
"GET /api/production-tool/dashboard/<cluster_id>": "Get stored dashboard payload",
"POST /api/production-tool/dashboard/<cluster_id>": "Receive aggregated dashboard data per cluster"
},
"health": {
"GET /health": "Health check"
}
}
}
This is a static listing
GET /api returns a hand-maintained catalogue baked into the route. It intentionally omits a few live routes (e.g. POST /api/config/refresh, GET /api/config/sheets-status, GET /api/config/validate, POST /api/config/upload-ai-doc, and the AI recommendation route). For the complete, always-current contract use the Swagger UI at /docs or the spec at /apispec.json.
Example¶
Health Check¶
Returns service health status. Use this for uptime monitoring and load-balancer health checks.
Response¶
Example¶
Monitoring
Point your uptime monitor (UptimeRobot, Render health checks, Pingdom, etc.) at /health for reliable service monitoring. The endpoint has no dependencies — it returns immediately without loading data.