{
  "openapi": "3.1.1",
  "info": {
    "title": "Wildeye API",
    "description": "The Wildeye API extracts the time series information recorded by your Wildeyes, supporting multiple measurement types and sensors. Learn more about Wildeye at [www.mywildeye.com](https://www.mywildeye.com/).\n\n## Getting started\n\nAll requests are authenticated with an API key as a bearer token:\n\n```\nGET /api/v1/sites\nAuthorization: Bearer <your API key>\n```\n\nStart with `GET /api/v1/sites` — it lists the sites your key can access that have discoverable **inputs**, and each site's inputs come with it. Input ids are the handles you pass to the measurements endpoints to retrieve time-series data. (A site with no inputs isn't listed here, but a known site id can still be resolved directly via `GET /api/v1/sites/{id}`.)\n\n**Getting a key:** generate one yourself in the Wildeye web app under **client settings → Wildeye API Keys** (available when the API is enabled for your account — the key is shown once, so store it safely), or contact support@mywildeye.com. To try requests directly from this documentation, open the Auth section, choose Bearer, and paste your key.\n\n**Checking a key:** `GET /api/v1/ping/auth` returns 200 when your token is well-formed and authenticates (correct signature, not expired) — the quickest way to confirm the bearer token is being sent and accepted. It does not check whether the key has since been revoked; a revoked key is rejected by the data endpoints, not here.\n\n## What your key can see\n\nEvery response is scoped to your API key. By default a key covers your whole account (all of its sites, including sub-accounts). A key can also be restricted to specific sites or groups — those restrictions are provisioned by Wildeye rather than chosen in the key-creation screen, so contact support@mywildeye.com if you need a narrowed key.\n\n- Archived snapshots of a site your key can access are included automatically — archiving never moves data out of reach of a key that could see the original site.\n- Snapshots your account owns are always visible to a key covering your account, whatever happened to the record they were archived from.\n- The one narrow exception applies to keys **restricted to specific sites or groups**: such a key reaches a snapshot through the site it was archived from, so if that *snapshot* has since been transferred to a different account it is no longer covered. A snapshot missing in that situation is expected, not a scoping fault.\n- If a site you expect is missing from `/sites`, or an input id you know is returning 403 from the measurements endpoints, check the key's scope first — the data almost certainly exists, but this key wasn't granted it.\n\n## Core concepts: sites, devices, and archived sites\n\n**A site id is the identifier to store.** It is stable for the life of the site record — it survives renames, device changes, and archiving. Everything else is a lookup handle:\n\n- **Site names** are user-editable and are rewritten when a site is archived.\n- **Device ids (OP numbers)** identify the physical Wildeye unit. A device can be moved between sites, so `GET /api/v1/sites/op/{op}` always answers \"which site is this device on *right now*.\"\n\n**Archiving** happens when a device is retired from a location or redeployed elsewhere. The platform freezes the site's data as an **archived site**: a read-only snapshot holding all readings up to the archive date. The original site record carries on (typically redeployed with the device under a new name). On an archived site:\n\n- `archived` is the archive date (UTC) — non-null identifies it as archived.\n- `deviceId` is always null — a snapshot holds no device.\n- `archivedFromSiteId` points at the site it was archived from — the stable id linking history together. It is a correlation token, not a guaranteed-dereferenceable link: if that originating record has since moved to another account your key can't see, resolving it via `/sites/{id}` or `/sites/{id}/history` returns 404 even though the snapshot itself remains fully accessible.\n\nRepeat archives simply add more snapshots pointing at the same site, each holding its own slice of time.\n\n## Inputs, measurements and meter readings\n\nAn **input** is one measured channel on a site — a flow meter, a soil-moisture probe, a rain gauge. The site response describes each input:\n\n- `unit` — the unit its values are returned in.\n- `category` — the measurement category (`Flow Meter - Irrigation`, `Temperature - Air`, …) used by category queries; the full vocabulary comes from `GET /api/v1/categories`.\n- `loggingInterval` — seconds between recorded readings.\n- `sensor` / `sensorType` — the physical sensor, where configured.\n- `customFields` — name/value pairs your organisation has attached to the site or input (for example a council meter number).\n- `hasMeterReadings` — whether the input also carries meter readings (below).\n\nThere are **two kinds of time series** per input, served by different endpoints:\n\n- **Measurements** (`/measurements`) — the automatically logged sensor data, recorded at the input's logging interval. This is the data most consumers want.\n- **Meter readings** (`/meterreadings`) — manually entered readings of the physical meter's display (typically captured in the Wildeye app for calibration or compliance). Sparse by nature, and only present on inputs with `hasMeterReadings: true`.\n\nIf your organisation tags inputs with a meter-number custom field, `/MeterReadingsForMeterIds` retrieves meter readings addressed by those meter numbers directly, with cursor-based pagination suited to compliance-style exports. Any of these custom field names work (case-insensitive): **`MeterId`**, **`Meter Id`**, **`Meter Number`**, or **`Meter Serial Number`**.\n\n## The query language\n\nThe measurements and meter-readings endpoints take a `query` parameter selecting which inputs to read. It must be the **whole `query=` value** — parameters like `categories=` are not top-level:\n\n```\nGET /api/v1/measurements?query=ids=['029c99f0-f53a-4e05-bb86-1db8dc19f634']&start=2024-03-01\nGET /api/v1/measurements?query=categories=['Flow Meter - Irrigation','Moisture - Soil']&start=2024-03-01&page=1\nGET /api/v1/measurements?query=deviceids=['op74525','op70449']&start=2024-03-01\n```\n\n- **`ids`** — input ids from `/sites`. The most precise way to ask.\n- **`categories`** — measurement categories. Names must match **exactly**: get the valid list from `GET /api/v1/categories` (e.g. `Temperature - Air`, not `Air Temperature`). Multiple categories are allowed.\n- **`deviceids`** — all inputs currently on the listed devices. Note this resolves the devices' *current* sites — archived data is addressed by input id instead.\n\nValues are single-quoted, comma-separated, inside `[...]`. Your HTTP client will percent-encode the brackets and quotes automatically; if you're using `curl`, pass `-g` (globoff) so it doesn't try to expand the brackets itself.\n\nThe three filter types are **mutually exclusive — supply exactly one per request.** Combining them in a single `query` is not supported: the values are mis-parsed rather than merged, so the request will either fail or return nothing useful. If you need more than one selection, make separate requests.\n\n## Paging and reliable retrieval\n\nMeasurement responses are paged at up to 10,000 readings per page. Start at `page=1` and increment until an **empty array `[]`** comes back — that is the end-of-data signal.\n\nPractical guidance for robust collectors:\n\n- The data-retrieval endpoints are read-only GETs, so **retrying them is always safe** — but only retry what can actually change: network failures, timeouts, 429/5xx responses, or an unexpected non-JSON/empty body. A 400/401/403/404 is permanent — fix the request or the key rather than retrying (see Errors). Only trust a well-formed `[]` as the end of the data.\n- If you merge results from retries, de-duplicate on (input id, time) — a reading is uniquely identified by its input and timestamp.\n- For a stable multi-page sweep, use an explicit `end` in the past rather than paging an open-ended range while new readings are arriving.\n\n## Dates and timezones\n\nMeasurement data and all lifecycle dates (`archived`, `installationDate`, history event dates) are **UTC**. `displayTimezone` on a site is presentation metadata only.\n\n`start` and `end` are also interpreted as UTC. The time component is optional — `start=2024-03-07` is treated as midnight UTC on that date.\n\n## Rate limits and fair use\n\nThere are no enforced rate limits today. We do reserve the right to introduce them, or to restrict access, where usage is excessive enough to affect the service for others — so poll at a sensible interval for your data's logging frequency rather than continuously, and prefer a bounded `start`/`end` window over re-fetching all history on every run.\n\n## Data freshness, late data and corrections\n\nWildeye devices record continuously but **upload in batches**, and a device that loses connectivity uploads its backlog when it reconnects — so readings can appear in the API hours or occasionally days after their timestamps. Data can also occasionally be corrected retrospectively (for example a mis-scaled sensor being fixed).\n\nDon't treat the data as append-only: when syncing incrementally, re-fetch a trailing window (for example the last few days) each run instead of only asking for \"everything since my last timestamp\". A site's `lastUpload` tells you when its device last checked in.\n\nThe API returns readings at the device's raw logging interval (commonly 15 minutes — see each input's `loggingInterval`). Aggregation to hourly/daily totals is up to the consumer.\n\n## Working with archived data\n\n**Routine collection (active sites only):**\n\n```\nGET /api/v1/sites?status=active\nGET /api/v1/measurements/latest?query=ids=['<input id>',…]\n```\n\nBy default (`status=all`) the sites list includes archived sites; filter with `status=active|archived|all`.\n\n**A device was redeployed — where is its old data?** The old location's data lives on an archived site. Resolve the device to its current site, then list that site's snapshots:\n\n```\nGET /api/v1/sites/op/{op}                     -> current site; note its \"id\"\nGET /api/v1/sites?archivedFrom={id}           -> its archived sites, oldest first\nGET /api/v1/measurements?query=ids=[…]&start=…&end=…   -> readings via the snapshot's input ids\n```\n\n**What happened to this site over time?**\n\n```\nGET /api/v1/sites/{id}/history\n```\n\nreturns a date-ordered event list (UTC): `archived` events (with the snapshot's id and `deviceIdAtArchive` — the device that was on the site at the time) and `loggerSwapped` events (the site's device changed: `fromDeviceId` → `toDeviceId`). Use it to attribute archived data to the device that recorded it.\n\n**Bulk backfill:** `GET /api/v1/sites?status=archived`, group by `archivedFromSiteId`, and pull each snapshot's readings by input id. Each snapshot's `archived` date is its data cutoff.\n\n## Errors\n\n- **401 Unauthorized** — missing, malformed, or expired token. Check the token is being sent and accepted with `GET /api/v1/ping/auth`.\n- **403 Forbidden** — the token authenticates, but the request isn't allowed: a requested input is outside the key's scope, or the key has been revoked (deleted).\n- **404 Not Found** — the site doesn't exist, or is outside your key's scope (deliberately indistinguishable).\n- **400 Bad Request** — invalid parameters. The body is problem-details JSON naming the offending parameter:\n\n```json\n{\n  \"title\": \"One or more validation errors occurred\",\n  \"status\": 400,\n  \"errors\": { \"status\": [\"Invalid status 'bogus'. Valid values are: active, archived, all.\"] }\n}\n```\n\nThe 4xx statuses above are permanent for a given request — correct the parameters, key, or id rather than retrying. Retry only transient failures (network errors, timeouts, 429/5xx), which is always safe on the read-only data-retrieval endpoints.\n\n## Water orders\n\nThe `/waterorders` endpoints (delivery points, placing and cancelling water orders) exist for irrigation-scheme integrations that Wildeye sets up per account. Unless your account is part of such a scheme, they will return nothing useful and you can ignore them.\n\n## Integration recipes\n\n**Power BI**: create a *Web* data source pointing at `https://api.mywildeye.com/api/v1/sites` (or a measurements URL), open the advanced options, and add an HTTP header — name `Authorization`, value `Bearer <your API key>`. The same pattern works for Excel's Power Query and most BI tools.\n\n**curl**:\n\n```\ncurl -g -H \"Authorization: Bearer <your API key>\" \"https://api.mywildeye.com/api/v1/sites\"\n```\n\n**Insomnia / Postman**: import `/openapi/v1.json` from this server to get a ready-made request collection; set the bearer token once on the collection.\n\n## FAQ\n\n**Why is `deviceId` null on an archived site?** Snapshots hold data, not hardware. The device stayed with the original record — see the site's `/history` for which device was attached at archive time.\n\n**Why does `sites/op/{op}` return a different site than last month?** The device was moved or the site was archived and redeployed. The OP number always resolves to the device's current site; historical locations are reachable via `?archivedFrom=` on the stable site id.\n\n**How far back does `/history` go?** Events reflect recorded history. Device swaps have been recorded since 2009; where attribution can't be determined from records, fields are null rather than guessed.\n",
    "contact": {
      "name": "Wildeye Support",
      "url": "https://www.mywildeye.com",
      "email": "support@mywildeye.com"
    },
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.mywildeye.com/"
    }
  ],
  "paths": {
    "/api/v{version}/Categories": {
      "get": {
        "tags": [
          "Categories"
        ],
        "summary": "List of Categories available for the current user",
        "description": "Fetch the measurement categories available to the current user:\r\n\r\n```\r\nGET /api/v1/categories\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  \"Flow Meter - Irrigation\",\r\n  \"Level - Other\",\r\n  \"Flow Meter - Other\",\r\n  \"Flow Meter - Water\"\r\n]\r\n```",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of <see cref=\"!:IEnumerable&lt;string&gt;\">Categories</see>",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/Measurements": {
      "get": {
        "tags": [
          "Measurements"
        ],
        "summary": "Returns a list of Measurements",
        "description": "Fetch all measurements for a specific measurement category, from a defined start date, starting with\r\nthe first page of results; by input ids (defaults to starting 1 day ago); or for all inputs connected\r\nto a list of specified devices:\r\n\r\n```\r\nGET /api/v1/measurements?query=categories=['Flow Meter - Irrigation']&start=2024-03-07&page=1\r\nGET /api/v1/measurements?query=ids=['029c99f0-f53a-4e05-bb86-1db8dc19f634','b9c613cc-946a-4a52-866e-03bd660a1881','6d43525d-afff-4ed2-8564-60a24efb1f0b']\r\nGET /api/v1/measurements?query=deviceids=['op74525','op70449']\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"d18bca39-4ff9-40e8-8596-2b690a55b064\",\r\n    \"measurements\": [\r\n      { \"time\": \"2024-03-07T04:31:00\", \"value\": -10.4368385486663 },\r\n      { \"time\": \"2024-03-07T04:32:00\", \"value\": -10.4366682034269 },\r\n      { \"time\": \"2024-03-07T04:33:00\", \"value\": -10.4364978581875 }\r\n    ]\r\n  }\r\n]\r\n```",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (start at 1 and increment page until an empty array is returned)",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "query",
            "in": "query",
            "description": "Query string",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "start",
            "in": "query",
            "description": "Start date and time (If not supplied it will default to 1 day ago).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "end",
            "in": "query",
            "description": "End date",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of IList&lt;T&gt;",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InputMeasurements"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/Measurements/latest": {
      "get": {
        "tags": [
          "Measurements"
        ],
        "summary": "Returns the latest measurements for the specified inputs.",
        "description": "Fetch the latest measurements for a specific input id:\r\n\r\n```\r\nGET /api/v1/measurements/latest?query=ids=['029c99f0-f53a-4e05-bb86-1db8dc19f634']&page=1\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"029c99f0-f53a-4e05-bb86-1db8dc19f634\",\r\n    \"measurements\": [\r\n      { \"time\": \"2024-03-07T04:31:00\", \"value\": -10.4368385486663 }\r\n    ]\r\n  }\r\n]\r\n```",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (start at 1 and increment page until an empty array is returned)",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "query",
            "in": "query",
            "description": "Query string",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "count",
            "in": "query",
            "description": "Optional Count of Measurements to be returned",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of IList&lt;T&gt;",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InputMeasurements"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/MeterReadings": {
      "get": {
        "tags": [
          "MeterReadings"
        ],
        "summary": "Returns a list of Meter Readings",
        "description": "Fetch by measurement category, from a defined start date, starting with the first page of results;\r\nby input ids (defaults to starting 1 day ago); or by device ids:\r\n\r\n```\r\nGET /api/v1/meterreadings?query=categories=['Flow Meter - Irrigation']&start=2024-03-07&page=1\r\nGET /api/v1/meterreadings?query=ids=['029c99f0-f53a-4e05-bb86-1db8dc19f634','b9c613cc-946a-4a52-866e-03bd660a1881']\r\nGET /api/v1/meterreadings?query=deviceids=['op74525','op70449']\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"d18bca39-4ff9-40e8-8596-2b690a55b064\",\r\n    \"measurements\": [\r\n      { \"time\": \"2024-03-07T04:31:00\", \"value\": 87542 }\r\n    ]\r\n  }\r\n]\r\n```",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (start at 1 and increment page until an empty array is returned)",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "query",
            "in": "query",
            "description": "Query string",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "start",
            "in": "query",
            "description": "Start date and time (If not supplied it will default to 1 day ago).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "end",
            "in": "query",
            "description": "End date",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of IList&lt;T&gt;",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InputMeasurements"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/MeterReadings/latest": {
      "get": {
        "tags": [
          "MeterReadings"
        ],
        "summary": "Returns the latest meter readings for the specified inputs.",
        "description": "Fetch the latest meter readings for a specific input id; or the latest meter reading for a\r\nspecific measurement category, starting with the first page of results:\r\n\r\n```\r\nGET /api/v1/meterreadings/latest?query=ids=['029c99f0-f53a-4e05-bb86-1db8dc19f634']&page=1\r\nGET /api/v1/meterreadings/latest?query=categories=['Flow Meter - Irrigation']&count=1&page=1\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"029c99f0-f53a-4e05-bb86-1db8dc19f634\",\r\n    \"measurements\": [\r\n      { \"time\": \"2024-03-07T04:31:00\", \"value\": 87542 }\r\n    ]\r\n  }\r\n]\r\n```",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (start at 1 and increment page until an empty array is returned)",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "query",
            "in": "query",
            "description": "Query string",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "count",
            "in": "query",
            "description": "Optional Count of Measurements to be returned",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of IList&lt;T&gt;",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/InputMeasurements"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/MeterReadingsForMeterIds": {
      "get": {
        "tags": [
          "MeterReadingsForMeterIds"
        ],
        "summary": "Returns meter readings for inputs that include a custom field called `MeterId`, `Meter Id`, `Meter Number` or\r\n`Meter Serial Number` on them.",
        "description": "This endpoint uses time-based pagination to handle large result sets efficiently.\r\nRequest the first page, then subsequent pages (using nextStart from the previous response):\r\n\r\n```\r\nGET /api/v1/MeterReadingsForMeterIds?start=2023-06-01T00:00:00Z&end=2023-06-30T23:59:59Z\r\nGET /api/v1/MeterReadingsForMeterIds?start=2023-06-05T14:30:00.123Z&end=2023-06-30T23:59:59Z\r\n```\r\n\r\nThe response includes:\r\n\r\n- data: Array of meter readings (up to pageSize, default 10,000)\r\n- nextPageUrl: URL to fetch next page, or empty string if no more data\r\n- metadata: Pagination information including count, nextStart, and pageSize\r\n\r\nNote: For subsequent pages, the start parameter should be set to the nextStart value from the previous response.\r\nThe nextStart value is exclusive (readings at exactly that time are not included in the next page).",
        "parameters": [
          {
            "name": "start",
            "in": "query",
            "description": "Start date and time in UTC (optional, defaults to 1 day ago)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "end",
            "in": "query",
            "description": "End date and time in UTC (optional, defaults to current time)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "meterIds",
            "in": "query",
            "description": "Meter Ids to filter to (optional)",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Number of records per page (optional, default 10000, max 50000)",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 10000
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated response containing meter readings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeterReadingsForMeterIdsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/Ping": {
      "get": {
        "tags": [
          "Ping"
        ],
        "summary": "Used to determine that the service is alive and responding.",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "\"pong\"",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v{version}/Ping/auth": {
      "get": {
        "tags": [
          "Ping"
        ],
        "summary": "Used to determine the authentication header is correct and you are authenticated",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "\"authenticated pong [list of claims]\"",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/Sites": {
      "get": {
        "tags": [
          "Sites"
        ],
        "summary": "List of Sites and Inputs",
        "description": "List all sites; only active sites; or the archived snapshots of a specific site:\r\n\r\n```\r\nGET /api/v1/sites\r\nGET /api/v1/sites?status=active\r\nGET /api/v1/sites?archivedFrom=81df4df6-b270-451c-9158-46b9f4d3e405\r\n```\r\n\r\nArchived sites are frozen snapshots of a site's data taken when the site was archived: they have\r\n\"archived\" set, no \"deviceId\", and \"archivedFromSiteId\" pointing at the site they were archived from.\r\nBy default (status=all) both active and archived sites are returned.\r\n\r\nUse archivedFrom with a site id to list that site's archived snapshots (its archive history),\r\nordered oldest to newest. Each snapshot's inputs carry the ids used to retrieve its historical\r\nmeasurements. A site id is stable across archiving and device changes, so it is the recommended\r\nidentifier to store.\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"81df4df6-b270-451c-9158-46b9f4d3e405\",\r\n    \"name\": \"Test Logger\",\r\n    \"projectReference\": null,\r\n    \"lastUpload\": \"2026-07-01T02:15:00\",\r\n    \"deviceId\": \"op69417\",\r\n    \"archived\": null,\r\n    \"installationDate\": \"2023-02-14T00:00:00Z\",\r\n    \"archivedFromSiteId\": null,\r\n    \"gps\": null,\r\n    \"displayTimezone\": {\r\n      \"offset\": \"+12:00\",\r\n      \"followsDST\": true\r\n    },\r\n    \"client\": {\r\n      \"id\": \"c0be8a4f-0a49-4701-aa93-8285698717c8\",\r\n      \"name\": \"Staff\"\r\n    },\r\n    \"inputs\": [\r\n      {\r\n        \"id\": \"609e4580-ed91-435c-aacf-dc689700c497\",\r\n        \"name\": \"Level\",\r\n        \"logical\": \"\",\r\n        \"scada\": null,\r\n        \"loggingInterval\": 300,\r\n        \"unit\": \"meter\",\r\n        \"category\": \"Level - Other\",\r\n        \"sensor\": \"Water level\",\r\n        \"sensorType\": \"ACC-SEN-SUBM-ABS-S3-D5M-CxxM EasyLevel sensor\",\r\n        \"hasMeterReadings\": false,\r\n        \"customFields\": []\r\n      }\r\n    ],\r\n    \"customFields\": []\r\n  }\r\n]\r\n```",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by archive status: \"active\", \"archived\" or \"all\" (case-insensitive).\r\nDefaults to \"all\", returning active and archived sites together.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "archivedFrom",
            "in": "query",
            "description": "A site id: returns only the archived snapshots of that site, ordered by archive date (oldest first).\r\nAn id with no archived snapshots yields an empty list.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the available list of IEnumerable&lt;T&gt; and\r\nInput for each Site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Site"
                  }
                }
              }
            }
          },
          "400": {
            "description": "If the status value is not one of \"active\", \"archived\" or \"all\", or archivedFrom is not a valid id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/Sites/op/{op}": {
      "get": {
        "tags": [
          "Sites"
        ],
        "summary": "Get a specific Site by device ID (OP number)",
        "description": "Retrieves detailed information about a single site including all its inputs.\r\n\r\n```\r\nGET /api/v1/sites/op/op69417\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n{\r\n  \"id\": \"81df4df6-b270-451c-9158-46b9f4d3e405\",\r\n  \"name\": \"Test Logger\",\r\n  \"projectReference\": null,\r\n  \"lastUpload\": \"2026-07-01T02:15:00\",\r\n  \"deviceId\": \"op69417\",\r\n  \"archived\": null,\r\n  \"installationDate\": \"2023-02-14T00:00:00Z\",\r\n  \"archivedFromSiteId\": null,\r\n  \"gps\": null,\r\n  \"displayTimezone\": {\r\n    \"offset\": \"+12:00\",\r\n    \"followsDST\": true\r\n  },\r\n  \"client\": {\r\n    \"id\": \"c0be8a4f-0a49-4701-aa93-8285698717c8\",\r\n    \"name\": \"Staff\"\r\n  },\r\n  \"inputs\": [\r\n    {\r\n      \"id\": \"609e4580-ed91-435c-aacf-dc689700c497\",\r\n      \"name\": \"Level\",\r\n      \"logical\": \"\",\r\n      \"scada\": null,\r\n      \"loggingInterval\": 300,\r\n      \"unit\": \"meter\",\r\n      \"category\": \"Level - Other\",\r\n      \"sensor\": \"Water level\",\r\n      \"sensorType\": \"ACC-SEN-SUBM-ABS-S3-D5M-CxxM EasyLevel sensor\",\r\n      \"hasMeterReadings\": false,\r\n      \"customFields\": []\r\n    }\r\n  ],\r\n  \"customFields\": []\r\n}\r\n```\r\n\r\nNote: this endpoint resolves the device's CURRENT site. Archived sites hold no device, so they are never\r\nreturned here — after a device is redeployed, its OP number resolves to the new (live) site.",
        "parameters": [
          {
            "name": "op",
            "in": "path",
            "description": "The device ID (OP number) of the site to retrieve (e.g., \"op69417\")",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the Site with the specified device ID including all associated\r\nInput.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            }
          },
          "404": {
            "description": "If no site with the specified device ID is found or the user doesn't have access to it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/Sites/{id}": {
      "get": {
        "tags": [
          "Sites"
        ],
        "summary": "Get a specific Site by its id",
        "description": "Returns the same shape as the sites list, for a single site.\r\n\r\n```\r\nGET /api/v1/sites/81df4df6-b270-451c-9158-46b9f4d3e405\r\n```\r\n\r\nA site id is stable across archiving, device swaps and renames, so it is the recommended\r\nidentifier to store; use this endpoint to resolve stored ids (for example an\r\n\"archivedFromSiteId\" from an archived site).\r\n\r\nSample response (an archived site — note the null \"deviceId\" and the populated\r\n\"archived\" / \"archivedFromSiteId\"):\r\n\r\n```json\r\n{\r\n  \"id\": \"0e6713f8-6b8a-4f5e-9f8b-1c2d3e4f5a6b\",\r\n  \"name\": \"North Bore\",\r\n  \"projectReference\": null,\r\n  \"lastUpload\": null,\r\n  \"deviceId\": null,\r\n  \"archived\": \"2023-05-10T00:00:00Z\",\r\n  \"installationDate\": \"2021-02-01T00:00:00Z\",\r\n  \"archivedFromSiteId\": \"81df4df6-b270-451c-9158-46b9f4d3e405\",\r\n  \"gps\": null,\r\n  \"displayTimezone\": {\r\n    \"offset\": \"+12:00\",\r\n    \"followsDST\": true\r\n  },\r\n  \"client\": {\r\n    \"id\": \"c0be8a4f-0a49-4701-aa93-8285698717c8\",\r\n    \"name\": \"Staff\"\r\n  },\r\n  \"inputs\": [\r\n    {\r\n      \"id\": \"e1542e55-09fc-4a95-b4e4-fa597c3e9bed\",\r\n      \"name\": \"North Bore C1\",\r\n      \"logical\": null,\r\n      \"scada\": null,\r\n      \"loggingInterval\": 3600,\r\n      \"unit\": null,\r\n      \"category\": null,\r\n      \"sensor\": null,\r\n      \"sensorType\": null,\r\n      \"hasMeterReadings\": false,\r\n      \"customFields\": []\r\n    }\r\n  ],\r\n  \"customFields\": []\r\n}\r\n```",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The id of the site to retrieve",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the Site with the specified id including its Input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            }
          },
          "404": {
            "description": "If no site with the specified id is found or the user doesn't have access to it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/Sites/{id}/history": {
      "get": {
        "tags": [
          "Sites"
        ],
        "summary": "Lifecycle history of a Site: archive and device-swap events",
        "description": "Returns everything recorded that has happened to the site, as one date-ordered list (oldest first).\r\nAll dates are UTC.\r\n\r\n```\r\nGET /api/v1/sites/81df4df6-b270-451c-9158-46b9f4d3e405/history\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n{\r\n  \"siteId\": \"81df4df6-b270-451c-9158-46b9f4d3e405\",\r\n  \"events\": [\r\n    {\r\n      \"type\": \"archived\",\r\n      \"date\": \"2023-05-09T12:00:00Z\",\r\n      \"snapshotSiteId\": \"0e6713f8-6b8a-4f5e-9f8b-1c2d3e4f5a6b\",\r\n      \"deviceIdAtArchive\": \"op69417\",\r\n      \"fromDeviceId\": null,\r\n      \"toDeviceId\": null,\r\n      \"otherSiteId\": null,\r\n      \"otherSiteName\": null\r\n    },\r\n    {\r\n      \"type\": \"loggerSwapped\",\r\n      \"date\": \"2024-02-01T03:12:44Z\",\r\n      \"snapshotSiteId\": null,\r\n      \"deviceIdAtArchive\": null,\r\n      \"fromDeviceId\": \"op69417\",\r\n      \"toDeviceId\": \"op70021\",\r\n      \"otherSiteId\": \"5f8e2b1a-3c4d-4e5f-8a9b-0c1d2e3f4a5b\",\r\n      \"otherSiteName\": \"South Paddock\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\nNotes:\r\n\r\n- \"archived\" events point at the snapshot site holding the data up to that date; retrieve its inputs\r\n  via /sites?archivedFrom= or /sites/{id}. \"deviceIdAtArchive\" is the device that was on the site at\r\n  archive time: where swaps are recorded it is reconstructed by walking them (null if the recorded\r\n  chain cannot support an answer); where a site has no recorded swaps it reports the current device\r\n  as a best-effort inference, which a swap made without a recorded entry cannot account for.\r\n- \"loggerSwapped\" events show the device exchange from this site's perspective. The other site's\r\n  identity is only included when this API key has access to it.\r\n- Asking a snapshot site for its history returns the archive event that created it.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The id of the site whose history to retrieve",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the SiteHistory for the site.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteHistory"
                }
              }
            }
          },
          "404": {
            "description": "If no site with the specified id is found or the user doesn't have access to it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/WaterOrders/deliverypoint": {
      "get": {
        "tags": [
          "WaterOrders"
        ],
        "summary": "Get a specific delivery point by reference ID",
        "description": "Retrieves a single delivery point using its unique reference identifier.\r\n\r\n```\r\nGET /api/v1/waterOrders/deliverypoint?referenceId=0DAFC801-F736-4FA3-B7E8-560468E763C8\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n{\r\n  \"id\": \"22c2c331-f116-49ad-8655-3f50847ae72e\",\r\n  \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n  \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n  \"inputId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n  \"name\": \"M-30\"\r\n}\r\n```",
        "parameters": [
          {
            "name": "referenceId",
            "in": "query",
            "description": "The unique reference identifier for the delivery point",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the DeliveryPoint with the specified reference ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryPoint"
                }
              }
            }
          },
          "404": {
            "description": "If no delivery point with the specified reference ID is found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "WaterOrders"
        ],
        "summary": "Create a new delivery point",
        "description": "Creates a new delivery point linked to an input for water delivery tracking.\r\n\r\n```\r\nPOST /api/v1/waterOrders/deliverypoint\r\n```\r\n\r\nSample request body:\r\n\r\n```json\r\n{\r\n  \"name\": \"M-30\",\r\n  \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n  \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n  \"inputId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\"\r\n}\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n{\r\n  \"id\": \"22c2c331-f116-49ad-8655-3f50847ae72e\",\r\n  \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n  \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n  \"inputId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n  \"name\": \"M-30\"\r\n}\r\n```",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The delivery point to create",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryPoint"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryPoint"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryPoint"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Returns the newly created DeliveryPoint with its generated ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryPoint"
                }
              }
            }
          },
          "400": {
            "description": "If the delivery point data is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "WaterOrders"
        ],
        "summary": "Repoint an existing delivery point at a different Wildeye input",
        "description": "Updates the input (and optionally the name) of the delivery point identified by\r\nreferenceId. Use this to correct a turnout after a logger repurpose without raw DB\r\nedits. The op number is not consulted. An empty name leaves the existing name unchanged.\r\n\r\n```\r\nPUT /api/v1/waterOrders/deliverypoint\r\n```\r\n\r\nSample request body:\r\n\r\n```json\r\n{\r\n  \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n  \"inputId\": \"11111111-2222-3333-4444-555555555555\",\r\n  \"name\": \"M-6\"\r\n}\r\n```",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The delivery point to repoint (referenceId + inputId required)",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryPoint"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryPoint"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryPoint"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Returns the updated DeliveryPoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryPoint"
                }
              }
            }
          },
          "404": {
            "description": "If no delivery point with the referenceId, or no input with the inputId, is found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/WaterOrders/deliverypoints": {
      "get": {
        "tags": [
          "WaterOrders"
        ],
        "summary": "Get all delivery points for the current user's integration",
        "description": "Retrieves all delivery points associated with the API Keys client.\r\n\r\n```\r\nGET /api/v1/waterOrders/deliverypoints\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"22c2c331-f116-49ad-8655-3f50847ae72e\",\r\n    \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n    \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n    \"inputId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C8\",\r\n    \"name\": \"M-30\"\r\n  }\r\n]\r\n```",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns an array of DeliveryPoint objects for the user's integration",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DeliveryPoint"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/WaterOrders/active": {
      "get": {
        "tags": [
          "WaterOrders"
        ],
        "summary": "Get all active water orders",
        "description": "Retrieves all water orders for the current API Keys client.\r\n\r\n```\r\nGET /api/v1/waterOrders/active\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n[\r\n  {\r\n    \"id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\r\n    \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n    \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C9\",\r\n    \"deliveryPointId\": \"22c2c331-f116-49ad-8655-3f50847ae72e\",\r\n    \"start\": \"2025-10-19T17:00:00Z\",\r\n    \"end\": \"2025-10-20T05:00:00Z\",\r\n    \"quantity\": 3.6,\r\n    \"unit\": \"CFS\"\r\n  }\r\n]\r\n```",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns a list of active WaterOrder objects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WaterOrder"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v{version}/WaterOrders": {
      "post": {
        "tags": [
          "WaterOrders"
        ],
        "summary": "Create or update a water order",
        "description": "Creates a new water order or updates an existing one based on the reference ID.\r\nIf a water order with the same reference ID exists, it will be updated; otherwise, a new one is created.\r\n\r\n```\r\nPOST /api/v1/waterOrders\r\n```\r\n\r\nSample request body:\r\n\r\n```json\r\n{\r\n  \"deliveryPointId\": \"22c2c331-f116-49ad-8655-3f50847ae72e\",\r\n  \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n  \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C9\",\r\n  \"start\": \"2025-10-19T17:00:00Z\",\r\n  \"end\": \"2025-10-20T05:00:00Z\",\r\n  \"quantity\": 3.6,\r\n  \"unit\": \"CFS\"\r\n}\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n{\r\n  \"id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\r\n  \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n  \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C9\",\r\n  \"deliveryPointId\": \"22c2c331-f116-49ad-8655-3f50847ae72e\",\r\n  \"start\": \"2025-10-19T17:00:00Z\",\r\n  \"end\": \"2025-10-20T05:00:00Z\",\r\n  \"quantity\": 3.6,\r\n  \"unit\": \"CFS\"\r\n}\r\n```",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The water order to create or update. Must include referenceId, deliveryPointId, integrationId,\r\nstart date, quantity, and unit.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WaterOrder"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/WaterOrder"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/WaterOrder"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Returns the created or updated WaterOrder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaterOrder"
                }
              }
            }
          },
          "400": {
            "description": "If the water order data is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "WaterOrders"
        ],
        "summary": "Delete a water order",
        "description": "Deletes the water order with the specified reference ID and returns it.\r\n\r\n```\r\nDELETE /api/v1/waterOrders?referenceId=0DAFC801-F736-4FA3-B7E8-560468E763C9\r\n```\r\n\r\nSample response:\r\n\r\n```json\r\n{\r\n  \"id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\r\n  \"integrationId\": \"8F3CA2B4-4131-4745-A801-444A936658F6\",\r\n  \"referenceId\": \"0DAFC801-F736-4FA3-B7E8-560468E763C9\",\r\n  \"deliveryPointId\": \"22c2c331-f116-49ad-8655-3f50847ae72e\",\r\n  \"start\": \"2025-10-19T17:00:00Z\",\r\n  \"end\": \"2025-10-20T05:00:00Z\",\r\n  \"quantity\": 3.6,\r\n  \"unit\": \"CFS\"\r\n}\r\n```",
        "parameters": [
          {
            "name": "referenceId",
            "in": "query",
            "description": "The unique reference identifier of the water order to delete",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Returns the deleted WaterOrder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaterOrder"
                }
              }
            }
          },
          "404": {
            "description": "If no water order with the specified reference ID is found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Client": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of Client",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Name of Client"
          }
        },
        "description": "Wildeye's Client details"
      },
      "CustomField": {
        "required": [
          "name",
          "value"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of Custom Field"
          },
          "value": {
            "type": "string",
            "description": "Value of Custom Field"
          }
        },
        "description": "Custom Field details"
      },
      "DeliveryPoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "integrationId": {
            "type": "string",
            "format": "uuid"
          },
          "referenceId": {
            "type": "string"
          },
          "inputId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Gps": {
        "type": "object",
        "properties": {
          "latitude": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Latitude",
            "format": "double"
          },
          "longitude": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Longitude",
            "format": "double"
          }
        },
        "description": "Global Positioning System co-ordinates for the site of the Wildeye."
      },
      "Input": {
        "required": [
          "id",
          "name",
          "logical",
          "scada",
          "loggingInterval",
          "unit",
          "category",
          "sensor",
          "sensorType",
          "hasMeterReadings"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of Input",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Name of Input"
          },
          "logical": {
            "type": "string",
            "description": "Logical Name of Input"
          },
          "scada": {
            "type": "string",
            "description": "Scada ID Tag for Input"
          },
          "loggingInterval": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Logging interval of input in seconds",
            "format": "double"
          },
          "unit": {
            "type": [
              "null",
              "string"
            ],
            "description": "Unit of measurement"
          },
          "category": {
            "type": "string",
            "description": "Category of measurement"
          },
          "sensor": {
            "type": "string",
            "description": "Sensor name for Input"
          },
          "sensorType": {
            "type": "string",
            "description": "Sensor type for Input"
          },
          "hasMeterReadings": {
            "type": "boolean",
            "description": "Indicates if this input has meter readings available"
          },
          "customFields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomField"
            }
          }
        },
        "description": "An Input holds the time series data for a particular measurement at a site."
      },
      "InputMeasurements": {
        "required": [
          "id",
          "measurements"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "measurements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Measurement"
            }
          }
        }
      },
      "Measurement": {
        "required": [
          "time",
          "value"
        ],
        "type": "object",
        "properties": {
          "time": {
            "type": "string",
            "description": "Date and time of reading",
            "format": "date-time"
          },
          "value": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Reading value",
            "format": "double"
          }
        },
        "description": "Measurement at a particular time"
      },
      "MeterReadingForMeterId": {
        "required": [
          "outpostId",
          "siteId",
          "meterId",
          "inputId",
          "readingDateTimeUtc",
          "readingValue"
        ],
        "type": "object",
        "properties": {
          "outpostId": {
            "type": "string",
            "description": "Outpost ID"
          },
          "siteId": {
            "type": "string",
            "description": "Site ID a GUID that matches the SiteId in our other API endpoints",
            "format": "uuid"
          },
          "meterId": {
            "type": "string",
            "description": "Meter Id is the value of the custom field `MeterId`, `Meter Id`, `Meter Number` or `Meter Serial\r\nNumber`"
          },
          "inputId": {
            "type": "string",
            "description": "Input Id is a GUID that matches the InputId in our other API endpoints",
            "format": "uuid"
          },
          "readingDateTimeUtc": {
            "type": "string",
            "description": "Reading Date and Time in UTC",
            "format": "date-time"
          },
          "readingValue": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Reading value",
            "format": "double"
          }
        },
        "description": "Meter Reading for a Meter Id"
      },
      "MeterReadingsForMeterIdsResponse": {
        "required": [
          "data",
          "nextPageUrl",
          "metadata"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MeterReadingForMeterId"
            },
            "description": "Array of meter readings for the current page"
          },
          "nextPageUrl": {
            "type": "string",
            "description": "URL to fetch the next page, or empty string if no more data"
          },
          "metadata": {
            "description": "Pagination metadata",
            "$ref": "#/components/schemas/PaginationMetadata"
          }
        },
        "description": "Paginated response for meter readings"
      },
      "PaginationMetadata": {
        "required": [
          "count",
          "nextStart",
          "pageSize"
        ],
        "type": "object",
        "properties": {
          "count": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of records in the current page",
            "format": "int32"
          },
          "nextStart": {
            "type": [
              "null",
              "string"
            ],
            "description": "Start datetime for the next page (exclusive), or null if no more pages",
            "format": "date-time"
          },
          "pageSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Requested page size",
            "format": "int32"
          }
        },
        "description": "Pagination metadata"
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "Site": {
        "required": [
          "id",
          "name",
          "projectReference",
          "lastUpload",
          "deviceId",
          "archived",
          "installationDate",
          "archivedFromSiteId",
          "gps",
          "displayTimezone",
          "client",
          "inputs"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of Site. Stable: it survives archiving, device swaps, and renames — safe to store as a permanent reference.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Name of Site"
          },
          "projectReference": {
            "type": [
              "null",
              "string"
            ],
            "description": "Project Reference of Site"
          },
          "lastUpload": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "deviceId": {
            "type": [
              "null",
              "string"
            ],
            "description": "OP Number of the Site. Always null for archived sites (an archived site is a frozen snapshot and holds no device)."
          },
          "archived": {
            "type": [
              "null",
              "string"
            ],
            "description": "When this site was archived (UTC), or null for an active site. An archived site is an immutable snapshot of\r\nanother site's data up to this date, taken when that site was archived.",
            "format": "date-time"
          },
          "installationDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the site was installed (UTC), or null if not recorded.",
            "format": "date-time"
          },
          "archivedFromSiteId": {
            "type": [
              "null",
              "string"
            ],
            "description": "For an archived site, the Guid Site.Id of the site this snapshot was archived from; null for active sites.\r\nUse it to correlate archived snapshots with the site (and device) that produced them.",
            "format": "uuid"
          },
          "gps": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Location of the site",
                "$ref": "#/components/schemas/Gps"
              }
            ]
          },
          "displayTimezone": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Timezone of the Site (Note: measurement data is in UTC)",
                "$ref": "#/components/schemas/TimeZone"
              }
            ]
          },
          "client": {
            "description": "Client for the Site",
            "$ref": "#/components/schemas/Client"
          },
          "inputs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Input"
            },
            "description": "List of Inputs available for this Site"
          },
          "customFields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomField"
            }
          }
        },
        "description": "Site details"
      },
      "SiteHistory": {
        "required": [
          "siteId",
          "events"
        ],
        "type": "object",
        "properties": {
          "siteId": {
            "type": "string",
            "description": "Id of the Site the history belongs to",
            "format": "uuid"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SiteHistoryEvent"
            },
            "description": "The site's recorded events, oldest first"
          }
        },
        "description": "The lifecycle history of a site: everything that has happened to the site record, as one\r\ndate-ordered list of events (oldest first). All dates are UTC."
      },
      "SiteHistoryEvent": {
        "required": [
          "type",
          "date"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "\"archived\" — an archived snapshot of this site was created, freezing its data up to DateTime SiteHistoryEvent.Date;\r\n\"loggerSwapped\" — the site's device was exchanged with another site's."
          },
          "date": {
            "type": "string",
            "description": "When the event happened (UTC)",
            "format": "date-time"
          },
          "snapshotSiteId": {
            "type": [
              "null",
              "string"
            ],
            "description": "archived events: the id of the snapshot site holding the data; null when the snapshot is not\r\naccessible to this API key.",
            "format": "uuid"
          },
          "deviceIdAtArchive": {
            "type": [
              "null",
              "string"
            ],
            "description": "archived events: the device (OP number) on the site at archive time. Where device swaps are\r\nrecorded for the site, this is reconstructed by walking them, and is null when the recorded\r\nchain cannot support an answer. Where the site has no recorded swaps, it reports the site's\r\ncurrent device as a best-effort inference — correct for the usual case (the device stays with\r\nthe record), but a device replaced without a recorded swap cannot be detected."
          },
          "fromDeviceId": {
            "type": [
              "null",
              "string"
            ],
            "description": "loggerSwapped events: the device this site had before the swap"
          },
          "toDeviceId": {
            "type": [
              "null",
              "string"
            ],
            "description": "loggerSwapped events: the device this site has after the swap"
          },
          "otherSiteId": {
            "type": [
              "null",
              "string"
            ],
            "description": "loggerSwapped events: the site on the other side of the swap; null when it is not accessible\r\nto this API key.",
            "format": "uuid"
          },
          "otherSiteName": {
            "type": [
              "null",
              "string"
            ],
            "description": "loggerSwapped events: name of the other site; null when not accessible to this API key"
          }
        },
        "description": "A single lifecycle event for a site. string SiteHistoryEvent.Type determines which optional fields are populated.\r\nEvents reflect recorded history: device attribution and swap details before recording began cannot be\r\nreconstructed and surface as nulls rather than guesses."
      },
      "TimeZone": {
        "required": [
          "offset",
          "followsDST"
        ],
        "type": "object",
        "properties": {
          "offset": {
            "type": "string",
            "description": "Offset of the timezone"
          },
          "followsDST": {
            "type": "boolean",
            "description": "Does the timezone follow Daylight Saving Time"
          }
        },
        "description": "Timezone details"
      },
      "WaterOrder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "integrationId": {
            "type": "string",
            "format": "uuid"
          },
          "referenceId": {
            "type": "string"
          },
          "deliveryPointId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "quantity": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "format": "double"
          },
          "unit": {
            "type": "string"
          }
        }
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "description": "Your Wildeye API key. Generate one in the Wildeye web app under client settings > Wildeye API Keys (when enabled for your account), or contact support@mywildeye.com.",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "security": [
    {
      "Bearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Categories"
    },
    {
      "name": "Measurements"
    },
    {
      "name": "MeterReadings"
    },
    {
      "name": "MeterReadingsForMeterIds"
    },
    {
      "name": "Ping"
    },
    {
      "name": "Sites"
    },
    {
      "name": "WaterOrders"
    }
  ],
  "x-tagGroups": [
    {
      "name": "API Reference",
      "tags": [
        "Categories",
        "Measurements",
        "MeterReadings",
        "MeterReadingsForMeterIds",
        "Ping",
        "Sites",
        "WaterOrders"
      ]
    }
  ]
}