Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ricardomb-tech/surqo/llms.txt

Use this file to discover all available pages before exploring further.

The Surqo KPI engine translates raw sensor readings into decision-ready agronomic indices. Rather than exposing raw numbers, it computes well-established scientific formulas — Magnus equation for VPD, Penman-Monteith-derived ETc, degree-day accumulation for GDD, and a probabilistic pest-risk model — so farmers can act on insight rather than data. All KPI endpoints require authentication via a Supabase JWT and verify that the requesting user owns the target farm.

KPI Definitions

KPIFormulaInterpretation
VPD (kPa)es − ea (Magnus)< 0.8 optimal · 0.8–1.6 acceptable · > 1.6 stress · > 2.5 critical
ETc (mm/day)ET₀ × KcDaily water consumption by crop
GDD (degree-days)(Tmax+Tmin)/2 − TbaseAccumulated heat for crop development
Water deficit (mm)ETc_7d − rain_7dWater shortfall for the week
Soil health score (0–100)Composite moisture + temp> 70 healthy · < 50 critical
Pest risk (%)Temp + humidity + crop model> 70 = alert

Crop Kc Coefficients

ETc is calculated as ET₀ × Kc. The following crop coefficients are used by KPIService.calculate_etc():
CropKc coefficient
Arroz1.20
Maíz1.15
Plátano1.10
Algodón1.05
Café0.95
Yuca0.85
Crops not in the table fall back to a Kc of 1.0.

Endpoints

GET /api/v1/kpis/farm/{farm_id}

Returns a full KPI summary computed from the last 24 hours of sensor readings for the farm.
farm_id
string (UUID)
required
UUID of the farm. The authenticated user must be the owner; returns 403 otherwise.
Returns {"error": "Sin lecturas en las últimas 24 horas"} when no readings exist for the period. Response — 200 OK
{
  "vpd_kpa": 1.42,
  "avg_air_temp_c": 29.5,
  "avg_humidity_pct": 68.3,
  "avg_soil_moisture_pct": 44.1,
  "soil_health_score": 75,
  "pest_risk": {
    "risk_pct": 35,
    "pathogens": ["roya"],
    "conditions": "Condiciones moderadas para hongos"
  },
  "readings_count_24h": 96,
  "latest_reading_at": "2026-06-28T14:35:00Z"
}

GET /api/v1/kpis/farm/{farm_id}/vpd-history

Returns a time series of VPD values for the last 48 hours, ordered chronologically. Only readings that contain a non-null vpd_kpa value are included.
farm_id
string (UUID)
required
UUID of the farm.
Response — 200 OKlist[{timestamp, vpd_kpa}]
[
  {"timestamp": "2026-06-27T08:00:00+00:00", "vpd_kpa": 0.72},
  {"timestamp": "2026-06-27T08:15:00+00:00", "vpd_kpa": 0.89},
  {"timestamp": "2026-06-27T08:30:00+00:00", "vpd_kpa": 1.14}
]

GET /api/v1/kpis/farm/{farm_id}/water-balance

Returns the estimated 7-day water balance for the farm. ETc is calculated using an ET₀ approximation of 4.5 mm/day (standard for the Colombian tropical zone) and the Kc coefficient for the farm’s crop type. Rainfall is not yet sourced from sensors and defaults to 0.0 mm — for a weather-adjusted balance, use the full AI analysis endpoint (POST /api/v1/analysis/analyze).
farm_id
string (UUID)
required
UUID of the farm.
Response — 200 OK
{
  "farm_id": "uuid-finca",
  "period_days": 7,
  "et0_acumulada_mm": 31.5,
  "etc_acumulada_mm": 36.2,
  "lluvia_acumulada_mm": 0.0,
  "deficit_hidrico_mm": 36.2,
  "nota": "ETc calculada con ET0 estimada. Para mayor precisión, ejecutar /analysis/analyze"
}

GET /api/v1/kpis/farm/{farm_id}/pest-risk

Returns the current pest risk level based on the last 24 hours of readings. Risk is modelled from average air temperature and humidity against known conditions for fungal and crop-specific pathogens.
farm_id
string (UUID)
required
UUID of the farm.
Response — 200 OK
{
  "farm_id": "uuid-finca",
  "calculated_at": "2026-06-28T14:35:00Z",
  "avg_temp_c": 28.4,
  "avg_humidity_pct": 82.1,
  "risk_pct": 75,
  "pathogens": ["roya", "fusarium", "pudrición de mazorca"],
  "conditions": "Alta temperatura y humedad — riesgo fungal elevado"
}
Returns {"error": "Sin lecturas recientes para calcular riesgo"} when no readings exist in the last 24 hours.

VPD Formula Detail

The VPD is calculated by KPIService.calculate_vpd() using the Magnus approximation:
# Magnus equation — from kpi_service.py
es = 0.6108 * exp(17.27 * T / (T + 237.3))  # saturation vapor pressure (kPa)
ea = es * (RH / 100)                          # actual vapor pressure
VPD = es - ea
T is the average air temperature in °C and RH is the relative humidity percentage. The result is floored at 0.0 and rounded to 3 decimal places.
The soil health score is a composite of three factors:
def calculate_soil_health_score(soil_moisture, soil_temp, ec=None) -> int:
    score = 0
    # Moisture: optimal 40–70% → +40 pts
    if 40 <= soil_moisture <= 70:
        score += 40
    elif 30 <= soil_moisture < 40 or 70 < soil_moisture <= 80:
        score += 25
    elif 20 <= soil_moisture < 30:
        score += 10

    # Soil temp: optimal 15–30°C → +30 pts
    if 15 <= soil_temp <= 30:
        score += 30
    elif 10 <= soil_temp < 15 or 30 < soil_temp <= 35:
        score += 15

    # EC (optional): optimal 200–800 µS/cm → +30 pts
    # Falls back to +15 pts (neutral) when EC sensor not present
    if ec is not None:
        if 200 <= ec <= 800:
            score += 30
        elif 100 <= ec < 200 or 800 < ec <= 1200:
            score += 15
    else:
        score += 15

    return min(100, score)
Risk is based on temperature–humidity thresholds with crop-specific pathogen mapping:
def calculate_pest_risk(temp_c, humidity_pct, crop_type) -> dict:
    risk_pct = 0
    # High temp + high humidity → fungal risk
    if temp_c > 25 and humidity_pct > 80:
        risk_pct = 75
    elif temp_c > 22 and humidity_pct > 70:
        risk_pct = 40
    elif humidity_pct > 85:
        risk_pct = 50

    # Pathogens activated when risk_pct > 40
    crop_pathogens = {
        "maíz":    ["roya", "fusarium", "pudrición de mazorca"],
        "café":    ["roya del café", "broca", "antracnosis"],
        "plátano": ["sigatoka negra", "moko"],
        "yuca":    ["bacteriosis", "superalargamiento"],
        "arroz":   ["pyricularia", "helminthosporium"],
        "algodón": ["verticillium", "fusarium"],
    }
For the most accurate KPI results, ensure your ESP32 node is using a DHT22 (±0.5°C) rather than a DHT11 (±2°C). The Magnus VPD equation amplifies temperature errors — a 2°C offset can produce VPD errors of up to 0.15 kPa, which can trigger false stress alerts.

Build docs developers (and LLMs) love