{"openapi":"3.1.0","info":{"title":"VachakEarth API","version":"0.6.0","description":"Multi-tenant geospatial platform API. Authentication uses an httpOnly session cookie (`gv_session`) set by /auth/login or /auth/register. All tenant data is scoped to the active organization and enforced by PostgreSQL Row Level Security."},"servers":[{"url":"/api"}],"components":{"securitySchemes":{"session":{"type":"apiKey","in":"cookie","name":"gv_session"}}},"security":[{"session":[]}],"paths":{"/health":{"get":{"tags":["System"],"security":[],"summary":"Liveness + database check","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/health/ready":{"get":{"tags":["System"],"security":[],"summary":"Readiness: database, applied migrations, job queue and (if configured) the GIS service. 503 when not ready","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"503":{"description":"Not ready","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/mfa/verify":{"post":{"tags":["Auth"],"security":[],"summary":"Second sign-in step: exchange the mfaToken from /auth/login plus a TOTP or recovery code for a session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mfaToken":{"type":"string"},"code":{"type":"string"},"recoveryCode":{"type":"string"}},"required":["mfaToken"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Invalid code or expired challenge","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/mfa":{"get":{"tags":["Auth"],"summary":"Whether two-factor authentication is enabled","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/mfa/setup":{"post":{"tags":["Auth"],"summary":"Start TOTP setup: returns the secret and otpauth:// URI (not active until enabled)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/mfa/enable":{"post":{"tags":["Auth"],"summary":"Confirm setup with a current code; returns 8 single-use recovery codes once","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid code","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/mfa/disable":{"post":{"tags":["Auth"],"summary":"Turn off two-factor authentication (password + current code)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"string"},"code":{"type":"string"}},"required":["password","code"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Incorrect","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/logout-all":{"post":{"tags":["Auth"],"summary":"Sign out every device (bumps the session version)","responses":{"204":{"description":"Signed out everywhere"}}}},"/admin/platform":{"get":{"tags":["Platform"],"summary":"Super admins only: read-only totals, organizations and recent failed jobs across the platform","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"403":{"description":"Not a super admin","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/assistant/status":{"get":{"tags":["Assistant"],"summary":"Whether the GeoAI assistant is configured","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/conversations":{"get":{"tags":["Assistant"],"summary":"Your conversations in a project","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Assistant"],"summary":"Start a conversation","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/conversations/{id}":{"get":{"tags":["Assistant"],"summary":"Conversation with messages and tool calls","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/conversations/{id}/messages":{"post":{"tags":["Assistant"],"summary":"Send a message; runs the tool loop and returns the reply with sources","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string"},"context":{"type":"object"}},"required":["content"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/assets":{"get":{"tags":["Assets"],"summary":"Asset registry (filter by type, name, verification)","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Assets"],"summary":"Register an asset from a geometry or an existing feature","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"assetType":{"type":"string"},"geometry":{"type":"object"},"fromFeature":{"type":"object"},"criticality":{"type":"integer"}},"required":["assetType"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/assets/geojson":{"get":{"tags":["Assets"],"summary":"Assets as GeoJSON with latest risk score","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/assets/import":{"post":{"tags":["Assets"],"summary":"Register every feature of a layer as assets (keeps source provenance)","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"layerId":{"type":"string"},"assetType":{"type":"string"},"nameProperty":{"type":"string"}},"required":["layerId","assetType"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/assets/{id}":{"get":{"tags":["Assets"],"summary":"Asset detail: spatial context, observations, notes, risk history","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"patch":{"tags":["Assets"],"summary":"Update name, type, criticality, verification status, attributes","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"delete":{"tags":["Assets"],"summary":"Delete an asset","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted"}}}},"/assets/{id}/observations":{"post":{"tags":["Assets"],"summary":"Record a field-verified or user-reported observation (satellite observations come only from tools)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/assets/{id}/notes":{"post":{"tags":["Assets"],"summary":"Add a note","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string"}},"required":["body"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/risk-assessments":{"get":{"tags":["Risk"],"summary":"Risk assessments (run via the run_risk_assessment tool)","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/risk-assessments/{id}":{"get":{"tags":["Risk"],"summary":"Assessment with per-asset scores, components, inspection priority, inputs, confidence and limitations","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/reports":{"get":{"tags":["Reports"],"summary":"Generated reports","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Reports"],"summary":"Generate a PDF/XLSX/CSV/GeoJSON report from stored results (background job)","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"format":{"type":"string","enum":["pdf","xlsx","csv","geojson"]},"source":{"type":"object"}},"required":["format","source"]}}}},"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/reports/{id}/download":{"get":{"tags":["Reports"],"summary":"Download a finished report","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"File"}}}},"/reports/{id}":{"delete":{"tags":["Reports"],"summary":"Delete a report","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted"}}}},"/projects/{projectId}/twin":{"get":{"tags":["Digital twins"],"summary":"Time-aware twin: assets with the latest observation at or before ?at=, freshness and recent readings","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"at","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/ingest-tokens":{"get":{"tags":["Digital twins"],"summary":"Sensor ingest tokens (prefix only)","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Digital twins"],"summary":"Create a sensor ingest token (shown once)","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/ingest-tokens/{id}":{"delete":{"tags":["Digital twins"],"summary":"Revoke an ingest token","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Revoked"}}}},"/ingest/readings":{"post":{"tags":["Digital twins"],"security":[],"summary":"Device ingestion with `Authorization: Bearer gvk_…`; readings are stored as sensor observations","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"readings":{"type":"array"}},"required":["readings"]}}}},"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Invalid token","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/register":{"post":{"tags":["Auth"],"security":[],"summary":"Create an account (optionally with a new organization)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"},"fullName":{"type":"string"},"organizationName":{"type":"string"}},"required":["email","password","fullName"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object"}}}},"409":{"description":"Email exists","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/login":{"post":{"tags":["Auth"],"security":[],"summary":"Sign in. With 2FA enabled the response is {mfaRequired, mfaToken} and no session is set until /auth/mfa/verify","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/logout":{"post":{"tags":["Auth"],"summary":"Sign out","responses":{"204":{"description":"Signed out"}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Current user, active organization, and memberships","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/forgot-password":{"post":{"tags":["Auth"],"security":[],"summary":"Issue a password reset token","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/reset-password":{"post":{"tags":["Auth"],"security":[],"summary":"Reset password with a token","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"password":{"type":"string"}},"required":["token","password"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/invitations/{token}":{"get":{"tags":["Auth"],"security":[],"summary":"Look up an invitation by token","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/invitations/accept":{"post":{"tags":["Auth"],"summary":"Accept an invitation addressed to the signed-in user","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/orgs":{"post":{"tags":["Organizations"],"summary":"Create an organization (caller becomes Organization Admin)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/orgs/switch":{"post":{"tags":["Organizations"],"summary":"Switch active organization","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string"}},"required":["organizationId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/orgs/current":{"get":{"tags":["Organizations"],"summary":"Active organization","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"patch":{"tags":["Organizations"],"summary":"Rename organization (org_admin)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/orgs/current/members":{"get":{"tags":["Organizations"],"summary":"List members","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/orgs/current/members/{membershipId}":{"patch":{"tags":["Organizations"],"summary":"Change member role (org_admin)","parameters":[{"name":"membershipId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["org_admin","project_manager","gis_analyst","field_officer","viewer"]}},"required":["role"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"delete":{"tags":["Organizations"],"summary":"Remove member (org_admin)","parameters":[{"name":"membershipId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removed"}}}},"/orgs/current/invitations":{"get":{"tags":["Organizations"],"summary":"Pending invitations (org_admin)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Organizations"],"summary":"Invite a user (org_admin)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"role":{"type":"string","enum":["org_admin","project_manager","gis_analyst","field_officer","viewer"]}},"required":["email","role"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/orgs/current/invitations/{id}":{"delete":{"tags":["Organizations"],"summary":"Revoke invitation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Revoked"}}}},"/orgs/current/audit-logs":{"get":{"tags":["Organizations"],"summary":"Audit log (org_admin)","parameters":[{"name":"limit","in":"query","schema":{"type":"integer"}},{"name":"before","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/overview":{"get":{"tags":["Overview"],"summary":"Organization dashboard statistics (computed from stored data)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects":{"get":{"tags":["Projects"],"summary":"Projects visible to the caller","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Projects"],"summary":"Create project (org_admin, project_manager)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{id}":{"get":{"tags":["Projects"],"summary":"Project details with extent and counts","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"patch":{"tags":["Projects"],"summary":"Update project","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["active","archived"]}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"delete":{"tags":["Projects"],"summary":"Delete project (org_admin)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted"}}}},"/projects/{id}/aoi":{"put":{"tags":["Projects"],"summary":"Set area of interest (GeoJSON Polygon/MultiPolygon in EPSG:4326, or null)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"geometry":{"type":["object","null"]}},"required":["geometry"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{id}/members":{"get":{"tags":["Projects"],"summary":"Project members","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Projects"],"summary":"Add / update project member","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string","enum":["manager","editor","viewer"]}},"required":["userId","role"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{id}/members/{memberId}":{"delete":{"tags":["Projects"],"summary":"Remove project member","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"memberId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removed"}}}},"/projects/{projectId}/layers":{"get":{"tags":["Layers"],"summary":"Layers in a project (with feature counts, geometry types, bbox)","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["Layers"],"summary":"Create an empty sketch layer","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/layers/upload":{"post":{"tags":["Layers"],"summary":"Upload GeoJSON, KML, Shapefile ZIP or CSV as a new layer","description":"Geometries are validated (ST_MakeValid), reprojected to EPSG:4326 (ST_Transform) and stored in PostGIS. The source CRS is preserved in layer provenance.","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"name":{"type":"string"},"srid":{"type":"integer","description":"Source EPSG code (GeoJSON without crs / CSV)"}},"required":["file"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid file","content":{"application/json":{"schema":{"type":"object"}}}},"413":{"description":"Too large","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/uploads":{"get":{"tags":["Layers"],"summary":"Uploaded file records","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/layers/{id}":{"patch":{"tags":["Layers"],"summary":"Update layer name / visibility / opacity / color / order","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"visible":{"type":"boolean"},"opacity":{"type":"number"},"color":{"type":"string"},"sortOrder":{"type":"integer"}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"delete":{"tags":["Layers"],"summary":"Delete layer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted"}}}},"/layers/{id}/geojson":{"get":{"tags":["Layers"],"summary":"Layer features as GeoJSON (EPSG:4326). ?download=1 for attachment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/layers/{id}/features":{"post":{"tags":["Layers"],"summary":"Add a drawn feature","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"geometry":{"type":"object"},"properties":{"type":"object"}},"required":["geometry"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/layers/{id}/features/{featureId}":{"delete":{"tags":["Layers"],"summary":"Delete a feature","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"featureId","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"Deleted"}}}},"/catalog/datasets":{"get":{"tags":["Catalog"],"summary":"Data catalog (public + organization datasets) with latest availability check","parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"type","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/catalog/datasets/{id}":{"get":{"tags":["Catalog"],"summary":"Dataset record, coverage geometry, adapter info and check history","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/catalog/datasets/{id}/check":{"post":{"tags":["Catalog"],"summary":"Run a live availability check against the provider (stored per organization)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/tools":{"get":{"tags":["GIS tools"],"summary":"Tool registry with JSON Schema for each tool input","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/tools/{name}":{"post":{"tags":["GIS tools"],"summary":"Run a GIS tool in a project","description":"Synchronous tools return 200 with the result; background tools (import_osm_features, calculate_zonal_statistics) return 202 with a jobId. Every run is recorded in spatial_analysis_jobs and analysis_results with provenance.","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"input":{"type":"object","description":"Tool input — see GET /tools for the schema"}},"required":[]}}}},"responses":{"200":{"description":"Completed","content":{"application/json":{"schema":{"type":"object"}}}},"202":{"description":"Queued","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object"}}}},"422":{"description":"Tool failed / timed out","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/projects/{projectId}/jobs":{"get":{"tags":["Jobs"],"summary":"Recent jobs in a project","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/jobs/{id}":{"get":{"tags":["Jobs"],"summary":"Job status, progress, logs and result","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/jobs/{id}/cancel":{"post":{"tags":["Jobs"],"summary":"Cancel a queued or running job","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/jobs/{id}/retry":{"post":{"tags":["Jobs"],"summary":"Retry a failed, cancelled or stalled job","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/layers/{id}/tiles/{z}/{x}/{y}.png":{"get":{"tags":["Layers"],"summary":"Raster layer tile (rendered by the GIS service from the stored COG URL)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"z","in":"path","required":true,"schema":{"type":"integer"}},{"name":"x","in":"path","required":true,"schema":{"type":"integer"}},{"name":"y","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"PNG"},"204":{"description":"Outside raster"},"503":{"description":"GIS service not configured","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/geocode/search":{"get":{"tags":["Geocoding"],"summary":"Forward geocode (OSM Nominatim adapter, throttled to 1 req/s)","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"}},{"name":"india","in":"query","schema":{"type":"string","enum":["0","1"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"502":{"description":"Provider unavailable","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/geocode/reverse":{"get":{"tags":["Geocoding"],"summary":"Reverse geocode","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number"}},{"name":"lon","in":"query","required":true,"schema":{"type":"number"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}}}}