{"openapi": "3.0.3", "info": {"title": "Enviromesh platform API", "version": "1.0.0", "description": "UTC environmental observations. Public data is explicitly opted in; gateway evidence and QC flags are retained."}, "servers": [{"url": "https://freemesh.ai"}], "paths": {"/public/v1/networks/": {"get": {"operationId": "list_public_networks", "summary": "List networks that have enabled publication", "parameters": [], "security": [], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/public/v1/observations/": {"get": {"operationId": "get_public_observations", "summary": "Read future-only, published, passing observations; no messages, coordinates or keys", "parameters": [{"name": "hours", "in": "query", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 168, "default": 24}}, {"name": "limit", "in": "query", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 100}}, {"name": "network", "in": "query", "required": false, "schema": {"type": "string"}}], "security": [], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object", "properties": {"observations": {"type": "array", "items": {"$ref": "#/components/schemas/Observation"}}, "count": {"type": "integer"}}}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/nodes/{node_id}/history/": {"get": {"operationId": "get_instrument_history", "summary": "Read private, versioned instrument history", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "node_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}, {"name": "limit", "in": "query", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 50}}, {"name": "before_version", "in": "query", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 2147483647}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object", "properties": {"node_id": {"type": "string", "format": "uuid"}, "revisions": {"type": "array", "items": {"$ref": "#/components/schemas/InstrumentRevision"}}, "next_before_version": {"type": "integer", "nullable": true}}}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/": {"get": {"operationId": "list_networks", "summary": "List accessible networks", "parameters": [], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/session/": {"get": {"operationId": "get_session", "summary": "Verify current account before syncing drafts", "parameters": [], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/snapshot/": {"get": {"operationId": "get_snapshot", "summary": "Workspace snapshot (up to 500 nodes)", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/nodes/": {"get": {"operationId": "get_nodes", "summary": "Filter the instrument directory", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "q", "in": "query", "required": false, "schema": {"type": "string"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/observations/": {"get": {"operationId": "get_observations", "summary": "Read network observations", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "limit", "in": "query", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 500, "default": 100}}, {"name": "hours", "in": "query", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 720, "default": 24}}, {"name": "node", "in": "query", "required": false, "schema": {"type": "string"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/messages/": {"get": {"operationId": "get_messages", "summary": "Read recent messages", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}, "post": {"operationId": "queue_message", "summary": "Queue a message (session operator only; no token writes)", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}, "201": {"description": "Message durably queued"}, "409": {"description": "Idempotency key conflicts with different content"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["client_id", "node_id", "gateway_id", "content"], "properties": {"client_id": {"type": "string", "format": "uuid"}, "node_id": {"type": "string", "format": "uuid"}, "gateway_id": {"type": "string", "format": "uuid"}, "content": {"type": "string", "maxLength": 150, "description": "Maximum 150 UTF-8 bytes"}}}}}}}}, "/api/v1/networks/{slug}/alerts/": {"get": {"operationId": "get_alerts", "summary": "Read open alerts", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/messages/{message_id}/cancel/": {"post": {"operationId": "cancel_messages", "summary": "Session role required", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "message_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/alerts/{alert_id}/acknowledge/": {"post": {"operationId": "acknowledge_alerts", "summary": "Session role required", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "alert_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/networks/{slug}/credentials/{credential_id}/revoke/": {"post": {"operationId": "revoke_credentials", "summary": "Session role required", "parameters": [{"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "credential_id", "in": "path", "required": true, "schema": {"type": "string", "format": "uuid"}}], "security": [{"Bearer": []}, {"Session": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}}}, "/api/v1/gateway/events/": {"post": {"operationId": "ingest_events", "summary": "Store 1\u201350 versioned events atomically; scoped gateway token", "parameters": [], "security": [{"Bearer": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "See docs/API.md for the versioned event and receipt contracts."}}}}}}, "/api/v1/gateway/heartbeat/": {"post": {"operationId": "gateway_heartbeat", "summary": "Update gateway heartbeat; scoped gateway token", "parameters": [], "security": [{"Bearer": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "See docs/API.md for the versioned event and receipt contracts."}}}}}}, "/api/v1/gateway/commands/claim/": {"post": {"operationId": "claim_command", "summary": "Claim one leased command; scoped gateway token", "parameters": [], "security": [{"Bearer": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "See docs/API.md for the versioned event and receipt contracts."}}}}}}, "/api/v1/gateway/commands/complete/": {"post": {"operationId": "complete_command", "summary": "Record delivery receipt; scoped gateway token", "parameters": [], "security": [{"Bearer": []}], "responses": {"200": {"description": "Successful JSON response", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"description": "Invalid request"}, "401": {"description": "Authentication required"}, "403": {"description": "Insufficient access"}, "429": {"description": "Rate limit exceeded"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "See docs/API.md for the versioned event and receipt contracts."}}}}}}}, "components": {"schemas": {"Observation": {"type": "object", "required": ["id", "node_id", "observed_at", "measurements", "quality", "provenance"], "properties": {"id": {"type": "string", "format": "uuid"}, "node_id": {"type": "string", "format": "uuid"}, "node_name": {"type": "string"}, "observed_at": {"type": "string", "format": "date-time"}, "measurements": {"type": "object", "properties": {"temperature_c": {"type": "number", "description": "\u00b0C"}, "humidity_pct": {"type": "number", "description": "%"}, "pressure_hpa": {"type": "number", "description": "hPa"}, "battery_v": {"type": "number", "description": "V"}, "battery_pct": {"type": "number", "description": "%"}, "solar_w": {"type": "number", "description": "W"}, "wind_speed_ms": {"type": "number", "description": "m/s"}, "water_level_m": {"type": "number", "description": "m"}, "water_meter_m3": {"type": "number", "description": "m\u00b3"}}}, "units": {"type": "object", "additionalProperties": {"type": "string"}}, "quality": {"type": "string"}, "quality_flags": {"type": "array", "items": {"type": "string"}}, "qc_version": {"type": "string"}, "provenance": {"type": "string", "enum": ["gateway_attested", "ed25519_verified"]}, "simulation": {"type": "boolean"}}}, "InstrumentRevision": {"type": "object", "properties": {"id": {"type": "string", "format": "uuid"}, "version": {"type": "integer", "minimum": 1}, "kind": {"type": "string", "enum": ["commissioning", "deployment", "calibration", "maintenance"]}, "effective_at": {"type": "string", "format": "date-time"}, "recorded_at": {"type": "string", "format": "date-time"}, "manufacturer": {"type": "string"}, "model_name": {"type": "string"}, "serial_number": {"type": "string"}, "site_name": {"type": "string"}, "calibration_reference": {"type": "string"}, "notes": {"type": "string"}, "recorded_by": {"type": "string", "nullable": true}, "latitude": {"type": "number", "nullable": true}, "longitude": {"type": "number", "nullable": true}, "calibrated_on": {"type": "string", "format": "date", "nullable": true}, "calibration_due": {"type": "string", "format": "date", "nullable": true}}}}, "securitySchemes": {"Bearer": {"type": "http", "scheme": "bearer", "description": "Scoped, expiring network credential"}, "Session": {"type": "apiKey", "in": "cookie", "name": "sessionid", "description": "Session writes also require X-CSRFToken"}}}}