# Environmental Monitoring API

Structured telemetry ingestion for IoT sensors, OPC UA edge gateways and lab monitoring systems (ISO/IEC 17025 §6.3). This reference describes implemented code, not verified deployment or entitlement for every organisation. Requires the Analytics & QC module. Do not send production mutations while testing. REST base: `https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/api-v1`.

REST uses organisation API keys or PATs in `Authorization: Bearer <token>`. Unbound PATs need `X-Org-Id` or `org_id`; bound credentials cannot select a different organisation. `write` includes `read`.

## Concepts

A **monitoring point** defines what is measured where: parameter (e.g. temperature, humidity, differential pressure, fill level, door contact), unit, limits and an optional room/equipment link. Points are created in the app under QC → Environmental monitoring. Each point can carry an `external_code` — a stable alias (e.g. an OPC UA node name like `FH-04-AIRFLOW`) so gateways do not need the internal UUID.

A **reading** is one measured value at a point in time. Limit evaluation is done by the database, identically for API and manual entries: limits are copied from the point and `is_breach` is set automatically.

## List points

`GET /v1/environmental-points` with read. Optional `active=true` to list only active points. Returns up to 500 points including `id`, `external_code`, `parameter`, `unit`, `limit_min`, `limit_max`, `room_id`, `equipment_id`.

## List readings

`GET /v1/environmental-readings` with read. Optional filters: `point_id`, `since` (ISO 8601 timestamp), `breaches=true`, `limit` (default 100, maximum 500). Newest first.

## Ingest readings

`POST /v1/environmental-readings` with write. Accepts a single reading object or a batch `{"readings": [...]}` (1–500 items). Each item:

```json
{
  "external_code": "FH-04-AIRFLOW",
  "value": 0.42,
  "unit": "m/s",
  "measured_at": "2026-10-16T08:00:00+02:00",
  "notes": "optional"
}
```

- `point_id` (UUID) **or** `external_code` is required per item.
- `value`: required finite number. Discrete states map to numbers (e.g. door contact: `0` = closed, `1` = open, with limit `max = 0` on the point).
- `measured_at`: optional ISO 8601 with offset; defaults to server time.
- `unit`: optional; defaults to the point's unit.
- `sample_id`: optional UUID to link a reading to a sample.

Items whose point cannot be resolved or is inactive are rejected individually without failing the rest of the batch. 201 response: `{data: {created: 5, breaches: 1, readings: [...], rejected: [...]}}`. If every item was rejected, the response is 422.

Optional `Idempotency-Key` retains original status/body on replay (default 24 hours) — recommended for gateways with retry logic. Errors include `validation_failed`, `point_not_found`, `point_inactive`, `insufficient_scope`, `module_not_enabled`.

## OPC UA gateway pattern

Browsers and edge functions cannot speak `opc.tcp://` directly. Run a small on-premise forwarder (e.g. a Python or Node daemon on the lab gateway) that subscribes to OPC UA nodes, maps node IDs to `external_code`s, and POSTs batches to this endpoint with an organisation API key. Send on change or on a fixed interval (e.g. 60 s); avoid sub-second pushes.
