Skip to content

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.

GET /api/config

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": "&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors &copy; <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_highno_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

curl https://leaf-asrlm.in/api/config
import requests

r = requests.get("https://leaf-asrlm.in/api/config")
config = r.json()
print(f"Map center: {config['map_config']['center']}")
print(f"Feasibility colors: {len(config['feasibility_colors'])} classes")
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.

GET /api/levels

Response

{
  "levels": [
    { "id": "block", "name": "Block", "available": true }
  ]
}
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

curl https://leaf-asrlm.in/api/levels
import requests

r = requests.get("https://leaf-asrlm.in/api/levels")
data = r.json()

for level in data['levels']:
    print(f"{level['name']}: available={level['available']}")

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.

POST /api/config/refresh

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

curl -X POST https://leaf-asrlm.in/api/config/refresh
import requests

r = requests.post("https://leaf-asrlm.in/api/config/refresh")
data = r.json()
print("Refreshed:", data["refreshed"])
print("Validation OK:", data["validation"]["ok"])

Google Sheets Sync Status

Returns the current TTL-cache status for every Google Sheet data source, without forcing a refresh.

GET /api/config/sheets-status

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

curl https://leaf-asrlm.in/api/config/sheets-status

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.

GET /api/config/validate

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), unique BLOCK_ID, non-empty Block_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 an I_variable, range_minrange_max, every I_variable exists 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 the Block_name column exists and that extra columns map to a known friendly name. Silent no-op while the sheet is unconfigured.

Example

curl https://leaf-asrlm.in/api/config/validate

API Information

Returns a structured listing of all available API endpoints. Useful for programmatic API discovery.

GET /api

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

curl https://leaf-asrlm.in/api | python -m json.tool

Health Check

Returns service health status. Use this for uptime monitoring and load-balancer health checks.

GET /health

Response

{
  "status": "healthy"
}

Example

curl https://leaf-asrlm.in/health
import requests

r = requests.get("https://leaf-asrlm.in/health")
print(f"Status: {r.json()['status']}")
print(f"HTTP: {r.status_code}")

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.