{"openapi":"3.1.0","info":{"title":"DhuanTrace API","version":"1.0.0","license":{"name":"MIT"},"description":"Farm-fire smoke attribution, AQI forecasts, hidden hotspots, corridor alerts and a federated model for Madhya Pradesh and its neighbours by default; other regions run from region packs (REGION). Reads are open; writes are rate-limited per address. Screening-level estimates, not an official forecast."},"servers":[{"url":"https://db-vert-beta.vercel.app"}],"tags":[{"name":"Air"},{"name":"Accuracy"},{"name":"Citizens"},{"name":"Action"},{"name":"Sharing"},{"name":"System"}],"paths":{"/api/health":{"get":{"tags":["System"],"summary":"Liveness check","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}}}},"/api/jobs/hourly":{"post":{"tags":["System"],"summary":"Run the hourly jobs (forecast ledger, federated samples). Called by Cloud Scheduler with the x-job-token header","parameters":[{"name":"x-job-token","in":"header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"at":{"type":"integer"},"stations":{"type":"string"},"ledger":{"type":"object"},"federated":{"type":"object"},"errors":{"type":"array","items":{"type":"string"}}}}}}},"403":{"description":"Missing or wrong token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/system":{"get":{"tags":["System"],"summary":"Health of every data source, request metrics, uptime and version","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/System"}}}}}}},"/api/status":{"get":{"tags":["System"],"summary":"Live or sample mode of each data source","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sources":{"type":"object"},"languages":{"type":"object"},"now":{"type":"integer"}}}}}}}}},"/api/openapi.json":{"get":{"tags":["System"],"summary":"This description","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/requirements":{"get":{"tags":["System"],"summary":"Track 02 requirements traced to features, API, tests and a live check","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"track":{"type":"string"},"requirements":{"type":"array"},"live":{"type":"object"}}}}}}}}},"/api/region":{"get":{"tags":["System"],"summary":"The region pack this node runs: name, home states and their pollution boards, counts, available packs","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"home_states":{"type":"array","items":{"type":"object"}},"districts":{"type":"integer"},"cities":{"type":"integer"},"available":{"type":"array","items":{"type":"string"}}}}}}}}}},"/api/cities":{"get":{"tags":["Air"],"summary":"The cities of the active region pack, with population","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Place"}}}}}}}},"/api/fires":{"get":{"tags":["Air"],"summary":"Satellite fire detections (NASA FIRMS) with district","parameters":[{"name":"hours","in":"query","required":false,"description":"Look back 1–48 hours","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Fire"}}}}}}}},"/api/stations":{"get":{"tags":["Air"],"summary":"CPCB stations with current AQI and category","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}}}}},"/api/analyze":{"get":{"tags":["Air"],"summary":"Smoke attribution by district and state, 48 h forecast with range, smoke on the way","parameters":[{"name":"city","in":"query","required":false,"description":"City id, e.g. indore (see /api/cities)","schema":{"type":"string"}},{"name":"lat","in":"query","required":false,"description":"Latitude, instead of city","schema":{"type":"number"}},{"name":"lon","in":"query","required":false,"description":"Longitude, instead of city","schema":{"type":"number"}},{"name":"name","in":"query","required":false,"description":"Display name for a lat/lon point","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Analysis"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/priorities":{"get":{"tags":["Action"],"summary":"Home-region districts whose fires put smoke into the most people's air","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/hotspots":{"get":{"tags":["Air"],"summary":"Hidden hotspots: pollution CAMS and traced smoke cannot explain, with evidence and satellite check","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Hotspots"}}}}}}},"/api/corridors":{"get":{"tags":["Air"],"summary":"48 h worst-point forecast for six industrial corridors with spike alerts","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/ledger":{"get":{"tags":["Accuracy"],"summary":"Forecast ledger: forecasts written before and scored against CPCB after","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/skill":{"get":{"tags":["Accuracy"],"summary":"Model AQI versus each CPCB station right now","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/verify":{"get":{"tags":["Accuracy"],"summary":"Gemini reads the NASA true-colour image over a district's fires and checks the smoke direction","parameters":[{"name":"district","in":"query","required":true,"description":"District name in the active region pack","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/interstate":{"get":{"tags":["Sharing"],"summary":"Open bulletin: farm-fire smoke into and out of the home region by state (schema dhuantrace.interstate/1)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/packet":{"get":{"tags":["Sharing"],"summary":"Open city forecast packet (schema open-clean-air-packet/0.1)","parameters":[{"name":"city","in":"query","required":false,"description":"City id, e.g. indore (see /api/cities)","schema":{"type":"string"}},{"name":"lat","in":"query","required":false,"description":"Latitude, instead of city","schema":{"type":"number"}},{"name":"lon","in":"query","required":false,"description":"Longitude, instead of city","schema":{"type":"number"}},{"name":"name","in":"query","required":false,"description":"Display name for a lat/lon point","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/model":{"get":{"tags":["Sharing"],"summary":"Shared bias-correction model trained by the cities (weights only)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Model"}}}}}}},"/api/federated":{"get":{"tags":["Sharing"],"summary":"Federated training report per city node","parameters":[{"name":"simulate","in":"query","required":false,"description":"1 for the labelled simulation","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/federated/update":{"post":{"tags":["Sharing"],"summary":"Another state posts its model weights to join the average","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"node":{"type":"string"},"weights":{"type":"array","items":{"type":"number"},"minItems":5,"maxItems":5},"samples":{"type":"integer"}},"required":["node","weights"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"boolean"},"node":{"type":"string"},"samples":{"type":"integer"},"external_nodes":{"type":"integer"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/fednet":{"get":{"tags":["Sharing"],"summary":"Federated network: last run and the node processes","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/fednet/run":{"post":{"tags":["Sharing"],"summary":"Train over the network of separate nodes (protocol dhuantrace.fednet/1); only weights and error scores cross the wire","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FedNetRun"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Fewer than 2 nodes answered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/reports":{"get":{"tags":["Citizens"],"summary":"Citizen photo reports (last 48 h) with Gemini reading and trust score","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Report"}}}}}}},"post":{"tags":["Citizens"],"summary":"Send a photo report","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string","description":"data:image/jpeg|png|webp;base64,…"},"lat":{"type":"number"},"lon":{"type":"number"},"exif_head":{"type":"string","description":"base64 of the first 128 KB of the original file, for EXIF time and GPS"},"phash":{"type":"string","description":"64-bit dHash as 16 hex digits"}},"required":["image","lat","lon"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Report"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/reports/{id}/photo":{"get":{"tags":["Citizens"],"summary":"The reported photo","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Image","content":{"image/*":{}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/sensors":{"get":{"tags":["Citizens"],"summary":"Citizen sensor readings, calibrated against CPCB","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}}}},"post":{"tags":["Citizens"],"summary":"Send a PM2.5 (and PM10) reading","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"},"pm25":{"type":"number","minimum":0,"maximum":999},"pm10":{"type":"number","minimum":0,"maximum":1999},"device":{"type":"string","maxLength":40}},"required":["lat","lon","pm25"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/alert":{"post":{"tags":["Action"],"summary":"Draft an alert for the responsible district Collector (Gemini, template fallback)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"city":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"lang":{"type":"string","description":"en, hi, mr, gu, bn, ta, te, ur"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/alerts":{"get":{"tags":["Action"],"summary":"Alert log with outcome checks","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}}}},"post":{"tags":["Action"],"summary":"Save a drafted alert to the log","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"city":{"type":"string"},"lang":{"type":"string"},"to":{"type":"string"},"cc":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"},"by":{"type":"string"}},"required":["subject","body"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/alerts/{id}/status":{"post":{"tags":["Action"],"summary":"Move an alert to sent, acknowledged, action_taken or resolved","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["sent","acknowledged","action_taken","resolved"]},"note":{"type":"string"}},"required":["status"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ask":{"post":{"tags":["Action"],"summary":"Ask a question in any listed language; answered from the computed numbers only","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","maxLength":400},"city":{"type":"string"},"lang":{"type":"string"}},"required":["question"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"answer":{"type":"string"},"follow_ups":{"type":"array","items":{"type":"string"}},"by":{"type":"string"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Body must be JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this address; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"Place":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"hi":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"pop":{"type":"integer"}}},"Fire":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"},"ts":{"type":"integer","description":"ms since epoch, UTC"},"frp":{"type":"number","description":"fire radiative power, MW"},"source":{"type":"string"},"district":{"type":"string"}}},"Analysis":{"type":"object","properties":{"place":{"$ref":"#/components/schemas/Place"},"aqi_now":{"type":"object"},"peak_24h":{"type":"object","properties":{"value":{"type":"integer"},"t":{"type":"integer"},"lo":{"type":"integer"},"hi":{"type":"integer"}}},"attribution":{"type":"array","items":{"type":"object","properties":{"district":{"type":"string"},"state":{"type":"string"},"outside_mp":{"type":"boolean"},"share":{"type":"number"}}}},"by_state":{"type":"array","items":{"type":"object"}},"forecast":{"type":"array","items":{"type":"object","properties":{"t":{"type":"integer"},"cams":{"type":"number"},"corrected":{"type":"number"},"lo":{"type":"number"},"hi":{"type":"number"}}}},"upcoming":{"type":"object"}}},"Hotspots":{"type":"object","properties":{"points_checked":{"type":"integer"},"hotspots":{"type":"array","items":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"},"district":{"type":"string"},"likely_source":{"type":"string"},"confidence":{"type":"number"},"excess":{"type":"number"},"evidence":{"type":"array","items":{"type":"string"}},"satellite":{"type":"object","description":"aod and no2: over_spot, around, ratio, verdict (supports | weak | no_local_excess | no_data | unavailable), date, source"}}}}}},"Report":{"type":"object","properties":{"id":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"ts":{"type":"integer"},"district":{"type":"string"},"analysis":{"type":"object"},"trust":{"type":"object","properties":{"score":{"type":"number"},"level":{"type":"string"},"used_in_forecast":{"type":"boolean"},"reasons":{"type":"array","items":{"type":"object"}}}}}},"Model":{"type":"object","properties":{"schema":{"const":"dhuantrace-bias-model/1"},"features":{"type":"array","items":{"type":"string"}},"weights":{"type":"array","items":{"type":"number"}},"how_to_use":{"type":"string"}}},"FedNetRun":{"type":"object","properties":{"schema":{"const":"dhuantrace.fednet/1"},"mode":{"type":"string"},"nodes":{"type":"array","items":{"type":"object"}},"summary":{"type":"object"},"wire":{"type":"object","properties":{"messages":{"type":"integer"},"bytes_sent":{"type":"integer"},"bytes_received":{"type":"integer"},"fields_received":{"type":"array","items":{"type":"string"}},"raw_rows_received":{"type":"integer"}}}}},"System":{"type":"object","properties":{"ok":{"type":"boolean"},"version":{"type":"string"},"uptime_s":{"type":"integer"},"sources":{"type":"object"},"requests":{"type":"object"}}}}}}