diff --git a/.github/workflows/sync-openapi-v3.yml b/.github/workflows/sync-openapi-v3.yml new file mode 100644 index 0000000..0e4534b --- /dev/null +++ b/.github/workflows/sync-openapi-v3.yml @@ -0,0 +1,52 @@ +name: Sync OpenAPI v3 + +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * 1" + +permissions: + contents: write + +concurrency: + group: sync-openapi-v3 + cancel-in-progress: false + +jobs: + sync: + runs-on: ubuntu-latest + + steps: + - name: Check out main + uses: actions/checkout@v4 + with: + ref: main + + - name: Install jq + run: | + sudo apt-get update + sudo apt-get install -y jq + + - name: Sync v3 OpenAPI snapshot + run: ./scripts/update-openapi-v3.sh + + - name: Detect snapshot changes + id: changes + run: | + if git diff --quiet -- openapi/openapi-v3.json; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Commit updated snapshot + if: steps.changes.outputs.changed == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add openapi/openapi-v3.json + git commit -m "chore: sync openapi v3 snapshot" + + - name: Push to main + if: steps.changes.outputs.changed == 'true' + run: git push origin HEAD:main diff --git a/README.md b/README.md index a75cc30..6f50bc4 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,16 @@ Run: ./scripts/update-openapi-v3.sh ``` +## Automated spec sync + +GitHub Actions also keeps `openapi/openapi-v3.json` up to date automatically. + +- Weekly schedule: every Monday at 00:00 UTC, which is Monday 09:00 in Korea Standard Time +- Manual trigger: Actions -> `Sync OpenAPI v3` -> `Run workflow` +- Commit behavior: if the extracted v3 snapshot changed, the workflow commits the updated `openapi/openapi-v3.json` directly to `main` + +The workflow is defined in `.github/workflows/sync-openapi-v3.yml` and reuses `scripts/update-openapi-v3.sh` so local and automated sync stay consistent. + ## Local development ### TypeScript diff --git a/docs/plans/2026-04-16-openapi-sync-design.md b/docs/plans/2026-04-16-openapi-sync-design.md new file mode 100644 index 0000000..0c312f8 --- /dev/null +++ b/docs/plans/2026-04-16-openapi-sync-design.md @@ -0,0 +1,76 @@ +# OpenAPI Sync Workflow Design + +**Date:** 2026-04-16 + +## Goal + +Keep `openapi/openapi-v3.json` aligned with `https://patch-api.conalog.com/openapi.json` automatically, with a weekly schedule and a manual trigger, and commit changes directly to `main`. + +## Current Context + +- The repository already has a sync script at `scripts/update-openapi-v3.sh`. +- The script fetches the upstream OpenAPI document, filters `/api/v3/` paths, rewrites the API title, and writes the result to `openapi/openapi-v3.json`. +- There is no existing `.github/workflows` directory, so the automation can be introduced without conflicting with current CI conventions. + +## Recommended Approach + +Use a GitHub Actions workflow that runs on: + +- `schedule` once per week +- `workflow_dispatch` for manual runs + +The workflow should: + +1. Check out `main` +2. Install `jq` +3. Run `scripts/update-openapi-v3.sh` +4. Detect whether `openapi/openapi-v3.json` changed +5. Commit and push to `main` only when there is a real diff + +## Alternatives Considered + +### 1. GitHub Actions direct-to-main sync + +This is the recommended approach. + +- Matches the requested weekly plus manual trigger behavior +- Reuses the existing repository script +- Keeps operational logic close to the repository +- Avoids external scheduling infrastructure + +### 2. GitHub Actions with PR creation + +This is safer for review-heavy repositories, but it does not match the request to commit directly to `main` and adds operational overhead. + +### 3. External scheduler or local cron + +This would work technically, but it increases maintenance cost and moves a repository-owned concern outside GitHub. + +## Behavior Details + +- The scheduled run should happen weekly at a fixed UTC time. +- Manual runs should use the standard Actions "Run workflow" entry. +- The workflow should request `contents: write` so the default `GITHUB_TOKEN` can push to `main`. +- Concurrency should prevent overlapping sync jobs from racing each other. +- If there is no diff after running the sync script, the workflow exits without creating a commit. + +## Error Handling + +- `curl -fsSL` in the existing script already causes the job to fail on fetch errors. +- `set -euo pipefail` in the script ensures bad fetches or bad `jq` processing fail the job. +- A failed workflow should leave the repository unchanged. + +## Verification + +Local verification should cover: + +- Running `scripts/update-openapi-v3.sh` +- Confirming the workflow file is syntactically valid YAML +- Reviewing the generated diff to ensure only intended automation files and docs changed + +## Files To Change + +- Create: `.github/workflows/sync-openapi-v3.yml` +- Modify: `README.md` +- Create: `docs/plans/2026-04-16-openapi-sync.md` + diff --git a/docs/plans/2026-04-16-openapi-sync.md b/docs/plans/2026-04-16-openapi-sync.md new file mode 100644 index 0000000..7193610 --- /dev/null +++ b/docs/plans/2026-04-16-openapi-sync.md @@ -0,0 +1,79 @@ +# OpenAPI Sync Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Add a GitHub Actions workflow that refreshes the local v3 OpenAPI snapshot weekly and on manual trigger, then commits real changes directly to `main`. + +**Architecture:** Reuse the existing shell sync script as the single source of truth for generating `openapi/openapi-v3.json`. Wrap it in a GitHub Actions workflow that checks for diffs, commits only when needed, and documents the automation in the repository README. + +**Tech Stack:** GitHub Actions, Bash, `curl`, `jq`, Git + +--- + +### Task 1: Add the workflow + +**Files:** +- Create: `.github/workflows/sync-openapi-v3.yml` + +**Step 1: Draft workflow behavior** + +Define: +- `schedule` with a weekly cron +- `workflow_dispatch` +- `contents: write` permissions +- checkout, `jq` install, sync script run, diff detection, conditional commit and push + +**Step 2: Validate assumptions locally** + +Run: `scripts/update-openapi-v3.sh` +Expected: local snapshot refresh succeeds without manual edits + +**Step 3: Write minimal workflow** + +Create the YAML so it only automates the existing script and avoids unrelated build or release steps. + +**Step 4: Review for no-op safety** + +Confirm the commit step is gated so no commit is created when there is no diff. + +### Task 2: Document the automation + +**Files:** +- Modify: `README.md` + +**Step 1: Add automation section** + +Document: +- weekly schedule +- manual trigger path +- direct commit behavior + +**Step 2: Keep local workflow intact** + +Retain the existing local script instructions so the workflow and local operation stay aligned. + +### Task 3: Verify and finalize + +**Files:** +- Review: `.github/workflows/sync-openapi-v3.yml` +- Review: `README.md` + +**Step 1: Run the sync script** + +Run: `./scripts/update-openapi-v3.sh` +Expected: command succeeds and writes `openapi/openapi-v3.json` + +**Step 2: Inspect git diff** + +Run: `git diff -- .github/workflows/sync-openapi-v3.yml README.md docs/plans` +Expected: only the workflow, docs, and intentional README changes appear + +**Step 3: Commit** + +Run: +```bash +git add .github/workflows/sync-openapi-v3.yml README.md docs/plans +git commit -m "chore: automate openapi v3 sync" +``` + +Expected: a single commit capturing the workflow and docs diff --git a/openapi/openapi-v3.json b/openapi/openapi-v3.json index 46f4efa..ab6a84e 100644 --- a/openapi/openapi-v3.json +++ b/openapi/openapi-v3.json @@ -14,7 +14,7 @@ "type": "string" }, "email": { - "description": "The email address of the user. Only available for 'manager' and 'admin' account types.", + "description": "The email address of the user. Only available for 'manager' account types.", "examples": [ "manager@example.com" ], @@ -45,12 +45,12 @@ "enum": [ "manager", "viewer", - "admin" + "temporary" ], "type": "string" }, "username": { - "description": "The username of the user. Only available for 'viewer' account types.", + "description": "The username of the user. Only available for 'viewer' and 'temporary' account types.", "examples": [ "viewer_user" ], @@ -64,35 +64,47 @@ ], "type": "object" }, - "AuthAccountBody": { + "AssignOrganizationPermissionRequestBody": { "additionalProperties": false, "properties": { "$schema": { "description": "A URL to the JSON Schema for this object.", "examples": [ - "https://patch-api.conalog.com/schemas/AuthAccountBody.json" + "https://patch-api.conalog.com/schemas/AssignOrganizationPermissionRequestBody.json" ], "format": "uri", "readOnly": true, "type": "string" }, - "account": { - "description": "The user's account name or username used for authentication", + "email": { + "description": "User email to grant access for. Used when Role is manager", "examples": [ - "viewer_user" + "usr123456789012@conalog.com" ], "type": "string" }, - "password": { - "description": "The user's password for authentication", + "type": { + "description": "Account type of user.", + "enum": [ + "viewer", + "manager", + "temporary" + ], "examples": [ - "securepassword123" + "viewer" + ], + "type": "string" + }, + "username": { + "description": "User username to grant access for. Used when Role is viewer or temporary", + "examples": [ + "usr123456789012" ], "type": "string" } }, "required": [ - "account" + "type" ], "type": "object" }, @@ -129,38 +141,6 @@ ], "type": "object" }, - "AuthEmailBody": { - "additionalProperties": false, - "properties": { - "$schema": { - "description": "A URL to the JSON Schema for this object.", - "examples": [ - "https://patch-api.conalog.com/schemas/AuthEmailBody.json" - ], - "format": "uri", - "readOnly": true, - "type": "string" - }, - "email": { - "description": "The user's email address used for authentication", - "examples": [ - "manager@example.com" - ], - "type": "string" - }, - "password": { - "description": "The user's password for authentication", - "examples": [ - "securepassword123" - ], - "type": "string" - } - }, - "required": [ - "email" - ], - "type": "object" - }, "AuthMethodsBody": { "additionalProperties": false, "properties": { @@ -202,7 +182,7 @@ "type": "string" }, "email": { - "description": "The email address of the user. Only available for 'manager' and 'admin' account types.", + "description": "The email address of the user. Only available for 'manager' account types.", "examples": [ "manager@example.com" ], @@ -237,12 +217,12 @@ "enum": [ "manager", "viewer", - "admin" + "temporary" ], "type": "string" }, "username": { - "description": "The username of the user. Only available for 'viewer' account types.", + "description": "The username of the user. Only available for 'viewer' and 'temporary' account types.", "examples": [ "viewer_user" ], @@ -331,7 +311,7 @@ "type": "string" }, "email": { - "description": "The user's email address. Required when authenticating as a 'manager' account type.", + "description": "The user's email address. Accepted when authenticating as a 'manager' account type.", "examples": [ "manager@example.com" ], @@ -349,12 +329,13 @@ "description": "The type of account to authenticate. Determines the authentication method and access level.", "enum": [ "manager", - "viewer" + "viewer", + "temporary" ], "type": "string" }, "username": { - "description": "The user's username. Required when authenticating as a 'viewer' account type.", + "description": "The user's username. Accepted for 'manager' accounts and required when authenticating as a 'viewer' or 'temporary' account type.", "examples": [ "viewer_user" ], @@ -367,9 +348,108 @@ ], "type": "object" }, - "BodyInverterDailyData": { + "BlueprintListItem": { "additionalProperties": false, - "description": "Daily aggregated metrics for individual inverters at 1-day intervals.", + "properties": { + "created": { + "description": "Blueprint record creation timestamp.", + "type": "string" + }, + "date": { + "description": "Blueprint effective date.", + "type": "string" + }, + "id": { + "description": "Blueprint record ID.", + "type": "string" + }, + "updated": { + "description": "Blueprint record update timestamp.", + "type": "string" + } + }, + "required": [ + "id", + "date", + "updated" + ], + "type": "object" + }, + "BlueprintRecordPayload": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/BlueprintRecordPayload.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "created": { + "type": "string" + }, + "data": {}, + "date": { + "type": "string" + }, + "id": { + "type": "string" + }, + "metadata": {}, + "plant": { + "type": "string" + }, + "updated": { + "type": "string" + } + }, + "required": [ + "id", + "plant", + "date", + "updated", + "metadata", + "data" + ], + "type": "object" + }, + "BlueprintWriteBody": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/BlueprintWriteBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "data": { + "description": "Blueprint data payload." + }, + "date": { + "description": "Blueprint effective timestamp.", + "examples": [ + "2026-06-04 00:00:00.000Z" + ], + "type": "string" + }, + "metadata": { + "description": "Blueprint metadata." + } + }, + "required": [ + "date", + "data" + ], + "type": "object" + }, + "BodyESSPlantDailyData": { + "additionalProperties": false, + "description": "Plant-level energy storage system daily metrics at 1-day intervals.", "properties": { "before": { "description": "Number of historical intervals included in the response", @@ -379,7 +459,7 @@ "data": { "description": "Array of time-series data points matching the specified parameters", "items": { - "$ref": "#/components/schemas/InverterDailyData" + "$ref": "#/components/schemas/ESSPlantDailyData" }, "type": [ "array", @@ -415,12 +495,12 @@ "interval", "data" ], - "title": "Inverter Daily Metrics", + "title": "ESS Plant Daily Metrics", "type": "object" }, - "BodyInverterData": { + "BodyESSPlantIntradayData": { "additionalProperties": false, - "description": "Time-series metrics for individual inverters at 5-minute, 15-minute, and 1-hour intervals.", + "description": "Plant-level energy storage system metrics at 5-minute, 15-minute, and 1-hour intervals.", "properties": { "before": { "description": "Number of historical intervals included in the response", @@ -430,7 +510,7 @@ "data": { "description": "Array of time-series data points matching the specified parameters", "items": { - "$ref": "#/components/schemas/InverterData" + "$ref": "#/components/schemas/ESSPlantIntradayData" }, "type": [ "array", @@ -466,12 +546,12 @@ "interval", "data" ], - "title": "Inverter Intraday Metrics", + "title": "ESS Plant Intraday Metrics", "type": "object" }, - "BodyPanelDailyData": { + "BodyESSUnitDailyData": { "additionalProperties": false, - "description": "Daily aggregated metrics for individual panels at 1-day intervals.", + "description": "Energy storage system unit daily metrics at 1-day intervals.", "properties": { "before": { "description": "Number of historical intervals included in the response", @@ -481,7 +561,7 @@ "data": { "description": "Array of time-series data points matching the specified parameters", "items": { - "$ref": "#/components/schemas/PanelDailyData" + "$ref": "#/components/schemas/ESSUnitDailyData" }, "type": [ "array", @@ -517,12 +597,12 @@ "interval", "data" ], - "title": "Panel Daily Metrics", + "title": "ESS Unit Daily Metrics", "type": "object" }, - "BodyPanelData": { + "BodyESSUnitIntradayData": { "additionalProperties": false, - "description": "Time-series metrics for individual panels at 5-minute, 15-minute, and 1-hour intervals.", + "description": "Energy storage system unit metrics at 5-minute, 15-minute, and 1-hour intervals.", "properties": { "before": { "description": "Number of historical intervals included in the response", @@ -532,7 +612,7 @@ "data": { "description": "Array of time-series data points matching the specified parameters", "items": { - "$ref": "#/components/schemas/PanelData" + "$ref": "#/components/schemas/ESSUnitIntradayData" }, "type": [ "array", @@ -568,12 +648,12 @@ "interval", "data" ], - "title": "Panel Intraday Metrics", + "title": "ESS Unit Intraday Metrics", "type": "object" }, - "BodyPlantDailyData": { + "BodyInverterDailyData": { "additionalProperties": false, - "description": "Aggregated metrics for entire plant at 1-day, 1-month, and 1-year intervals.", + "description": "Daily aggregated metrics for individual inverters at 1-day intervals.", "properties": { "before": { "description": "Number of historical intervals included in the response", @@ -583,7 +663,7 @@ "data": { "description": "Array of time-series data points matching the specified parameters", "items": { - "$ref": "#/components/schemas/PlantDailyData" + "$ref": "#/components/schemas/InverterDailyData" }, "type": [ "array", @@ -619,12 +699,12 @@ "interval", "data" ], - "title": "Plant Daily/Monthly/Yearly Metrics", + "title": "Inverter Daily Metrics", "type": "object" }, - "BodyPlantData": { + "BodyInverterData": { "additionalProperties": false, - "description": "Time-series metrics for entire plant at 5-minute, 15-minute, and 1-hour intervals.", + "description": "Time-series metrics for individual inverters at 5-minute, 15-minute, and 1-hour intervals.", "properties": { "before": { "description": "Number of historical intervals included in the response", @@ -634,7 +714,7 @@ "data": { "description": "Array of time-series data points matching the specified parameters", "items": { - "$ref": "#/components/schemas/PlantData" + "$ref": "#/components/schemas/InverterData" }, "type": [ "array", @@ -670,12 +750,12 @@ "interval", "data" ], - "title": "Plant Intraday Metrics", + "title": "Inverter Intraday Metrics", "type": "object" }, - "BodySensorData": { + "BodyPanelDailyData": { "additionalProperties": false, - "description": "Time-series summary metrics for plant sensors at 5-minute intervals.", + "description": "Daily aggregated metrics for individual panels at 1-day intervals.", "properties": { "before": { "description": "Number of historical intervals included in the response", @@ -685,7 +765,7 @@ "data": { "description": "Array of time-series data points matching the specified parameters", "items": { - "$ref": "#/components/schemas/SensorData" + "$ref": "#/components/schemas/PanelDailyData" }, "type": [ "array", @@ -721,52 +801,255 @@ "interval", "data" ], - "title": "Sensor Intraday Metrics", + "title": "Panel Daily Metrics", "type": "object" }, - "CombinerItem": { + "BodyPanelData": { "additionalProperties": false, + "description": "Time-series metrics for individual panels at 5-minute, 15-minute, and 1-hour intervals.", "properties": { - "cancellation_date": { - "description": "Cancellation date value from the combiners asset catalog view.", - "type": "string" + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" }, - "category": { - "description": "Category value from the combiners asset catalog view.", - "type": "string" + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/PanelData" + }, + "type": [ + "array", + "null" + ] }, - "certification_date": { - "description": "Certification date value from the combiners asset catalog view.", + "date": { + "description": "Target date for the data retrieval", "type": "string" }, - "certification_target_type": { - "description": "Certification target type value from the combiners asset catalog view.", + "interval": { + "description": "Time interval used for data aggregation", "type": "string" }, - "created": { - "description": "Created timestamp value from the combiners asset catalog view.", + "plant_id": { + "description": "Plant identifier that was queried", "type": "string" }, - "depth_mm": { - "description": "Depth value from the combiners asset catalog view.", - "format": "double", - "type": "number" - }, - "equipment_code": { - "description": "Equipment code from the combiners asset catalog view.", + "source": { + "description": "Data source type that was queried", "type": "string" }, - "has_diode": { - "description": "Diode presence flag from the combiners asset catalog view.", + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Panel Intraday Metrics", + "type": "object" + }, + "BodyPlantDailyData": { + "additionalProperties": false, + "description": "Aggregated metrics for entire plant at 1-day, 1-month, and 1-year intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", "format": "int64", "type": "integer" }, - "height_fuse_mm": { - "description": "Height fuse value from the combiners asset catalog view.", - "format": "double", - "type": "number" + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/PlantDailyData" + }, + "type": [ + "array", + "null" + ] }, - "height_mm": { + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Plant Daily/Monthly/Yearly Metrics", + "type": "object" + }, + "BodyPlantData": { + "additionalProperties": false, + "description": "Time-series metrics for entire plant at 5-minute, 15-minute, and 1-hour intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/PlantData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Plant Intraday Metrics", + "type": "object" + }, + "BodySensorData": { + "additionalProperties": false, + "description": "Time-series summary metrics for plant sensors at 5-minute intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/SensorData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Sensor Intraday Metrics", + "type": "object" + }, + "CombinerItem": { + "additionalProperties": false, + "properties": { + "cancellation_date": { + "description": "Cancellation date value from the combiners asset catalog view.", + "type": "string" + }, + "category": { + "description": "Category value from the combiners asset catalog view.", + "type": "string" + }, + "certification_date": { + "description": "Certification date value from the combiners asset catalog view.", + "type": "string" + }, + "certification_target_type": { + "description": "Certification target type value from the combiners asset catalog view.", + "type": "string" + }, + "created": { + "description": "Created timestamp value from the combiners asset catalog view.", + "type": "string" + }, + "depth_mm": { + "description": "Depth value from the combiners asset catalog view.", + "format": "double", + "type": "number" + }, + "equipment_code": { + "description": "Equipment code from the combiners asset catalog view.", + "type": "string" + }, + "has_diode": { + "description": "Diode presence flag from the combiners asset catalog view.", + "type": "boolean" + }, + "height_fuse_mm": { + "description": "Height fuse value from the combiners asset catalog view.", + "format": "double", + "type": "number" + }, + "height_mm": { "description": "Height value from the combiners asset catalog view.", "format": "double", "type": "number" @@ -801,8 +1084,8 @@ }, "ip_rating": { "description": "IP rating value from the combiners asset catalog view.", - "format": "int64", - "type": "integer" + "format": "double", + "type": "number" }, "manufacturer": { "description": "Manufacturer value from the combiners asset catalog view.", @@ -851,8 +1134,7 @@ }, "rated_current_a": { "description": "Rated current value from the combiners asset catalog view.", - "format": "double", - "type": "number" + "type": "string" }, "rated_output_power_kva": { "description": "Rated output power value from the combiners asset catalog view.", @@ -861,8 +1143,8 @@ }, "string_count": { "description": "String count value from the combiners asset catalog view.", - "format": "int64", - "type": "integer" + "format": "double", + "type": "number" }, "technical_standard": { "description": "Technical standard value from the combiners asset catalog view.", @@ -888,93 +1170,293 @@ ], "type": "object" }, - "Comment": { + "CommentActionBody": { "additionalProperties": false, "properties": { - "created": { - "type": "string" - }, - "expand": { - "additionalProperties": {}, - "type": "object" - }, - "id": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/CommentActionBody.json" + ], + "format": "uri", + "readOnly": true, "type": "string" }, "images": { + "description": "Image file IDs attached to the comment.", "items": { "type": "string" }, - "type": [ - "array", - "null" - ] - }, - "isArchive": { - "type": [ - "boolean", - "null" - ] - }, - "isPrivate": { - "type": [ - "boolean", - "null" - ] - }, - "parent": { - "type": [ - "string", - "null" - ] + "type": "array" }, - "plant": { - "type": "string" + "map_ids": { + "description": "Plant map object IDs attached to the comment.", + "items": { + "minLength": 1, + "pattern": "^[^\\s\\p{Z}](?:[^\\r\\n\\x85\\p{Zl}\\p{Zp}]*[^\\s\\p{Z}])?$", + "patternDescription": "non-blank with no leading or trailing whitespace", + "type": "string" + }, + "type": "array" }, "related": { - "type": [ - "string", - "null" - ] - }, - "resolved": { - "type": [ - "string", - "null" - ] - }, - "tag": {}, - "text": { - "type": "string" - }, - "title": { - "type": "string" - }, - "updated": { + "description": "Related plant-data timestamp.", + "examples": [ + "2025-04-11 00:05:00.000Z" + ], "type": "string" }, - "user": { + "text": { + "description": "Comment text.", + "examples": [ + "입력전압 없음" + ], + "minLength": 1, "type": "string" } }, "required": [ - "id", - "plant", - "user", - "title", + "text" + ], + "type": "object" + }, + "CommentEditBody": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/CommentEditBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "images": { + "description": "Image file IDs attached to the comment.", + "items": { + "type": "string" + }, + "type": "array" + }, + "map_ids": { + "description": "Plant map object IDs attached to the comment.", + "items": { + "minLength": 1, + "pattern": "^[^\\s\\p{Z}](?:[^\\r\\n\\x85\\p{Zl}\\p{Zp}]*[^\\s\\p{Z}])?$", + "patternDescription": "non-blank with no leading or trailing whitespace", + "type": "string" + }, + "type": "array" + }, + "related": { + "description": "Related plant-data timestamp.", + "examples": [ + "2025-04-11 00:05:00.000Z" + ], + "type": "string" + }, + "text": { + "description": "Comment text.", + "examples": [ + "입력전압 없음" + ], + "minLength": 1, + "type": "string" + } + }, + "type": "object" + }, + "CommentOutput": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/CommentOutput.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "created": { + "type": "string" + }, + "expand": { + "additionalProperties": {}, + "type": "object" + }, + "id": { + "type": "string" + }, + "images": { + "description": "Array of image file URLs attached to the comment.", + "examples": [ + [ + "https://example.org/api/files/w8l3o9jedinck7g/img123/blob.jpg" + ] + ], + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "map_ids": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "parent": { + "type": "string" + }, + "related": { + "type": "string" + }, + "resolved": { + "type": "string" + }, + "text": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/CommentUserOutput" + } + }, + "required": [ + "id", "text", - "images", - "tag", - "parent", - "resolved", - "related", - "isPrivate", - "isArchive", + "user", + "created", + "updated" + ], + "type": "object" + }, + "CommentReadOutput": { + "additionalProperties": false, + "properties": { + "created": { + "type": "string" + }, + "id": { + "type": "string" + }, + "images": { + "description": "Array of image file URLs attached to the comment.", + "examples": [ + [ + "https://example.org/api/files/w8l3o9jedinck7g/img123/blob.jpg" + ] + ], + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "map_ids": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "parent": { + "type": "string" + }, + "related": { + "type": "string" + }, + "resolved": { + "type": "string" + }, + "text": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/CommentUserOutput" + } + }, + "required": [ + "id", + "text", + "user", "created", "updated" ], "type": "object" }, + "CommentStateBody": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/CommentStateBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "transition": { + "description": "State transition to apply to the comment.", + "enum": [ + "resolve", + "reopen", + "archive", + "restore" + ], + "examples": [ + "resolve" + ], + "type": "string" + } + }, + "required": [ + "transition" + ], + "type": "object" + }, + "CommentUserOutput": { + "additionalProperties": false, + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "username", + "email" + ], + "type": "object" + }, "CreateAccountOutputBody": { "additionalProperties": false, "properties": { @@ -988,12 +1470,16 @@ "type": "string" }, "email": { - "description": "The email address of the user. Only available for 'manager' and 'admin' account types.", + "description": "The email address of the user. Only available for 'manager' account types.", "examples": [ "manager@example.com" ], "type": "string" }, + "expired": { + "description": "Expiration timestamp for temporary accounts.", + "type": "string" + }, "id": { "description": "Unique identifier for the user within the system.", "examples": [ @@ -1001,11 +1487,15 @@ ], "type": "string" }, + "link": { + "description": "Generated onsite link for temporary accounts.", + "type": "string" + }, "metadata": { "description": "Additional metadata associated with the user account. May include custom fields, preferences, or configuration data. Not available for viewer accounts." }, "name": { - "description": "The display name of the user.", + "description": "The display name of the created manager or viewer account.", "examples": [ "John Doe" ], @@ -1026,12 +1516,12 @@ "enum": [ "manager", "viewer", - "admin" + "temporary" ], "type": "string" }, "username": { - "description": "The username of the user. Only available for 'viewer' account types.", + "description": "The username of the user. Only available for 'viewer' and 'temporary' account types.", "examples": [ "viewer_user" ], @@ -1041,11 +1531,50 @@ "required": [ "id", "type", - "name", "organizations" ], "type": "object" }, + "CreateFilterBody": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/CreateFilterBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "map_ids": { + "description": "Plant map object IDs included in this filter.", + "items": { + "minLength": 1, + "pattern": "^[^\\s\\p{Z}](?:[^\\r\\n\\x85\\p{Zl}\\p{Zp}]*[^\\s\\p{Z}])?$", + "patternDescription": "non-blank with no leading or trailing whitespace", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "name": { + "description": "Filter name.", + "examples": [ + "Inverter A" + ], + "minLength": 1, + "pattern": "^[^\\s\\p{Z}](?:[^\\r\\n\\x85\\p{Zl}\\p{Zp}]*[^\\s\\p{Z}])?$", + "patternDescription": "non-blank with no leading or trailing whitespace", + "type": "string" + } + }, + "required": [ + "name", + "map_ids" + ], + "type": "object" + }, "CreateOrganizationMemberRequestBody": { "additionalProperties": false, "properties": { @@ -1152,9 +1681,6 @@ "isArchive": { "type": "boolean" }, - "isPrivate": { - "type": "boolean" - }, "parent": { "type": "string" }, @@ -1178,7 +1704,7 @@ "additionalProperties": false, "properties": { "email": { - "description": "The email address of the user. Only available for 'manager' and 'admin' account types.", + "description": "The email address of the user. Only available for 'manager' account types.", "examples": [ "manager@example.com" ], @@ -1188,14 +1714,14 @@ "description": "Additional metadata associated with the user account. May include custom fields, preferences, or configuration data. Not available for viewer accounts." }, "name": { - "description": "The display name of the user.", + "description": "The display name of the user. Only used for 'manager' and 'viewer' account types.", "examples": [ "John Doe" ], "type": "string" }, "password": { - "description": "The user's password for authentication", + "description": "The user's password for authentication. Temporary account passwords are generated by the server and returned in the create response.", "examples": [ "securepassword123" ], @@ -1205,12 +1731,13 @@ "description": "The type of account indicating the user's access level and permissions. Determines available operations and data access.", "enum": [ "manager", - "viewer" + "viewer", + "temporary" ], "type": "string" }, "username": { - "description": "The username of the user. Only available for 'viewer' account types.", + "description": "The username of the user. Only used for 'viewer' account types. Temporary account usernames are generated by the server.", "examples": [ "viewer_user" ], @@ -1218,9 +1745,7 @@ } }, "required": [ - "type", - "password", - "name" + "type" ], "type": "object" }, @@ -1249,21 +1774,350 @@ ], "type": "object" }, - "ErrorDetail": { + "ESSPlantDailyData": { "additionalProperties": false, "properties": { - "location": { - "description": "Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'", - "type": "string" + "charge_energy": { + "description": "Daily charged energy total (kWh)", + "format": "double", + "type": "number" }, - "message": { - "description": "Error message text", + "date": { + "description": "Asia/Seoul local date represented by the row", "type": "string" }, - "value": { - "description": "The value at the given location" - } - }, + "discharge_energy": { + "description": "Daily discharged or output energy total (kWh)", + "format": "double", + "type": "number" + }, + "energy": { + "description": "Daily positive output energy total (kWh)", + "format": "double", + "type": "number" + }, + "plant_id": { + "description": "Plant identifier for the local date partition", + "type": "string" + }, + "pv_energy": { + "description": "Daily PV-side generated energy total (kWh)", + "format": "double", + "type": "number" + } + }, + "required": [ + "plant_id", + "date", + "energy", + "charge_energy", + "discharge_energy", + "pv_energy" + ], + "type": "object" + }, + "ESSPlantIntradayData": { + "additionalProperties": false, + "properties": { + "battery_current": { + "description": "Battery current (A); positive means net discharge and negative means net charge", + "format": "double", + "type": "number" + }, + "battery_power": { + "description": "Battery power magnitude during the interval (kW)", + "format": "double", + "type": "number" + }, + "battery_voltage": { + "description": "Battery voltage (V)", + "format": "double", + "type": "number" + }, + "charge_energy": { + "description": "Energy charged into the battery during the interval (kWh)", + "format": "double", + "type": "number" + }, + "discharge_energy": { + "description": "Energy discharged or output from the battery during the interval (kWh)", + "format": "double", + "type": "number" + }, + "output_a_current": { + "description": "AC output phase A current (A)", + "format": "double", + "type": "number" + }, + "output_a_voltage": { + "description": "AC output phase A voltage (V)", + "format": "double", + "type": "number" + }, + "output_b_current": { + "description": "AC output phase B current (A)", + "format": "double", + "type": "number" + }, + "output_b_voltage": { + "description": "AC output phase B voltage (V)", + "format": "double", + "type": "number" + }, + "output_c_current": { + "description": "AC output phase C current (A)", + "format": "double", + "type": "number" + }, + "output_c_voltage": { + "description": "AC output phase C voltage (V)", + "format": "double", + "type": "number" + }, + "pv_current": { + "description": "PV DC current (A)", + "format": "double", + "type": "number" + }, + "pv_energy": { + "description": "PV-side generated energy during the interval (kWh)", + "format": "double", + "type": "number" + }, + "pv_voltage": { + "description": "PV DC voltage (V)", + "format": "double", + "type": "number" + }, + "time": { + "description": "Asia/Seoul interval start time label in HH:MM format", + "type": "string" + }, + "timestamp": { + "description": "Interval start Unix timestamp interpreted from the local date", + "format": "int64", + "type": "integer" + }, + "total_charge_energy": { + "description": "Cumulative charge energy display value at the interval timestamp (kWh)", + "format": "double", + "type": "number" + }, + "total_discharge_energy": { + "description": "Cumulative discharge energy display value at the interval timestamp (kWh)", + "format": "double", + "type": "number" + } + }, + "required": [ + "charge_energy", + "discharge_energy", + "pv_energy", + "battery_power", + "output_a_voltage", + "output_b_voltage", + "output_c_voltage", + "output_a_current", + "output_b_current", + "output_c_current", + "battery_voltage", + "battery_current", + "pv_voltage", + "pv_current", + "total_charge_energy", + "total_discharge_energy", + "time", + "timestamp" + ], + "type": "object" + }, + "ESSUnitDailyData": { + "additionalProperties": false, + "properties": { + "charge_energy": { + "description": "Daily charged energy total (kWh)", + "format": "double", + "type": "number" + }, + "date": { + "description": "Asia/Seoul local date represented by the row", + "type": "string" + }, + "discharge_energy": { + "description": "Daily discharged or output energy total (kWh)", + "format": "double", + "type": "number" + }, + "energy": { + "description": "Daily positive output energy total (kWh)", + "format": "double", + "type": "number" + }, + "id": { + "description": "ESS device identifier", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier for the local date partition", + "type": "string" + }, + "pv_energy": { + "description": "Daily PV-side generated energy total (kWh)", + "format": "double", + "type": "number" + }, + "updated": { + "description": "Timestamp with time zone indicating when the daily row was updated", + "type": "string" + } + }, + "required": [ + "plant_id", + "id", + "date", + "energy", + "charge_energy", + "discharge_energy", + "pv_energy", + "updated" + ], + "type": "object" + }, + "ESSUnitIntradayData": { + "additionalProperties": false, + "properties": { + "battery_current": { + "description": "Battery current (A); positive means net discharge and negative means net charge", + "format": "double", + "type": "number" + }, + "battery_power": { + "description": "Battery power magnitude during the interval (kW)", + "format": "double", + "type": "number" + }, + "battery_voltage": { + "description": "Battery voltage (V)", + "format": "double", + "type": "number" + }, + "charge_energy": { + "description": "Energy charged into the battery during the interval (kWh)", + "format": "double", + "type": "number" + }, + "discharge_energy": { + "description": "Energy discharged or output from the battery during the interval (kWh)", + "format": "double", + "type": "number" + }, + "id": { + "description": "ESS device identifier", + "type": "string" + }, + "output_a_current": { + "description": "AC output phase A current (A)", + "format": "double", + "type": "number" + }, + "output_a_voltage": { + "description": "AC output phase A voltage (V)", + "format": "double", + "type": "number" + }, + "output_b_current": { + "description": "AC output phase B current (A)", + "format": "double", + "type": "number" + }, + "output_b_voltage": { + "description": "AC output phase B voltage (V)", + "format": "double", + "type": "number" + }, + "output_c_current": { + "description": "AC output phase C current (A)", + "format": "double", + "type": "number" + }, + "output_c_voltage": { + "description": "AC output phase C voltage (V)", + "format": "double", + "type": "number" + }, + "pv_current": { + "description": "PV DC current (A)", + "format": "double", + "type": "number" + }, + "pv_energy": { + "description": "PV-side generated energy during the interval (kWh)", + "format": "double", + "type": "number" + }, + "pv_voltage": { + "description": "PV DC voltage (V)", + "format": "double", + "type": "number" + }, + "time": { + "description": "Asia/Seoul interval start time label in HH:MM format", + "type": "string" + }, + "timestamp": { + "description": "Interval start Unix timestamp interpreted from the local date", + "format": "int64", + "type": "integer" + }, + "total_charge_energy": { + "description": "Cumulative charge energy display value at the interval timestamp (kWh)", + "format": "double", + "type": "number" + }, + "total_discharge_energy": { + "description": "Cumulative discharge energy display value at the interval timestamp (kWh)", + "format": "double", + "type": "number" + } + }, + "required": [ + "id", + "charge_energy", + "discharge_energy", + "pv_energy", + "battery_power", + "output_a_voltage", + "output_b_voltage", + "output_c_voltage", + "output_a_current", + "output_b_current", + "output_c_current", + "battery_voltage", + "battery_current", + "pv_voltage", + "pv_current", + "total_charge_energy", + "total_discharge_energy", + "time", + "timestamp" + ], + "type": "object" + }, + "ErrorDetail": { + "additionalProperties": false, + "properties": { + "location": { + "description": "Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'", + "type": "string" + }, + "message": { + "description": "Error message text", + "type": "string" + }, + "value": { + "description": "The value at the given location" + } + }, "type": "object" }, "ErrorModel": { @@ -1383,46 +2237,137 @@ ], "type": "object" }, - "HealthLevelBody": { + "FilterListItem": { "additionalProperties": false, "properties": { - "$schema": { - "description": "A URL to the JSON Schema for this object.", - "examples": [ - "https://patch-api.conalog.com/schemas/HealthLevelBody.json" - ], - "format": "uri", - "readOnly": true, + "condition": { + "description": "JSONLogic condition body for non-merge filters." + }, + "created": { + "description": "Filter record creation timestamp.", "type": "string" }, - "best": { - "$ref": "#/components/schemas/HealthLevelCategory" + "id": { + "description": "Filter record ID.", + "type": "string" }, - "caution": { - "$ref": "#/components/schemas/HealthLevelCategory" + "map_ids": { + "description": "Plant map object IDs included in this filter.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] }, - "faulty": { - "$ref": "#/components/schemas/HealthLevelCategory" + "name": { + "description": "Filter name.", + "type": "string" + }, + "updated": { + "description": "Filter record update timestamp.", + "type": "string" } }, "required": [ - "best", - "caution", - "faulty" + "id", + "name", + "map_ids", + "updated" ], "type": "object" }, - "HealthLevelCategory": { + "FilterOutput": { "additionalProperties": false, "properties": { - "count": { - "format": "int64", - "type": "integer" + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/FilterOutput.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" }, - "ids": { - "items": { - "type": "string" - }, + "condition": { + "description": "JSONLogic condition body for non-merge filters." + }, + "created": { + "description": "Filter record creation timestamp.", + "type": "string" + }, + "id": { + "description": "Filter record ID.", + "type": "string" + }, + "map_ids": { + "description": "Plant map object IDs included in this filter.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "name": { + "description": "Filter name.", + "type": "string" + }, + "updated": { + "description": "Filter record update timestamp.", + "type": "string" + } + }, + "required": [ + "id", + "name", + "map_ids", + "updated" + ], + "type": "object" + }, + "HealthLevelBody": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/HealthLevelBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "best": { + "$ref": "#/components/schemas/HealthLevelCategory" + }, + "caution": { + "$ref": "#/components/schemas/HealthLevelCategory" + }, + "faulty": { + "$ref": "#/components/schemas/HealthLevelCategory" + } + }, + "required": [ + "best", + "caution", + "faulty" + ], + "type": "object" + }, + "HealthLevelCategory": { + "additionalProperties": false, + "properties": { + "count": { + "format": "int64", + "type": "integer" + }, + "ids": { + "items": { + "type": "string" + }, "type": "array" } }, @@ -1431,6 +2376,103 @@ ], "type": "object" }, + "ImageUploadResponse": { + "additionalProperties": false, + "properties": { + "created": { + "description": "Timestamp when the image record was created", + "type": "string" + }, + "filename": { + "description": "Original filename of the uploaded image", + "type": "string" + }, + "id": { + "description": "Image record ID. Use this value in comment image attachment requests.", + "type": "string" + }, + "plant_id": { + "description": "Plant ID from the upload route path", + "type": "string" + }, + "size": { + "description": "Image file size in bytes", + "format": "int64", + "type": "integer" + }, + "updated": { + "description": "Timestamp when the image record was last updated", + "type": "string" + } + }, + "required": [ + "id", + "plant_id", + "filename", + "size", + "created", + "updated" + ], + "type": "object" + }, + "InternalInvalidateAuthValidationCacheBody": { + "additionalProperties": false, + "properties": { + "cache_key": { + "pattern": "^token-auth/v2/(manager|viewer|temporary)/[a-f0-9]{64}$", + "type": "string" + } + }, + "required": [ + "cache_key" + ], + "type": "object" + }, + "InternalInvalidateAuthValidationCacheResult": { + "additionalProperties": false, + "properties": { + "accepted": { + "type": "boolean" + }, + "deleted": { + "format": "int64", + "type": "integer" + } + }, + "required": [ + "accepted", + "deleted" + ], + "type": "object" + }, + "InternalInvalidatePlantListCacheInputBody": { + "additionalProperties": false, + "properties": { + "account_id": { + "type": "string" + }, + "account_type": { + "type": "string" + } + }, + "required": [ + "account_type", + "account_id" + ], + "type": "object" + }, + "InternalInvalidatePlantListCacheOutputBody": { + "additionalProperties": false, + "properties": { + "accepted": { + "type": "boolean" + } + }, + "required": [ + "accepted" + ], + "type": "object" + }, "InternalWarmCacheBody": { "additionalProperties": false, "properties": { @@ -1473,6 +2515,7 @@ "type": "string" }, "plant_id": { + "description": "Physical data plant ID to warm. RefPlant-backed public requests read canonical data plant cache keys, so warm targets should use the physical data plant ID.", "maxLength": 15, "minLength": 15, "type": "string" @@ -1480,7 +2523,8 @@ "source": { "enum": [ "device", - "inverter" + "inverter", + "ess" ], "type": "string" }, @@ -1492,6 +2536,7 @@ "enum": [ "panel", "inverter", + "ess", "string", "plant" ], @@ -1929,6 +2974,31 @@ ], "type": "object" }, + "InverterModelStat": { + "additionalProperties": false, + "properties": { + "count": { + "description": "Active inverter count for the model name at the end of the requested day.", + "format": "int64", + "type": "integer" + }, + "installed_capacity_w": { + "description": "Installed capacity in watts attributed to the inverter model at the end of the requested day.", + "format": "double", + "type": "number" + }, + "name": { + "description": "Active inverter model name at the end of the requested day.", + "type": "string" + } + }, + "required": [ + "name", + "count", + "installed_capacity_w" + ], + "type": "object" + }, "LatestDeviceBody": { "additionalProperties": false, "properties": { @@ -1950,9 +3020,6 @@ "metrics": { "$ref": "#/components/schemas/LatestDeviceBodyMetricsStruct" }, - "plant_id": { - "type": "string" - }, "state": { "additionalProperties": { "type": "boolean" @@ -1970,7 +3037,6 @@ "asset_type", "map_id", "map_type", - "plant_id", "edge_id", "metrics", "state" @@ -2097,9 +3163,7 @@ "type": "string" }, "cell_specification": { - "additionalProperties": {}, - "description": "Cell specification object from the modules asset catalog view.", - "type": "object" + "description": "Cell specification value from the modules asset catalog view." }, "certification_date": { "description": "Certification date value from the modules asset catalog view.", @@ -2222,13 +3286,13 @@ ], "type": "object" }, - "OrgAddPermissionInputBody": { + "OrgAddPermissionOutputBody": { "additionalProperties": false, "properties": { "$schema": { "description": "A URL to the JSON Schema for this object.", "examples": [ - "https://patch-api.conalog.com/schemas/OrgAddPermissionInputBody.json" + "https://patch-api.conalog.com/schemas/OrgAddPermissionOutputBody.json" ], "format": "uri", "readOnly": true, @@ -2241,28 +3305,20 @@ ], "type": "string" }, - "plantId": { - "description": "Target plant ID to grant access to.", - "examples": [ - "pln123456789012" - ], - "maxLength": 15, - "minLength": 15, + "plant_id": { "type": "string" }, "type": { - "description": "Account type of user.", + "description": "The type of account indicating the user's access level and permissions. Determines available operations and data access.", "enum": [ + "manager", "viewer", - "manager" - ], - "examples": [ - "viewer" + "temporary" ], "type": "string" }, "username": { - "description": "User username to grant access for. Used when Role is viewer", + "description": "User username to grant access for. Used when Role is viewer or temporary", "examples": [ "usr123456789012" ], @@ -2270,25 +3326,53 @@ } }, "required": [ - "plantId", + "plant_id", "type" ], "type": "object" }, - "OrgAddPermissionOutputBody": { + "OrgInfo": { + "additionalProperties": false, + "properties": { + "icon": { + "type": "string" + }, + "id": { + "type": "string" + }, + "logo": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "updated": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + }, + "OrgRemovePermissionOutputBody": { "additionalProperties": false, "properties": { "$schema": { "description": "A URL to the JSON Schema for this object.", "examples": [ - "https://patch-api.conalog.com/schemas/OrgAddPermissionOutputBody.json" + "https://patch-api.conalog.com/schemas/OrgRemovePermissionOutputBody.json" ], "format": "uri", "readOnly": true, "type": "string" }, "email": { - "description": "User email to grant access for. Used when Role is manager", + "description": "User email revoked. Used when Role is manager", "examples": [ "usr123456789012@conalog.com" ], @@ -2298,15 +3382,16 @@ "type": "string" }, "type": { - "description": "The type of account indicating the user's access level and permissions. Determines available operations and data access.", + "description": "The type of account whose plant access was removed.", "enum": [ "manager", - "viewer" + "viewer", + "temporary" ], "type": "string" }, "username": { - "description": "User username to grant access for. Used when Role is viewer", + "description": "User username revoked. Used when Role is viewer or temporary", "examples": [ "usr123456789012" ], @@ -2319,34 +3404,6 @@ ], "type": "object" }, - "OrgInfo": { - "additionalProperties": false, - "properties": { - "icon": { - "type": "string" - }, - "id": { - "type": "string" - }, - "logo": { - "type": "string" - }, - "name": { - "type": "string" - }, - "owner": { - "type": "string" - }, - "updated": { - "type": "string" - } - }, - "required": [ - "id", - "name" - ], - "type": "object" - }, "OrganizationBody": { "additionalProperties": false, "properties": { @@ -2388,6 +3445,10 @@ "PanelDailyData": { "additionalProperties": false, "properties": { + "date": { + "description": "Date of the data point in YYYY-MM-DD format", + "type": "string" + }, "energy": { "description": "Total daily energy production (kWh)", "format": "double", @@ -2400,6 +3461,7 @@ }, "required": [ "id", + "date", "energy" ], "type": "object" @@ -2407,38 +3469,81 @@ "PanelData": { "additionalProperties": false, "properties": { + "avg_seqnum": { + "description": "Average sequence number indicator for the panel, or null when the source parquet does not contain this optional metric.", + "format": "double", + "type": [ + "number", + "null" + ] + }, + "count": { + "description": "Data count for the panel interval, or null when the source parquet does not contain this optional metric.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cumulative_energy": { - "description": "Cumulative energy production up to this point (kWh)", + "description": "Cumulative energy production up to this point (kWh), or null when the source parquet does not contain this optional metric.", "format": "double", - "type": "number" + "type": [ + "number", + "null" + ] }, "date": { - "description": "Date of the data point in YYYY-MM-DD format", + "description": "Date/time label from the panel-all parquet row.", "type": "string" }, "energy": { - "description": "Energy production for the time interval (kWh)", + "description": "Energy production for the time interval (kWh), or null when the source parquet does not contain this optional metric.", "format": "double", - "type": "number" + "type": [ + "number", + "null" + ] }, "i_out": { - "description": "Output current measurement (Amperes)", + "description": "Output current measurement (Amperes), or null when the source parquet does not contain this optional metric.", "format": "double", - "type": "number" + "type": [ + "number", + "null" + ] }, "id": { "description": "Unique identifier for the solar panel", "type": "string" }, + "min_seqnum": { + "description": "Minimum sequence number indicator for the panel, or null when the source parquet does not contain this optional metric.", + "format": "double", + "type": [ + "number", + "null" + ] + }, "p": { - "description": "Power output measurement (Watts)", + "description": "Power output measurement (Watts), or null when the source parquet does not contain this optional metric.", "format": "double", - "type": "number" + "type": [ + "number", + "null" + ] }, "temp": { - "description": "Panel temperature measurement (Celsius)", + "description": "Panel temperature measurement (Celsius), or null when the source parquet does not contain this optional metric.", "format": "double", - "type": "number" + "type": [ + "number", + "null" + ] + }, + "time": { + "description": "Same time label as date for device panel intraday rows.", + "type": "string" }, "timestamp": { "description": "Unix timestamp of the measurement", @@ -2446,27 +3551,37 @@ "type": "integer" }, "v_in": { - "description": "Input voltage measurement (Volts)", + "description": "Input voltage measurement (Volts), or null when the source parquet does not contain this optional metric.", "format": "double", - "type": "number" + "type": [ + "number", + "null" + ] }, "v_out": { - "description": "Output voltage measurement (Volts)", + "description": "Output voltage measurement (Volts), or null when the source parquet does not contain this optional metric.", "format": "double", - "type": "number" + "type": [ + "number", + "null" + ] } }, "required": [ - "id", - "date", - "timestamp", - "energy", - "cumulative_energy", "i_out", "p", "v_in", "v_out", - "temp" + "temp", + "avg_seqnum", + "min_seqnum", + "count", + "energy", + "cumulative_energy", + "id", + "date", + "time", + "timestamp" ], "type": "object" }, @@ -2759,9 +3874,58 @@ ], "type": "object" }, + "Record": { + "additionalProperties": false, + "properties": { + "detected": { + "type": "string" + }, + "id": { + "type": "string" + }, + "map_id": { + "type": "string" + }, + "map_type": { + "type": "string" + }, + "plant_id": { + "type": "string" + }, + "resolved": { + "type": "string" + }, + "severity": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "plant_id", + "map_type", + "map_id", + "type", + "severity", + "detected", + "resolved" + ], + "type": "object" + }, "RegisterBody": { "additionalProperties": false, "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/RegisterBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, "asset_id": { "description": "Unique identifier for the physical asset to be registered (e.g., device serial number, inverter ID)", "examples": [ @@ -2769,12 +3933,20 @@ ], "type": "string" }, + "asset_model": { + "additionalProperties": {}, + "description": "Optional asset model information to store with the registry record.", + "type": "object" + }, "asset_type": { "description": "Classification of the physical asset being registered", "enum": [ "device", "inverter", - "edge" + "edge", + "panel", + "panel_group", + "sensor" ], "examples": [ "device" @@ -2796,7 +3968,10 @@ "edge", "inverter", "combiner", - "panel" + "panel", + "panel_group", + "tracker", + "sensor" ], "examples": [ "panel" @@ -2812,18 +3987,35 @@ "type": "string" }, "registered_meta": { - "description": "Additional metadata associated with the registration event (JSON string or custom format)", - "examples": [ - "{\"installer\": \"John Doe\", \"warranty\": \"2029-01-24\"}" - ], - "type": "string" + "description": "Registration action metadata. Structured form accepts an object with JSON values; legacy string is still accepted.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Registration action metadata as a flat object with JSON values. Limited to 4096 bytes, 32 keys, key length 64, and value JSON size 1024 bytes.", + "maxProperties": 32, + "type": "object" + }, + { + "deprecated": true, + "description": "Deprecated compatibility form. Use structured actor metadata instead. The full meta JSON value is limited to 4096 bytes.", + "maxLength": 4094, + "type": "string" + } + ] }, "tag": { - "description": "Optional descriptive tag or label for the asset (e.g., location, purpose, or custom identifier)", - "examples": [ - "North Array Panel 5" - ], - "type": "string" + "description": "Optional tag metadata for the asset.", + "oneOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "deprecated": true, + "description": "Deprecated compatibility form. Use an object instead.", + "type": "string" + } + ] } }, "required": [ @@ -2835,6 +4027,20 @@ ], "type": "object" }, + "RegistryMeta": { + "additionalProperties": false, + "properties": { + "fielder_name": { + "description": "Name of the field worker who performed the registry event.", + "type": "string" + }, + "fielder_org": { + "description": "Organization of the field worker who performed the registry event.", + "type": "string" + } + }, + "type": "object" + }, "RegistryOutputBody": { "additionalProperties": false, "properties": { @@ -2852,7 +4058,10 @@ "enum": [ "device", "inverter", - "edge" + "edge", + "panel", + "panel_group", + "sensor" ], "type": "string" }, @@ -2868,7 +4077,10 @@ "edge", "inverter", "combiner", - "panel" + "panel", + "panel_group", + "tracker", + "sensor" ], "type": "string" }, @@ -2876,14 +4088,31 @@ "description": "Timestamp when the asset was registered in RFC3339 format", "type": "string" }, + "registered_meta": { + "$ref": "#/components/schemas/RegistryMeta", + "description": "Sanitized registration actor metadata. Only fielder_name and fielder_org are exposed." + }, "tag": { - "additionalProperties": {}, "description": "Additional metadata and tags associated with the asset registration", - "type": "object" + "oneOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "deprecated": true, + "description": "Deprecated compatibility form. Use an object instead.", + "type": "string" + } + ] }, "unregistered": { "description": "Timestamp when the asset was unregistered in RFC3339 format (empty if still registered)", "type": "string" + }, + "unregistered_meta": { + "$ref": "#/components/schemas/RegistryMeta", + "description": "Sanitized unregistration actor metadata. Only fielder_name and fielder_org are exposed." } }, "required": [ @@ -2898,9 +4127,82 @@ ], "type": "object" }, + "RemoveOrganizationPermissionRequestBody": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/RemoveOrganizationPermissionRequestBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "email": { + "description": "User email to revoke access from. Used when Role is manager", + "examples": [ + "usr123456789012@conalog.com" + ], + "type": "string" + }, + "type": { + "description": "Account type of user.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "examples": [ + "viewer" + ], + "type": "string" + }, + "username": { + "description": "User username to revoke access from. Used when Role is viewer or temporary", + "examples": [ + "usr123456789012" + ], + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "RenameFilterBody": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/RenameFilterBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "name": { + "description": "Filter name.", + "examples": [ + "Inverter A" + ], + "minLength": 1, + "pattern": "^[^\\s\\p{Z}](?:[^\\r\\n\\x85\\p{Zl}\\p{Zp}]*[^\\s\\p{Z}])?$", + "patternDescription": "non-blank with no leading or trailing whitespace", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, "Report": { "additionalProperties": false, "properties": { + "content": {}, "created": { "type": "string" }, @@ -2932,6 +4234,7 @@ "start": { "type": "string" }, + "summary": {}, "title": { "type": "string" }, @@ -3006,42 +4309,88 @@ ], "type": "object" }, - "StatModelCount": { + "SetTemporaryExpiredBody": { "additionalProperties": false, "properties": { - "count": { - "description": "Active row count for the model name at the timestamp.", - "format": "int64", - "type": "integer" + "expired": { + "description": "Expiration timestamp to set on the temporary account.", + "examples": [ + "2026-06-11 00:00:00.000Z" + ], + "type": "string" }, - "name": { - "description": "Model name for the active panel or panel_group rows at the timestamp.", + "id": { + "description": "Temporary account record ID.", + "examples": [ + "temporary123456" + ], "type": "string" } }, "required": [ - "name", - "count" + "id", + "expired" ], "type": "object" }, - "StatPoint": { + "SetTemporaryExpiredOutputBody": { "additionalProperties": false, "properties": { - "$schema": { - "description": "A URL to the JSON Schema for this object.", - "examples": [ - "https://patch-api.conalog.com/schemas/StatPoint.json" - ], - "format": "uri", - "readOnly": true, + "expired": { + "description": "Updated expiration timestamp.", "type": "string" }, - "device_models": { - "description": "Active device model stats at the end of the requested day.", - "items": { - "$ref": "#/components/schemas/DeviceModelStat" - }, + "id": { + "description": "Temporary account record ID.", + "type": "string" + } + }, + "required": [ + "id", + "expired" + ], + "type": "object" + }, + "StatModelCount": { + "additionalProperties": false, + "properties": { + "count": { + "description": "Active row count for the model name at the timestamp.", + "format": "int64", + "type": "integer" + }, + "name": { + "description": "Model name for the active panel or panel_group rows at the timestamp.", + "type": "string" + } + }, + "required": [ + "name", + "count" + ], + "type": "object" + }, + "StatPoint": { + "additionalProperties": false, + "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/StatPoint.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, + "all_asset_models_registered": { + "description": "Whether every active stat-covered registry record has required asset model information registered at the end of the requested day. Panels may inherit a panel_group model. Edge, sensor, tracker, combiner, string, and other non-stat records are ignored.", + "type": "boolean" + }, + "device_models": { + "description": "Active device model stats at the end of the requested day.", + "items": { + "$ref": "#/components/schemas/DeviceModelStat" + }, "type": [ "array", "null" @@ -3052,6 +4401,16 @@ "format": "double", "type": "number" }, + "inverter_models": { + "description": "Active inverter model stats at the end of the requested day.", + "items": { + "$ref": "#/components/schemas/InverterModelStat" + }, + "type": [ + "array", + "null" + ] + }, "module_models": { "description": "Active panel and panel_group model counts at the end of the requested day.", "items": { @@ -3070,14 +4429,25 @@ "required": [ "timestamp", "installed_capacity_w", + "all_asset_models_registered", "module_models", - "device_models" + "device_models", + "inverter_models" ], "type": "object" }, "UnregisterBody": { "additionalProperties": false, "properties": { + "$schema": { + "description": "A URL to the JSON Schema for this object.", + "examples": [ + "https://patch-api.conalog.com/schemas/UnregisterBody.json" + ], + "format": "uri", + "readOnly": true, + "type": "string" + }, "asset_id": { "description": "Unique identifier for the physical asset to be unregistered (must match the originally registered asset ID)", "examples": [ @@ -3090,7 +4460,10 @@ "enum": [ "device", "inverter", - "edge" + "edge", + "panel", + "panel_group", + "sensor" ], "examples": [ "device" @@ -3112,20 +4485,16 @@ "edge", "inverter", "combiner", - "panel" + "panel", + "panel_group", + "tracker", + "sensor" ], "examples": [ "panel" ], "type": "string" }, - "tag": { - "description": "Optional descriptive tag or label for the asset (should match the originally registered tag if applicable)", - "examples": [ - "North Array Panel 5" - ], - "type": "string" - }, "unregistered": { "description": "Timestamp when the asset should be considered unregistered. Must be in RFC3339 format and should be after the original registration date.", "examples": [ @@ -3135,11 +4504,21 @@ "type": "string" }, "unregistered_meta": { - "description": "Additional metadata associated with the unregistration event (JSON string or custom format)", - "examples": [ - "{\"reason\": \"Equipment replacement\", \"technician\": \"Jane Smith\"}" - ], - "type": "string" + "description": "Unregistration action metadata. Structured form accepts an object with JSON values; legacy string is still accepted.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Registration action metadata as a flat object with JSON values. Limited to 4096 bytes, 32 keys, key length 64, and value JSON size 1024 bytes.", + "maxProperties": 32, + "type": "object" + }, + { + "deprecated": true, + "description": "Deprecated compatibility form. Use structured actor metadata instead. The full meta JSON value is limited to 4096 bytes.", + "maxLength": 4094, + "type": "string" + } + ] } }, "required": [ @@ -3166,9 +4545,6 @@ "isArchive": { "type": "boolean" }, - "isPrivate": { - "type": "boolean" - }, "parent": { "type": "string" }, @@ -3187,6 +4563,184 @@ } }, "type": "object" + }, + "WeatherForecastDaily": { + "additionalProperties": false, + "properties": { + "img_1x": { + "type": "string" + }, + "img_2x": { + "type": "string" + }, + "img_4x": { + "type": "string" + }, + "precip_prob": { + "format": "double", + "type": "number" + }, + "temp_max_c": { + "format": "double", + "type": "number" + }, + "temp_min_c": { + "format": "double", + "type": "number" + }, + "wmo_code": { + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "WeatherForecastHour": { + "additionalProperties": false, + "properties": { + "img_1x": { + "type": "string" + }, + "img_2x": { + "type": "string" + }, + "img_4x": { + "type": "string" + }, + "precip_prob": { + "format": "double", + "type": "number" + }, + "temp_c": { + "format": "double", + "type": "number" + }, + "time": { + "type": "string" + }, + "wmo_code": { + "format": "int64", + "type": "integer" + } + }, + "required": [ + "time" + ], + "type": "object" + }, + "WeatherForecastRow": { + "additionalProperties": false, + "properties": { + "daily": { + "$ref": "#/components/schemas/WeatherForecastDaily" + }, + "hourly": { + "items": { + "$ref": "#/components/schemas/WeatherForecastHour" + }, + "type": [ + "array", + "null" + ] + }, + "local_date": { + "type": "string" + } + }, + "required": [ + "local_date", + "daily" + ], + "type": "object" + }, + "WeatherObservedDaily": { + "additionalProperties": false, + "properties": { + "img_1x": { + "type": "string" + }, + "img_2x": { + "type": "string" + }, + "img_4x": { + "type": "string" + }, + "precip_prob": { + "format": "double", + "type": "number" + }, + "temp_max_c": { + "format": "double", + "type": "number" + }, + "temp_min_c": { + "format": "double", + "type": "number" + }, + "wmo_code": { + "format": "int64", + "type": "integer" + } + }, + "type": "object" + }, + "WeatherObservedHour": { + "additionalProperties": false, + "properties": { + "img_1x": { + "type": "string" + }, + "img_2x": { + "type": "string" + }, + "img_4x": { + "type": "string" + }, + "precip_prob": { + "format": "double", + "type": "number" + }, + "temp_c": { + "format": "double", + "type": "number" + }, + "time": { + "type": "string" + }, + "wmo_code": { + "format": "int64", + "type": "integer" + } + }, + "required": [ + "time" + ], + "type": "object" + }, + "WeatherObservedRow": { + "additionalProperties": false, + "properties": { + "daily": { + "$ref": "#/components/schemas/WeatherObservedDaily" + }, + "hourly": { + "items": { + "$ref": "#/components/schemas/WeatherObservedHour" + }, + "type": [ + "array", + "null" + ] + }, + "local_date": { + "type": "string" + } + }, + "required": [ + "local_date", + "daily" + ], + "type": "object" } }, "securitySchemes": { @@ -3219,16 +4773,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -3389,7 +4943,7 @@ }, "/api/v3/account/auth-with-password": { "post": { - "description": "Authenticates users using either email (for managers) or username (for viewers) with password. The response includes an authentication token for subsequent API calls. This endpoint does not require the Authorization header.", + "description": "Authenticates users using email or username for managers, and username for viewers and temporary accounts, with password. The response includes an authentication token for subsequent API calls. This endpoint does not require the Authorization header.", "operationId": "authenticate_user", "requestBody": { "content": { @@ -3511,16 +5065,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -3576,16 +5130,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -3640,16 +5194,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -3704,16 +5258,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -3754,7 +5308,7 @@ }, "/api/v3/organizations/{organization_id}/members": { "post": { - "description": "Creates a new user under the specified organization. Alias of POST /api/v3/account.", + "description": "Creates a new user under the specified organization. Only the organization owner may create organization members. Alias of POST /api/v3/account.", "operationId": "create_org_member", "parameters": [ { @@ -3768,16 +5322,14 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Every v3/org route requires a manager account that owns the organization.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Every v3/org route requires a manager account that owns the organization.", "enum": [ - "viewer", - "manager", - "admin" + "manager" ], "type": "string" } @@ -3842,31 +5394,30 @@ ] } }, - "/api/v3/organizations/{organization_id}/permissions": { + "/api/v3/organizations/{organization_id}/plants/{plant_id}/permissions/grant": { "post": { - "description": "Grants a user a specific role-based permission (viewer, user, temporary) to access a plant under the given organization.", + "description": "Grants a user a specific role-based permission (viewer, manager, temporary) to access a plant under the given organization. Only the organization owner may add plant permissions.", "operationId": "assign_plant_permission", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", "in": "header", "name": "Authorization", + "required": true, "schema": { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", "type": "string" } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Every v3/org route requires a manager account that owns the organization.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Every v3/org route requires a manager account that owns the organization.", "enum": [ - "viewer", - "manager", - "admin" + "manager" ], "type": "string" } @@ -3885,13 +5436,28 @@ "minLength": 15, "type": "string" } + }, + { + "description": "Target plant ID to grant access to.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Target plant ID to grant access to.", + "examples": [ + "pln123456789012" + ], + "maxLength": 15, + "minLength": 15, + "type": "string" + } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrgAddPermissionInputBody", + "$ref": "#/components/schemas/AssignOrganizationPermissionRequestBody", "description": "Request body for assigning plant permission to a user" } } @@ -3932,10 +5498,10 @@ ] } }, - "/api/v3/plants": { - "get": { - "description": "Retrieves a paginated list of all solar plants associated with the user's account. Includes plant ID, name, location, organization information, and other relevant details with pagination support.", - "operationId": "get_plant_list", + "/api/v3/organizations/{organization_id}/plants/{plant_id}/permissions/revoke": { + "post": { + "description": "Removes a user's role-based permission (viewer, manager, temporary) to access a plant under the given organization. Only the organization owner may remove plant permissions.", + "operationId": "remove_plant_permission", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -3948,37 +5514,140 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Every v3/org route requires a manager account that owns the organization.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Every v3/org route requires a manager account that owns the organization.", "enum": [ - "viewer", - "manager", - "admin" + "manager" ], "type": "string" } }, { - "description": "Page number for pagination (1-based indexing). Determines which subset of results to return. Results from (page-1)*size to page*size will be returned.", - "explode": false, - "in": "query", - "name": "page", + "description": "Organization ID that owns the target plant.", + "in": "path", + "name": "organization_id", + "required": true, "schema": { - "default": 1, - "description": "Page number for pagination (1-based indexing). Determines which subset of results to return. Results from (page-1)*size to page*size will be returned.", - "format": "int64", - "minimum": 1, - "type": "integer" - } - }, - { - "description": "Number of items per page for pagination. Controls how many plant installations are returned in a single request. Maximum recommended value is 100.", - "explode": false, - "in": "query", + "description": "Organization ID that owns the target plant.", + "examples": [ + "org123456789012" + ], + "maxLength": 15, + "minLength": 15, + "type": "string" + } + }, + { + "description": "Target plant ID to revoke access from.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Target plant ID to revoke access from.", + "examples": [ + "pln123456789012" + ], + "maxLength": 15, + "minLength": 15, + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RemoveOrganizationPermissionRequestBody", + "description": "Request body for removing plant permission from a user" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrgRemovePermissionOutputBody", + "description": "Response body with removal result info" + } + } + }, + "description": "OK" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Remove plant permission from a user", + "tags": [ + "v3/org" + ] + } + }, + "/api/v3/plants": { + "get": { + "description": "Retrieves a paginated list of all solar plants associated with the user's account. Includes plant ID, name, location, organization information, and other relevant details with pagination support.", + "operationId": "get_plant_list", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Plant collection routes are available to manager and viewer accounts only.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Plant collection routes are available to manager and viewer accounts only.", + "enum": [ + "viewer", + "manager" + ], + "type": "string" + } + }, + { + "description": "Page number for pagination (1-based indexing). Determines which subset of results to return. Results from (page-1)*size to page*size will be returned.", + "explode": false, + "in": "query", + "name": "page", + "schema": { + "default": 1, + "description": "Page number for pagination (1-based indexing). Determines which subset of results to return. Results from (page-1)*size to page*size will be returned.", + "format": "int64", + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Number of items per page for pagination. Controls how many plant installations are returned in a single request. Maximum recommended value is 100.", + "explode": false, + "in": "query", "name": "size", "schema": { "default": 30, @@ -4107,16 +5776,14 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant creation is available to manager accounts only.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant creation is available to manager accounts only.", "enum": [ - "viewer", - "manager", - "admin" + "manager" ], "type": "string" } @@ -4183,16 +5850,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -4322,16 +5989,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the blueprint account type making the request. Supported values map to public blueprint collections: 'manager', 'viewer', and 'temporary'.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the blueprint account type making the request. Supported values map to public blueprint collections: 'manager', 'viewer', and 'temporary'.", "enum": [ - "viewer", "manager", - "admin" + "viewer", + "temporary" ], "type": "string" } @@ -4444,10 +6111,10 @@ ] } }, - "/api/v3/plants/{plant_id}/indicator/device-state": { + "/api/v3/plants/{plant_id}/blueprints": { "get": { - "description": "Retrieves device or panel state indicator data at 5-minute interval. Use the `kind` query parameter with one of: seqnum, relay, rsd.", - "operationId": "get_device_state", + "description": "Lists blueprint records for a specific plant without including each record's data payload. Authorization header and Account-Type header are required.", + "operationId": "list_plant_blueprints", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -4460,80 +6127,143 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the blueprint account type making the request. Supported values map to public blueprint collections: 'manager', 'viewer', and 'temporary'.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the blueprint account type making the request. Supported values map to public blueprint collections: 'manager', 'viewer', and 'temporary'.", "enum": [ - "viewer", "manager", - "admin" + "viewer", + "temporary" ], "type": "string" } }, { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "in": "header", - "name": "If-None-Match", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to list blueprint records for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, "schema": { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to list blueprint records for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Blueprint records without data payloads.", + "items": { + "$ref": "#/components/schemas/BlueprintListItem" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK" }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ { - "description": "The ID of the plant.", - "in": "path", - "name": "plant_id", + "bearer": [] + } + ], + "summary": "List Plant Blueprint Records", + "tags": [ + "v3/plants/blueprints" + ] + } + }, + "/api/v3/plants/{plant_id}/blueprints/record": { + "post": { + "description": "Records a new immutable blueprint snapshot for a plant. This endpoint never mutates an existing blueprint record. On success, it invalidates the plant blueprint timeline/list cache.", + "operationId": "record_plant_blueprint", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", "required": true, "schema": { - "description": "The ID of the plant.", + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", "type": "string" } }, { - "description": "Target date (YYYY-MM-DD).", - "explode": false, - "in": "query", - "name": "date", + "description": "Specifies the blueprint account type making the request. Blueprint record writes are available to manager accounts only.", + "in": "header", + "name": "Account-Type", "required": true, "schema": { - "description": "Target date (YYYY-MM-DD).", - "examples": [ - "2025-08-14" + "description": "Specifies the blueprint account type making the request. Blueprint record writes are available to manager accounts only.", + "enum": [ + "manager" ], - "format": "date", "type": "string" } }, { - "description": "Indicator kind to retrieve. One of: seqnum, relay, rsd.", - "explode": false, - "in": "query", - "name": "kind", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to record blueprint data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", "required": true, "schema": { - "description": "Indicator kind to retrieve. One of: seqnum, relay, rsd.", - "enum": [ - "seqnum", - "relay", - "rsd" + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to record blueprint data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", "type": "string" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BlueprintWriteBody" + } + } + }, + "required": true + }, "responses": { - "200": { - "description": "OK" + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BlueprintRecordPayload", + "description": "Recorded immutable blueprint snapshot." + } + } + }, + "description": "Created" }, "default": { "content": { @@ -4551,16 +6281,16 @@ "bearer": [] } ], - "summary": "Retrieve Device State (5m avg)", + "summary": "Record Plant Blueprint Snapshot", "tags": [ - "v3/plants/indicator" + "v3/plants/blueprints" ] } }, - "/api/v3/plants/{plant_id}/indicator/health-level/{unit}": { + "/api/v3/plants/{plant_id}/blueprints/{blueprint_id}": { "get": { - "description": "Calculates and retrieves the health level of panels for a specific plant based on daily energy production. Panels are categorized as 'best', 'caution', or 'faulty'.", - "operationId": "get_asset_health_level", + "description": "Retrieves a specific blueprint record on a plant, including record metadata and the data payload. Authorization header and Account-Type header are required.", + "operationId": "get_plant_blueprint_data", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -4573,89 +6303,96 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the blueprint account type making the request. Supported values map to public blueprint collections: 'manager', 'viewer', and 'temporary'.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the blueprint account type making the request. Supported values map to public blueprint collections: 'manager', 'viewer', and 'temporary'.", "enum": [ - "viewer", "manager", - "admin" + "viewer", + "temporary" ], "type": "string" } }, { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "in": "header", - "name": "If-None-Match", - "schema": { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] - } - }, - { - "description": "The ID of the plant to calculate health levels for.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to fetch blueprint data for. Must be exactly 15 characters in length.", "in": "path", "name": "plant_id", "required": true, "schema": { - "description": "The ID of the plant to calculate health levels for.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to fetch blueprint data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", "type": "string" } }, { - "description": "The unit to calculate health levels for (e.g., panel).", + "description": "Blueprint record ID.", "in": "path", - "name": "unit", + "name": "blueprint_id", "required": true, "schema": { - "default": "panel", - "description": "The unit to calculate health levels for (e.g., panel).", + "description": "Blueprint record ID.", "examples": [ - "panel" + "blueprint123456" ], "type": "string" } }, { - "description": "The specific date for health level calculation (YYYY-MM-DD).", - "explode": false, - "in": "query", - "name": "date", - "required": true, + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", "schema": { - "description": "The specific date for health level calculation (YYYY-MM-DD).", - "examples": [ - "2025-08-14" - ], - "format": "date", + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", "type": "string" } }, { - "description": "The view type for the response (summary or detail).", - "explode": false, - "in": "query", - "name": "view", + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", "schema": { - "default": "summary", - "description": "The view type for the response (summary or detail).", - "enum": [ - "summary", - "detail" - ], - "examples": [ - "summary" - ], + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", "type": "string" } } @@ -4665,14 +6402,67 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HealthLevelBody" + "description": "Blueprint record. The data field contains the stored blueprint payload, which can be an object or an array.", + "properties": { + "created": { + "description": "Blueprint record creation timestamp.", + "type": "string" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + } + ], + "description": "Blueprint data payload." + }, + "date": { + "description": "Blueprint effective timestamp.", + "type": "string" + }, + "id": { + "description": "Blueprint record ID.", + "type": "string" + }, + "metadata": { + "additionalProperties": true, + "description": "Blueprint record metadata.", + "type": "object" + }, + "plant": { + "description": "Requested plant ID.", + "type": "string" + }, + "updated": { + "description": "Blueprint record update timestamp.", + "type": "string" + } + }, + "required": [ + "id", + "plant", + "date", + "updated", + "metadata", + "data" + ], + "type": "object" } } }, - "description": "OK", + "description": "Blueprint record with metadata and data payload.", "headers": { "ETag": { "schema": { + "description": "Weak entity tag for conditional requests.", "type": "string" } } @@ -4694,16 +6484,16 @@ "bearer": [] } ], - "summary": "Retrieve Panel Health Levels", + "summary": "Retrieve Plant Blueprint Data By Record ID", "tags": [ - "v3/plants/indicator" + "v3/plants/blueprints" ] } }, - "/api/v3/plants/{plant_id}/logs/inverter": { + "/api/v3/plants/{plant_id}/comments": { "get": { - "description": "Retrieves paginated log entries for all inverters within a plant. Includes status messages, error codes, timestamps, and raw log data with Korean message translations.", - "operationId": "list_inverter_logs", + "description": "Lists comments for a specific plant.", + "operationId": "list_plant_comments", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -4716,74 +6506,36 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } }, { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "in": "path", "name": "plant_id", "required": true, "schema": { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ - "bsajtokt84s2byf" + "unw4id41ud2p0wt" ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } - }, - { - "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", - "explode": false, - "in": "query", - "name": "page", - "schema": { - "default": 1, - "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", - "format": "int64", - "minimum": 1, - "type": "integer" - } - }, - { - "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", - "explode": false, - "in": "query", - "name": "size", - "schema": { - "default": 100, - "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", - "format": "int64", - "maximum": 1000, - "minimum": 1, - "type": "integer" - } - }, - { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "in": "header", - "name": "If-None-Match", - "schema": { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] - } } ], "responses": { @@ -4791,19 +6543,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InverterLogsResponse" + "items": { + "$ref": "#/components/schemas/CommentReadOutput" + }, + "type": [ + "array", + "null" + ] } } }, - "description": "OK", - "headers": { - "ETag": { - "schema": { - "description": "Weak entity tag for conditional requests.", - "type": "string" - } - } - } + "description": "OK" }, "default": { "content": { @@ -4821,16 +6571,16 @@ "bearer": [] } ], - "summary": "Retrieve Inverter Log List with Pagination", + "summary": "List Plant Comments", "tags": [ - "v3/plants/logs" + "v3/plants/comments" ] } }, - "/api/v3/plants/{plant_id}/logs/inverters/{inverter_id}": { - "get": { - "description": "Retrieves paginated log entries for a specific inverter within a plant. Includes status messages, error codes, timestamps, and raw log data with Korean message translations.", - "operationId": "list_inverter_logs_by_id", + "/api/v3/plants/{plant_id}/comments/start_thread": { + "post": { + "description": "Starts a comment thread for a specific plant.", + "operationId": "start_plant_comment_thread", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -4843,104 +6593,57 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", "enum": [ "viewer", - "manager", - "admin" + "manager" ], "type": "string" } }, { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "in": "path", "name": "plant_id", "required": true, "schema": { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ - "bsajtokt84s2byf" + "unw4id41ud2p0wt" ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } - }, - { - "description": "Inverter ID.", - "in": "path", - "name": "inverter_id", - "required": true, - "schema": { - "description": "Inverter ID.", - "type": "string" - } - }, - { - "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", - "explode": false, - "in": "query", - "name": "page", - "schema": { - "default": 1, - "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", - "format": "int64", - "minimum": 1, - "type": "integer" - } - }, - { - "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", - "explode": false, - "in": "query", - "name": "size", - "schema": { - "default": 100, - "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", - "format": "int64", - "maximum": 1000, - "minimum": 1, - "type": "integer" - } - }, - { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "in": "header", - "name": "If-None-Match", - "schema": { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] - } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommentActionBody" + } + } + }, + "required": true + }, "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InverterLogsResponse" + "$ref": "#/components/schemas/CommentOutput" } } }, - "description": "OK", - "headers": { - "ETag": { - "schema": { - "description": "Weak entity tag for conditional requests.", - "type": "string" - } - } - } + "description": "Created" }, "default": { "content": { @@ -4958,16 +6661,16 @@ "bearer": [] } ], - "summary": "Retrieve Specific Inverter Logs", + "summary": "Start Plant Comment Thread", "tags": [ - "v3/plants/logs" + "v3/plants/comments" ] } }, - "/api/v3/plants/{plant_id}/metrics/device/latest": { - "get": { - "description": "Retrieves the most recent metrics data for all devices (panels) in the specified plant. Includes current, voltage, temperature, and status information.", - "operationId": "get_latest_device_metrics", + "/api/v3/plants/{plant_id}/comments/{comment_id}/edit": { + "post": { + "description": "Edits a specific plant comment.", + "operationId": "edit_plant_comment", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -4980,137 +6683,180 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", "enum": [ "viewer", - "manager", - "admin" + "manager" ], "type": "string" } }, { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "in": "path", "name": "plant_id", "required": true, "schema": { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ "unw4id41ud2p0wt" ], "maxLength": 15, "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", "patternDescription": "alphanum", "type": "string" } }, { - "explode": false, - "in": "query", - "name": "includeState", + "description": "Comment ID. A unique alphanumeric identifier representing the comment. Must be exactly 15 characters in length.", + "in": "path", + "name": "comment_id", + "required": true, "schema": { - "default": false, - "type": "boolean" - } - }, - { - "description": "The time range in minutes for filtering active devices. Only devices that have sent data within the last 'ago' minutes will be included in the response. This helps exclude inactive or unresponsive devices. The maximum allowed value is 600 minutes.", - "explode": false, - "in": "query", - "name": "ago", - "schema": { - "default": 15, - "description": "The time range in minutes for filtering active devices. Only devices that have sent data within the last 'ago' minutes will be included in the response. This helps exclude inactive or unresponsive devices. The maximum allowed value is 600 minutes.", + "description": "Comment ID. A unique alphanumeric identifier representing the comment. Must be exactly 15 characters in length.", "examples": [ - 15 + "unw4id41ud2p0wt" ], - "format": "int64", - "maximum": 600, - "minimum": 1, - "type": "integer" + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommentEditBody" + } } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommentOutput" + } + } + }, + "description": "OK" }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ { - "description": "Succeeds if the server's resource matches one of the passed values.", + "bearer": [] + } + ], + "summary": "Edit Plant Comment", + "tags": [ + "v3/plants/comments" + ] + } + }, + "/api/v3/plants/{plant_id}/comments/{comment_id}/reply": { + "post": { + "description": "Creates a reply under a specific plant comment.", + "operationId": "reply_plant_comment", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", "in": "header", - "name": "If-Match", + "name": "Authorization", + "required": true, "schema": { - "description": "Succeeds if the server's resource matches one of the passed values.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" } }, { - "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", "in": "header", - "name": "If-None-Match", + "name": "Account-Type", + "required": true, "schema": { - "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", + "enum": [ + "viewer", + "manager" + ], + "type": "string" } }, { - "description": "Succeeds if the server's resource date is more recent than the passed date.", - "in": "header", - "name": "If-Modified-Since", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, "schema": { - "description": "Succeeds if the server's resource date is more recent than the passed date.", - "format": "date-time-http", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } }, { - "description": "Succeeds if the server's resource date is older or the same as the passed date.", - "in": "header", - "name": "If-Unmodified-Since", + "description": "Comment ID. A unique alphanumeric identifier representing the comment. Must be exactly 15 characters in length.", + "in": "path", + "name": "comment_id", + "required": true, "schema": { - "description": "Succeeds if the server's resource date is older or the same as the passed date.", - "format": "date-time-http", + "description": "Comment ID. A unique alphanumeric identifier representing the comment. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommentActionBody" + } + } + }, + "required": true + }, "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "items": { - "$ref": "#/components/schemas/LatestDeviceBody" - }, - "type": [ - "array", - "null" - ] + "$ref": "#/components/schemas/CommentOutput" } } }, - "description": "OK", - "headers": { - "ETag": { - "schema": { - "description": "Weak entity tag for conditional requests", - "type": "string" - } - } - } + "description": "Created" }, "default": { "content": { @@ -5128,16 +6874,16 @@ "bearer": [] } ], - "summary": "Retrieve Latest Device Metrics", + "summary": "Reply To Plant Comment", "tags": [ - "v3/plants/metrics" + "v3/plants/comments" ] } }, - "/api/v3/plants/{plant_id}/metrics/inverter/latest": { - "get": { - "description": "Retrieves the most recent metrics data for all inverters in the specified plant. Includes logs, status, daily/total energy production, and operational information.", - "operationId": "get_latest_inverter_metrics", + "/api/v3/plants/{plant_id}/comments/{comment_id}/state": { + "post": { + "description": "Applies a state transition to a specific plant comment.", + "operationId": "change_plant_comment_state", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -5150,108 +6896,74 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Plant comment mutation routes are available to manager and viewer accounts only.", "enum": [ "viewer", - "manager", - "admin" + "manager" ], "type": "string" } }, { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "in": "path", "name": "plant_id", "required": true, "schema": { - "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ - "bsajtokt84s2byf" + "unw4id41ud2p0wt" ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } }, { - "description": "Succeeds if the server's resource matches one of the passed values.", - "in": "header", - "name": "If-Match", - "schema": { - "description": "Succeeds if the server's resource matches one of the passed values.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] - } - }, - { - "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", - "in": "header", - "name": "If-None-Match", - "schema": { - "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] - } - }, - { - "description": "Succeeds if the server's resource date is more recent than the passed date.", - "in": "header", - "name": "If-Modified-Since", - "schema": { - "description": "Succeeds if the server's resource date is more recent than the passed date.", - "format": "date-time-http", - "type": "string" - } - }, - { - "description": "Succeeds if the server's resource date is older or the same as the passed date.", - "in": "header", - "name": "If-Unmodified-Since", + "description": "Comment ID. A unique alphanumeric identifier representing the comment. Must be exactly 15 characters in length.", + "in": "path", + "name": "comment_id", + "required": true, "schema": { - "description": "Succeeds if the server's resource date is older or the same as the passed date.", - "format": "date-time-http", + "description": "Comment ID. A unique alphanumeric identifier representing the comment. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommentStateBody" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "items": { - "$ref": "#/components/schemas/InverterDataBody" - }, - "type": [ - "array", - "null" - ] + "$ref": "#/components/schemas/CommentOutput" } } }, - "description": "OK", - "headers": { - "ETag": { - "schema": { - "description": "Weak entity tag for conditional requests", - "type": "string" - } - } - } + "description": "OK" }, "default": { "content": { @@ -5269,16 +6981,16 @@ "bearer": [] } ], - "summary": "Retrieve Latest Inverter Metrics", + "summary": "Change Plant Comment State", "tags": [ - "v3/plants/metrics" + "v3/plants/comments" ] } }, - "/api/v3/plants/{plant_id}/metrics/{source}/{unit}-{interval}": { + "/api/v3/plants/{plant_id}/filters": { "get": { - "description": "Retrieves comprehensive time-series metrics data for a specific plant with configurable data source, aggregation unit, and time interval. Supports various combinations of panel, inverter, string, and plant-level data aggregation across different time intervals.", - "operationId": "get_metrics_by_date", + "description": "Lists reusable map object filters for a specific plant, including each filter's map object IDs.", + "operationId": "list_plant_filters", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -5291,443 +7003,2418 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } }, { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "in": "header", - "name": "If-None-Match", - "schema": { - "description": "Succeeds when none of the supplied ETags match the current representation.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] - } - }, - { - "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to fetch metrics data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "in": "path", "name": "plant_id", "required": true, "schema": { - "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to fetch metrics data for. Must be exactly 15 characters in length.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ "unw4id41ud2p0wt" ], "maxLength": 15, "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", "patternDescription": "alphanum", "type": "string" } - }, + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Plant filters with map object IDs.", + "items": { + "$ref": "#/components/schemas/FilterListItem" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ { - "description": "Data source type. Indicates the origin of the data collection system. 'device' refers to panel-level monitoring devices, 'inverter' refers to inverter-level data collection, and 'sensor' refers to sensor data collection.", - "in": "path", - "name": "source", + "bearer": [] + } + ], + "summary": "List Plant Filters", + "tags": [ + "v3/plants/filters" + ] + } + }, + "/api/v3/plants/{plant_id}/filters/create": { + "post": { + "description": "Creates a reusable map object filter for a specific plant from the provided map IDs.", + "operationId": "create_plant_filter", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", "required": true, "schema": { - "default": "device", - "description": "Data source type. Indicates the origin of the data collection system. 'device' refers to panel-level monitoring devices, 'inverter' refers to inverter-level data collection, and 'sensor' refers to sensor data collection.", + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Plant filter mutation routes are available to manager and viewer accounts only.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Plant filter mutation routes are available to manager and viewer accounts only.", "enum": [ - "device", - "inverter", - "sensor" - ], - "examples": [ - "device" + "viewer", + "manager" ], "type": "string" } }, { - "description": "Data aggregation unit. Specifies the hierarchical level of data aggregation within the solar installation. 'panel' provides individual panel data, 'inverter' aggregates by inverter, 'string' aggregates by string configuration, 'plant' provides plant-wide aggregated data, and 'temperature'/'insolation' are sensor-specific units.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "in": "path", - "name": "unit", + "name": "plant_id", "required": true, "schema": { - "default": "plant", - "description": "Data aggregation unit. Specifies the hierarchical level of data aggregation within the solar installation. 'panel' provides individual panel data, 'inverter' aggregates by inverter, 'string' aggregates by string configuration, 'plant' provides plant-wide aggregated data, and 'temperature'/'insolation' are sensor-specific units.", - "enum": [ - "panel", - "inverter", - "string", - "plant", - "temperature", - "insolation" - ], + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ - "plant" + "unw4id41ud2p0wt" ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateFilterBody" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FilterOutput", + "description": "Created plant filter." + } + } + }, + "description": "Created" }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ { - "description": "Time interval for data aggregation. Determines the temporal granularity of the returned time-series data. Supported intervals: '5m' (5 minutes), '15m' (15 minutes), '1h' (1 hour), '1d' (1 day), '1M' (1 month), '1y' (1 year).", - "in": "path", - "name": "interval", + "bearer": [] + } + ], + "summary": "Create Plant Filter", + "tags": [ + "v3/plants/filters" + ] + } + }, + "/api/v3/plants/{plant_id}/filters/{filter_id}": { + "delete": { + "description": "Deletes a reusable map object filter for a specific plant.", + "operationId": "delete_plant_filter", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Plant filter mutation routes are available to manager and viewer accounts only.", + "in": "header", + "name": "Account-Type", "required": true, "schema": { - "description": "Time interval for data aggregation. Determines the temporal granularity of the returned time-series data. Supported intervals: '5m' (5 minutes), '15m' (15 minutes), '1h' (1 hour), '1d' (1 day), '1M' (1 month), '1y' (1 year).", + "description": "Specifies the type of user account making the request. Plant filter mutation routes are available to manager and viewer accounts only.", "enum": [ - "5m", - "15m", - "1h", - "1d", - "1M", - "1y" - ], - "examples": [ - "5m" + "viewer", + "manager" ], "type": "string" } }, { - "description": "Target date for data retrieval. The specific date for which you want to retrieve solar plant metrics data. Must follow the ISO 8601 date format 'YYYY-MM-DD'.", - "explode": false, - "in": "query", - "name": "date", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", "required": true, "schema": { - "description": "Target date for data retrieval. The specific date for which you want to retrieve solar plant metrics data. Must follow the ISO 8601 date format 'YYYY-MM-DD'.", + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ - "2024-01-24" + "unw4id41ud2p0wt" ], - "format": "date", + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", "type": "string" } }, { - "description": "Historical data range. Number of time intervals before the specified date to include in the response. This parameter allows retrieval of historical trend data. Note: Only applicable for daily and longer intervals (1d, 1M, 1y), not for intraday intervals (5m, 15m, 1h).", - "explode": false, - "in": "query", - "name": "before", + "description": "Filter record ID.", + "in": "path", + "name": "filter_id", + "required": true, "schema": { - "default": 1, - "description": "Historical data range. Number of time intervals before the specified date to include in the response. This parameter allows retrieval of historical trend data. Note: Only applicable for daily and longer intervals (1d, 1M, 1y), not for intraday intervals (5m, 15m, 1h).", + "description": "Filter record ID.", "examples": [ - 1 + "filter123456789" ], - "format": "int64", - "maximum": 100, - "minimum": 1, - "type": "integer" + "type": "string" } + } + ], + "responses": { + "204": { + "description": "No Content" }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ { - "description": "Data fields selection. Specifies which metrics or parameters to include in the response. Use 'all' to retrieve all available measurement fields, or specify individual field names (e.g., 'i_out' for output current, 'p' for power, 'energy' for energy production).", - "explode": false, - "in": "query", - "name": "fields", + "bearer": [] + } + ], + "summary": "Delete Plant Filter", + "tags": [ + "v3/plants/filters" + ] + } + }, + "/api/v3/plants/{plant_id}/filters/{filter_id}/rename": { + "post": { + "description": "Renames a reusable map object filter for a specific plant.", + "operationId": "rename_plant_filter", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, "schema": { - "default": [ - "all" + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Plant filter mutation routes are available to manager and viewer accounts only.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Plant filter mutation routes are available to manager and viewer accounts only.", + "enum": [ + "viewer", + "manager" ], - "description": "Data fields selection. Specifies which metrics or parameters to include in the response. Use 'all' to retrieve all available measurement fields, or specify individual field names (e.g., 'i_out' for output current, 'p' for power, 'energy' for energy production).", + "type": "string" + } + }, + { + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation. Must be exactly 15 characters in length.", "examples": [ - [ - "i_out", - "p" - ] + "unw4id41ud2p0wt" ], - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", + "type": "string" } }, { - "description": "Panel ID filters. Supported only when source=device and unit=panel. Multiple id query parameters are allowed (e.g., id=pnl-001&id=pnl-002).", - "explode": false, - "in": "query", - "name": "id", + "description": "Filter record ID.", + "in": "path", + "name": "filter_id", + "required": true, "schema": { - "description": "Panel ID filters. Supported only when source=device and unit=panel. Multiple id query parameters are allowed (e.g., id=pnl-001&id=pnl-002).", + "description": "Filter record ID.", "examples": [ - [ - "pnl-001", - "pnl-002" - ] + "filter123456789" ], - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] + "type": "string" } } ], - "responses": { - "200": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenameFilterBody" + } + } + }, + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "oneOf": [ - { - "additionalProperties": false, - "description": "Time-series metrics for individual panels at 5-minute, 15-minute, and 1-hour intervals.", - "properties": { - "before": { - "description": "Number of historical intervals included in the response", - "format": "int64", - "type": "integer" - }, - "data": { - "description": "Array of time-series data points matching the specified parameters", - "items": { - "$ref": "#/components/schemas/PanelData" - }, - "type": [ - "array", - "null" - ] - }, - "date": { - "description": "Target date for the data retrieval", - "type": "string" - }, - "interval": { - "description": "Time interval used for data aggregation", - "type": "string" - }, - "plant_id": { - "description": "Plant identifier that was queried", - "type": "string" - }, - "source": { - "description": "Data source type that was queried", - "type": "string" - }, - "unit": { - "description": "Data aggregation unit that was requested", - "type": "string" - } - }, - "required": [ - "plant_id", - "unit", - "source", - "date", - "interval", - "data" - ], - "title": "Panel Intraday Metrics", - "type": "object" - }, - { - "additionalProperties": false, - "description": "Daily aggregated metrics for individual panels at 1-day intervals.", - "properties": { - "before": { - "description": "Number of historical intervals included in the response", - "format": "int64", - "type": "integer" - }, - "data": { - "description": "Array of time-series data points matching the specified parameters", - "items": { - "$ref": "#/components/schemas/PanelDailyData" - }, - "type": [ - "array", - "null" - ] - }, - "date": { - "description": "Target date for the data retrieval", - "type": "string" - }, - "interval": { - "description": "Time interval used for data aggregation", - "type": "string" - }, - "plant_id": { - "description": "Plant identifier that was queried", - "type": "string" - }, - "source": { - "description": "Data source type that was queried", - "type": "string" - }, - "unit": { - "description": "Data aggregation unit that was requested", - "type": "string" - } - }, - "required": [ - "plant_id", - "unit", - "source", - "date", - "interval", - "data" - ], - "title": "Panel Daily Metrics", - "type": "object" - }, - { - "additionalProperties": false, - "description": "Time-series metrics for individual inverters at 5-minute, 15-minute, and 1-hour intervals.", - "properties": { - "before": { - "description": "Number of historical intervals included in the response", - "format": "int64", - "type": "integer" - }, - "data": { - "description": "Array of time-series data points matching the specified parameters", - "items": { - "$ref": "#/components/schemas/InverterData" - }, - "type": [ - "array", - "null" - ] - }, - "date": { - "description": "Target date for the data retrieval", - "type": "string" - }, - "interval": { - "description": "Time interval used for data aggregation", - "type": "string" - }, - "plant_id": { - "description": "Plant identifier that was queried", - "type": "string" - }, - "source": { - "description": "Data source type that was queried", - "type": "string" - }, - "unit": { - "description": "Data aggregation unit that was requested", - "type": "string" - } - }, - "required": [ - "plant_id", - "unit", - "source", - "date", - "interval", - "data" - ], - "title": "Inverter Intraday Metrics", - "type": "object" - }, - { - "additionalProperties": false, - "description": "Daily aggregated metrics for individual inverters at 1-day intervals.", - "properties": { - "before": { - "description": "Number of historical intervals included in the response", - "format": "int64", - "type": "integer" - }, - "data": { - "description": "Array of time-series data points matching the specified parameters", - "items": { - "$ref": "#/components/schemas/InverterDailyData" - }, - "type": [ - "array", - "null" - ] - }, - "date": { - "description": "Target date for the data retrieval", - "type": "string" - }, - "interval": { - "description": "Time interval used for data aggregation", - "type": "string" - }, - "plant_id": { - "description": "Plant identifier that was queried", - "type": "string" - }, - "source": { - "description": "Data source type that was queried", - "type": "string" - }, - "unit": { - "description": "Data aggregation unit that was requested", - "type": "string" - } - }, - "required": [ - "plant_id", - "unit", - "source", - "date", - "interval", - "data" - ], - "title": "Inverter Daily Metrics", - "type": "object" - }, - { - "additionalProperties": false, - "description": "Time-series metrics for entire plant at 5-minute, 15-minute, and 1-hour intervals.", - "properties": { - "before": { - "description": "Number of historical intervals included in the response", - "format": "int64", - "type": "integer" - }, - "data": { - "description": "Array of time-series data points matching the specified parameters", - "items": { - "$ref": "#/components/schemas/PlantData" - }, - "type": [ - "array", - "null" - ] - }, - "date": { - "description": "Target date for the data retrieval", - "type": "string" - }, - "interval": { - "description": "Time interval used for data aggregation", - "type": "string" - }, - "plant_id": { - "description": "Plant identifier that was queried", - "type": "string" - }, - "source": { - "description": "Data source type that was queried", - "type": "string" - }, - "unit": { - "description": "Data aggregation unit that was requested", - "type": "string" - } - }, + "$ref": "#/components/schemas/FilterOutput", + "description": "Renamed plant filter." + } + } + }, + "description": "OK" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Rename Plant Filter", + "tags": [ + "v3/plants/filters" + ] + } + }, + "/api/v3/plants/{plant_id}/indicator/anomaly": { + "get": { + "description": "Retrieves active anomaly snapshots from the previous date plus anomaly logs from the requested date.", + "operationId": "get_plant_anomaly_timeline", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "type": "string" + } + }, + { + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "examples": [ + "2026-06-15" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of anomaly records for the specified plant and date.", + "items": { + "$ref": "#/components/schemas/Record" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Plant Anomaly Timeline", + "tags": [ + "v3/plants/indicator" + ] + } + }, + "/api/v3/plants/{plant_id}/indicator/anomaly/logs": { + "get": { + "description": "Retrieves anomaly records detected or resolved on the requested date.", + "operationId": "get_plant_anomaly_logs", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "type": "string" + } + }, + { + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "examples": [ + "2026-06-15" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Optional anomaly map ID filter.", + "explode": false, + "in": "query", + "name": "map_id", + "schema": { + "description": "Optional anomaly map ID filter.", + "type": "string" + } + }, + { + "description": "Optional anomaly map type filter.", + "explode": false, + "in": "query", + "name": "map_type", + "schema": { + "description": "Optional anomaly map type filter.", + "enum": [ + "plant", + "inverter", + "string", + "panel" + ], + "type": "string" + } + }, + { + "description": "Optional anomaly type filter.", + "explode": false, + "in": "query", + "name": "type", + "schema": { + "description": "Optional anomaly type filter.", + "type": "string" + } + }, + { + "description": "Optional severity filter.", + "explode": false, + "in": "query", + "name": "severity", + "schema": { + "description": "Optional severity filter.", + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of anomaly records for the specified plant and date.", + "items": { + "$ref": "#/components/schemas/Record" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Plant Anomaly Logs", + "tags": [ + "v3/plants/indicator" + ] + } + }, + "/api/v3/plants/{plant_id}/indicator/anomaly/logs/filter": { + "get": { + "description": "Filters anomaly records by date, map_id, map_type, type, and severity. At least one query parameter is required.", + "operationId": "filter_plant_anomaly_logs", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "type": "string" + } + }, + { + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "examples": [ + "2026-06-15" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Optional anomaly map ID filter.", + "explode": false, + "in": "query", + "name": "map_id", + "schema": { + "description": "Optional anomaly map ID filter.", + "type": "string" + } + }, + { + "description": "Optional anomaly map type filter.", + "explode": false, + "in": "query", + "name": "map_type", + "schema": { + "description": "Optional anomaly map type filter.", + "enum": [ + "plant", + "inverter", + "string", + "panel" + ], + "type": "string" + } + }, + { + "description": "Optional anomaly type filter.", + "explode": false, + "in": "query", + "name": "type", + "schema": { + "description": "Optional anomaly type filter.", + "type": "string" + } + }, + { + "description": "Optional severity filter.", + "explode": false, + "in": "query", + "name": "severity", + "schema": { + "description": "Optional severity filter.", + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of anomaly records for the specified plant and date.", + "items": { + "$ref": "#/components/schemas/Record" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Filter Plant Anomaly Logs", + "tags": [ + "v3/plants/indicator" + ] + } + }, + "/api/v3/plants/{plant_id}/indicator/anomaly/snapshots": { + "get": { + "description": "Retrieves anomalies detected before the end of the requested date that had not resolved by that time.", + "operationId": "get_plant_anomaly_snapshots", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "type": "string" + } + }, + { + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "examples": [ + "2026-06-15" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Optional anomaly map ID filter.", + "explode": false, + "in": "query", + "name": "map_id", + "schema": { + "description": "Optional anomaly map ID filter.", + "type": "string" + } + }, + { + "description": "Optional anomaly map type filter.", + "explode": false, + "in": "query", + "name": "map_type", + "schema": { + "description": "Optional anomaly map type filter.", + "enum": [ + "plant", + "inverter", + "string", + "panel" + ], + "type": "string" + } + }, + { + "description": "Optional anomaly type filter.", + "explode": false, + "in": "query", + "name": "type", + "schema": { + "description": "Optional anomaly type filter.", + "type": "string" + } + }, + { + "description": "Optional severity filter.", + "explode": false, + "in": "query", + "name": "severity", + "schema": { + "description": "Optional severity filter.", + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of anomaly records for the specified plant and date.", + "items": { + "$ref": "#/components/schemas/Record" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Plant Anomaly Snapshots", + "tags": [ + "v3/plants/indicator" + ] + } + }, + "/api/v3/plants/{plant_id}/indicator/device-state": { + "get": { + "description": "Retrieves device-state data at 5-minute intervals. The fields query parameter is accepted for API compatibility but currently all device-state columns are returned. Rows include id, timestamp, date, is_forced_rapid_shutdown, is_forced_ref, is_forced_relay, is_rapid_shutdown, and is_relay.", + "operationId": "get_device_state", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "The ID of the plant.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "The ID of the plant.", + "type": "string" + } + }, + { + "description": "Target date (YYYY-MM-DD).", + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "description": "Target date (YYYY-MM-DD).", + "examples": [ + "2025-08-14" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Device-state fields to include. Selection is reserved for future use; the endpoint currently returns all device-state columns.", + "explode": false, + "in": "query", + "name": "fields", + "schema": { + "default": [ + "all" + ], + "description": "Device-state fields to include. Selection is reserved for future use; the endpoint currently returns all device-state columns.", + "examples": [ + [ + "is_relay", + "is_rapid_shutdown" + ] + ], + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Device state response envelope. Rows include id, timestamp, date, is_forced_rapid_shutdown, is_forced_ref, is_forced_relay, is_rapid_shutdown, and is_relay.", + "properties": { + "data": { + "description": "Device-state rows. Rows include id, timestamp, date, is_forced_rapid_shutdown, is_forced_ref, is_forced_relay, is_rapid_shutdown, and is_relay.", + "items": { + "additionalProperties": false, + "description": "Rows include id, timestamp, date, is_forced_rapid_shutdown, is_forced_ref, is_forced_relay, is_rapid_shutdown, and is_relay.", + "properties": { + "date": { + "description": "Time label from the device-state parquet row.", + "type": "string" + }, + "id": { + "description": "Device-state row identifier.", + "type": "string" + }, + "is_forced_rapid_shutdown": { + "description": "Forced rapid shutdown state.", + "type": "boolean" + }, + "is_forced_ref": { + "description": "Forced reference state.", + "type": "boolean" + }, + "is_forced_relay": { + "description": "Forced relay state.", + "type": "boolean" + }, + "is_rapid_shutdown": { + "description": "Rapid shutdown state.", + "type": "boolean" + }, + "is_relay": { + "description": "Relay state.", + "type": "boolean" + }, + "timestamp": { + "description": "Unix timestamp of the device-state row.", + "format": "int64", + "type": "integer" + } + }, + "required": [ + "id", + "timestamp", + "date", + "is_forced_rapid_shutdown", + "is_forced_ref", + "is_forced_relay", + "is_rapid_shutdown", + "is_relay" + ], + "type": "object" + }, + "type": "array" + }, + "date": { + "description": "Requested local date.", + "type": "string" + }, + "plant_id": { + "description": "Requested plant ID.", + "type": "string" + } + }, + "required": [ + "plant_id", + "date", + "data" + ], + "type": "object" + } + } + }, + "description": "Gzip JSON envelope containing device-state rows. Rows include id, timestamp, date, is_forced_rapid_shutdown, is_forced_ref, is_forced_relay, is_rapid_shutdown, and is_relay.", + "headers": { + "Content-Encoding": { + "description": "Response body content encoding. Device-state streams are gzip encoded.", + "schema": { + "enum": [ + "gzip" + ], + "type": "string" + } + }, + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Device State (5m)", + "tags": [ + "v3/plants/indicator" + ] + } + }, + "/api/v3/plants/{plant_id}/indicator/health-level/{unit}": { + "get": { + "description": "Calculates panel health indicators from daily energy production. The response reports the top ten panels as 'best' and reports panels outside the top ten only when they fall into 'caution' or 'faulty' thresholds.", + "operationId": "get_asset_health_level", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "The ID of the plant to calculate health levels for.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "The ID of the plant to calculate health levels for.", + "type": "string" + } + }, + { + "description": "The unit to calculate health levels for (e.g., panel).", + "in": "path", + "name": "unit", + "required": true, + "schema": { + "default": "panel", + "description": "The unit to calculate health levels for (e.g., panel).", + "examples": [ + "panel" + ], + "type": "string" + } + }, + { + "description": "The specific date for health level calculation (YYYY-MM-DD).", + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "description": "The specific date for health level calculation (YYYY-MM-DD).", + "examples": [ + "2025-08-14" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "The view type for the response (summary or detail).", + "explode": false, + "in": "query", + "name": "view", + "schema": { + "default": "summary", + "description": "The view type for the response (summary or detail).", + "enum": [ + "summary", + "detail" + ], + "examples": [ + "summary" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HealthLevelBody" + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Panel Health Levels", + "tags": [ + "v3/plants/indicator" + ] + } + }, + "/api/v3/plants/{plant_id}/logs/inverter": { + "get": { + "description": "Retrieves paginated log entries for all inverters within a plant. Includes status messages, error codes, timestamps, and raw log data with Korean message translations.", + "operationId": "list_inverter_logs", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "bsajtokt84s2byf" + ], + "type": "string" + } + }, + { + "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", + "explode": false, + "in": "query", + "name": "page", + "schema": { + "default": 1, + "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", + "format": "int64", + "minimum": 1, + "type": "integer" + } + }, + { + "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", + "explode": false, + "in": "query", + "name": "size", + "schema": { + "default": 100, + "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InverterLogsResponse" + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Inverter Log List with Pagination", + "tags": [ + "v3/plants/logs" + ] + } + }, + "/api/v3/plants/{plant_id}/logs/inverters/{inverter_id}": { + "get": { + "description": "Retrieves paginated log entries for a specific inverter within a plant. Includes status messages, error codes, timestamps, and raw log data with Korean message translations.", + "operationId": "list_inverter_logs_by_id", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "bsajtokt84s2byf" + ], + "type": "string" + } + }, + { + "description": "Inverter ID.", + "in": "path", + "name": "inverter_id", + "required": true, + "schema": { + "description": "Inverter ID.", + "type": "string" + } + }, + { + "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", + "explode": false, + "in": "query", + "name": "page", + "schema": { + "default": 1, + "description": "The page number for pagination. This allows the user to retrieve a specific subset of data from the total available records. The default value is 1, indicating the first page.", + "format": "int64", + "minimum": 1, + "type": "integer" + } + }, + { + "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", + "explode": false, + "in": "query", + "name": "size", + "schema": { + "default": 100, + "description": "The number of records to retrieve per page. This parameter allows for pagination, enabling the user to control the amount of data returned in each request. The default value is 100.", + "format": "int64", + "maximum": 1000, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InverterLogsResponse" + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Specific Inverter Logs", + "tags": [ + "v3/plants/logs" + ] + } + }, + "/api/v3/plants/{plant_id}/metrics/device/latest": { + "get": { + "description": "Retrieves the most recent metrics data for all devices (panels) in the specified plant. Includes current, voltage, temperature, and status information.", + "operationId": "get_latest_device_metrics", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", + "type": "string" + } + }, + { + "explode": false, + "in": "query", + "name": "includeState", + "schema": { + "default": false, + "type": "boolean" + } + }, + { + "description": "The time range in minutes for filtering active devices. Only devices that have sent data within the last 'ago' minutes will be included in the response. This helps exclude inactive or unresponsive devices. The maximum allowed value is 600 minutes.", + "explode": false, + "in": "query", + "name": "ago", + "schema": { + "default": 15, + "description": "The time range in minutes for filtering active devices. Only devices that have sent data within the last 'ago' minutes will be included in the response. This helps exclude inactive or unresponsive devices. The maximum allowed value is 600 minutes.", + "examples": [ + 15 + ], + "format": "int64", + "maximum": 600, + "minimum": 1, + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/LatestDeviceBody" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Latest Device Metrics", + "tags": [ + "v3/plants/metrics" + ] + } + }, + "/api/v3/plants/{plant_id}/metrics/inverter/latest": { + "get": { + "description": "Retrieves the most recent metrics data for all inverters in the specified plant. Includes logs, status, daily/total energy production, and operational information.", + "operationId": "get_latest_inverter_metrics", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "bsajtokt84s2byf" + ], + "maxLength": 15, + "minLength": 15, + "pattern": "^[a-zA-Z0-9]{15}$", + "patternDescription": "alphanum", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/InverterDataBody" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Latest Inverter Metrics", + "tags": [ + "v3/plants/metrics" + ] + } + }, + "/api/v3/plants/{plant_id}/metrics/{source}/{unit}-{interval}": { + "get": { + "description": "Retrieves comprehensive time-series metrics data for a specific plant with configurable data source, aggregation unit, and time interval. Supports various combinations of panel, inverter, string, ESS, and plant-level data aggregation across different time intervals. ESS supports ess and plant units at 5m, 15m, 1h, and 1d intervals only.", + "operationId": "get_metrics_by_date", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds when none of the supplied ETags match the current representation.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to fetch metrics data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Plant ID. A unique alphanumeric identifier representing the solar plant installation to fetch metrics data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" + } + }, + { + "description": "Data source type. Indicates the origin of the data collection system. 'device' refers to panel-level monitoring devices, 'inverter' refers to inverter-level data collection, 'ess' refers to energy storage system data collection, and 'sensor' refers to sensor data collection. For source 'ess', supported units are 'ess' and 'plant'.", + "in": "path", + "name": "source", + "required": true, + "schema": { + "default": "device", + "description": "Data source type. Indicates the origin of the data collection system. 'device' refers to panel-level monitoring devices, 'inverter' refers to inverter-level data collection, 'ess' refers to energy storage system data collection, and 'sensor' refers to sensor data collection. For source 'ess', supported units are 'ess' and 'plant'.", + "enum": [ + "device", + "inverter", + "ess", + "sensor" + ], + "examples": [ + "device" + ], + "type": "string" + } + }, + { + "description": "Data aggregation unit. Specifies the hierarchical level of data aggregation within the solar installation. 'panel' provides individual panel data, 'inverter' aggregates by inverter, 'ess' provides individual ESS data, 'string' aggregates by string configuration, 'plant' provides plant-wide aggregated data, and 'temperature'/'insolation' are sensor-specific units. Unit 'ess' is valid only with source 'ess'.", + "in": "path", + "name": "unit", + "required": true, + "schema": { + "default": "plant", + "description": "Data aggregation unit. Specifies the hierarchical level of data aggregation within the solar installation. 'panel' provides individual panel data, 'inverter' aggregates by inverter, 'ess' provides individual ESS data, 'string' aggregates by string configuration, 'plant' provides plant-wide aggregated data, and 'temperature'/'insolation' are sensor-specific units. Unit 'ess' is valid only with source 'ess'.", + "enum": [ + "panel", + "inverter", + "ess", + "string", + "plant", + "temperature", + "insolation" + ], + "examples": [ + "plant" + ], + "type": "string" + } + }, + { + "description": "Time interval for data aggregation. Determines the temporal granularity of the returned time-series data. Supported intervals: '5m' (5 minutes), '15m' (15 minutes), '1h' (1 hour), '1d' (1 day), '1M' (1 month), '1y' (1 year). For source 'ess', supported intervals are '5m', '15m', '1h', and '1d'.", + "in": "path", + "name": "interval", + "required": true, + "schema": { + "description": "Time interval for data aggregation. Determines the temporal granularity of the returned time-series data. Supported intervals: '5m' (5 minutes), '15m' (15 minutes), '1h' (1 hour), '1d' (1 day), '1M' (1 month), '1y' (1 year). For source 'ess', supported intervals are '5m', '15m', '1h', and '1d'.", + "enum": [ + "5m", + "15m", + "1h", + "1d", + "1M", + "1y" + ], + "examples": [ + "5m" + ], + "type": "string" + } + }, + { + "description": "Target date for data retrieval. The specific date for which you want to retrieve solar plant metrics data. Must follow the ISO 8601 date format 'YYYY-MM-DD'.", + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "description": "Target date for data retrieval. The specific date for which you want to retrieve solar plant metrics data. Must follow the ISO 8601 date format 'YYYY-MM-DD'.", + "examples": [ + "2024-01-24" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Historical data range. Number of time intervals before the specified date to include in the response. This parameter allows retrieval of historical trend data. Note: Only applicable for daily and longer intervals (1d, 1M, 1y), not for intraday intervals (5m, 15m, 1h). For source 'ess', before ranges are supported only for interval '1d'.", + "explode": false, + "in": "query", + "name": "before", + "schema": { + "default": 1, + "description": "Historical data range. Number of time intervals before the specified date to include in the response. This parameter allows retrieval of historical trend data. Note: Only applicable for daily and longer intervals (1d, 1M, 1y), not for intraday intervals (5m, 15m, 1h). For source 'ess', before ranges are supported only for interval '1d'.", + "examples": [ + 1 + ], + "format": "int64", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + { + "description": "Data fields selection. Specifies which metrics or parameters to include in the response. Use 'all' to retrieve all available measurement fields, or specify individual field names (e.g., 'i_out' for output current, 'p' for power, 'energy' for energy production).", + "explode": false, + "in": "query", + "name": "fields", + "schema": { + "default": [ + "all" + ], + "description": "Data fields selection. Specifies which metrics or parameters to include in the response. Use 'all' to retrieve all available measurement fields, or specify individual field names (e.g., 'i_out' for output current, 'p' for power, 'energy' for energy production).", + "examples": [ + [ + "i_out", + "p" + ] + ], + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Device panel or inverter ID filters. Supported only for intraday device/panel and inverter/inverter requests (5m, 15m, 1h). Multiple id query parameters are allowed (e.g., id=pnl-001&id=pnl-002).", + "explode": false, + "in": "query", + "name": "id", + "schema": { + "description": "Device panel or inverter ID filters. Supported only for intraday device/panel and inverter/inverter requests (5m, 15m, 1h). Multiple id query parameters are allowed (e.g., id=pnl-001&id=pnl-002).", + "examples": [ + [ + "pnl-001", + "pnl-002" + ] + ], + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "additionalProperties": false, + "description": "Time-series metrics for individual panels at 5-minute, 15-minute, and 1-hour intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/PanelData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Panel Intraday Metrics", + "type": "object" + }, + { + "additionalProperties": false, + "description": "Daily aggregated metrics for individual panels at 1-day intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/PanelDailyData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Panel Daily Metrics", + "type": "object" + }, + { + "additionalProperties": false, + "description": "Time-series metrics for individual inverters at 5-minute, 15-minute, and 1-hour intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/InverterData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Inverter Intraday Metrics", + "type": "object" + }, + { + "additionalProperties": false, + "description": "Daily aggregated metrics for individual inverters at 1-day intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/InverterDailyData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "Inverter Daily Metrics", + "type": "object" + }, + { + "additionalProperties": false, + "description": "Time-series metrics for entire plant at 5-minute, 15-minute, and 1-hour intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/PlantData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, "required": [ "plant_id", "unit", @@ -5790,6 +9477,210 @@ "title": "Sensor Intraday Metrics", "type": "object" }, + { + "additionalProperties": false, + "description": "Energy storage system unit metrics at 5-minute, 15-minute, and 1-hour intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/ESSUnitIntradayData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "ESS Unit Intraday Metrics", + "type": "object" + }, + { + "additionalProperties": false, + "description": "Energy storage system unit daily metrics at 1-day intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/ESSUnitDailyData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "ESS Unit Daily Metrics", + "type": "object" + }, + { + "additionalProperties": false, + "description": "Plant-level energy storage system metrics at 5-minute, 15-minute, and 1-hour intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/ESSPlantIntradayData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "ESS Plant Intraday Metrics", + "type": "object" + }, + { + "additionalProperties": false, + "description": "Plant-level energy storage system daily metrics at 1-day intervals.", + "properties": { + "before": { + "description": "Number of historical intervals included in the response", + "format": "int64", + "type": "integer" + }, + "data": { + "description": "Array of time-series data points matching the specified parameters", + "items": { + "$ref": "#/components/schemas/ESSPlantDailyData" + }, + "type": [ + "array", + "null" + ] + }, + "date": { + "description": "Target date for the data retrieval", + "type": "string" + }, + "interval": { + "description": "Time interval used for data aggregation", + "type": "string" + }, + "plant_id": { + "description": "Plant identifier that was queried", + "type": "string" + }, + "source": { + "description": "Data source type that was queried", + "type": "string" + }, + "unit": { + "description": "Data aggregation unit that was requested", + "type": "string" + } + }, + "required": [ + "plant_id", + "unit", + "source", + "date", + "interval", + "data" + ], + "title": "ESS Plant Daily Metrics", + "type": "object" + }, { "additionalProperties": false, "description": "Aggregated metrics for entire plant at 1-day, 1-month, and 1-year intervals.", @@ -5845,7 +9736,920 @@ } } }, - "description": "Successful response. The structure varies depending on the request parameters and will be one of the following schemas." + "description": "Successful response. The structure varies depending on the request parameters and will be one of the following schemas." + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Time-Series Metrics Data", + "tags": [ + "v3/plants/metrics" + ] + } + }, + "/api/v3/plants/{plant_id}/registry": { + "get": { + "description": "Retrieves the registry timeline for a date by combining snapshots from the previous date with logs from the requested date.", + "operationId": "get_plant_registry_timeline", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" + } + }, + { + "description": "Target date for registry timeline data. Must follow the format 'YYYY-MM-DD'.", + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "description": "Target date for registry timeline data. Must follow the format 'YYYY-MM-DD'.", + "examples": [ + "2024-01-24" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of asset registration records for the specified plant and date", + "items": { + "$ref": "#/components/schemas/RegistryOutputBody" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Plant Registry Timeline", + "tags": [ + "v3/plants/registry" + ] + } + }, + "/api/v3/plants/{plant_id}/registry/logs": { + "get": { + "description": "Retrieves registry log records for the specified plant and date using the explicit patch-registry logs path shape.", + "operationId": "get_plant_registry_logs", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" + } + }, + { + "description": "Target date of data. Must follow the format 'YYYY-MM-DD'.", + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "description": "Target date of data. Must follow the format 'YYYY-MM-DD'.", + "examples": [ + "2024-01-24" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Optional asset ID filter.", + "explode": false, + "in": "query", + "name": "asset_id", + "schema": { + "description": "Optional asset ID filter.", + "pattern": "^[a-zA-Z0-9.\\-_ /:@+]*$", + "type": "string" + } + }, + { + "description": "Optional map ID filter.", + "explode": false, + "in": "query", + "name": "map_id", + "schema": { + "description": "Optional map ID filter.", + "pattern": "^[a-zA-Z0-9.\\-_ /:@+]*$", + "type": "string" + } + }, + { + "description": "Optional asset type filter.", + "explode": false, + "in": "query", + "name": "asset_type", + "schema": { + "description": "Optional asset type filter.", + "enum": [ + "device", + "inverter", + "edge", + "panel", + "panel_group", + "sensor" + ], + "type": "string" + } + }, + { + "description": "Optional map type filter.", + "explode": false, + "in": "query", + "name": "map_type", + "schema": { + "description": "Optional map type filter.", + "enum": [ + "device", + "string", + "edge", + "inverter", + "combiner", + "panel", + "panel_group", + "tracker", + "sensor" + ], + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of asset registration records for the specified plant and date", + "items": { + "$ref": "#/components/schemas/RegistryOutputBody" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Retrieve Plant Registry Logs", + "tags": [ + "v3/plants/registry" + ] + } + }, + "/api/v3/plants/{plant_id}/registry/logs/filter": { + "get": { + "description": "Filters registry log records for the specified plant by date, asset_id, map_id, asset_type, and map_type. At least one query parameter is required.", + "operationId": "filter_plant_registry_logs", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" + } + }, + { + "description": "Optional target date of data. Must follow the format 'YYYY-MM-DD'.", + "explode": false, + "in": "query", + "name": "date", + "schema": { + "description": "Optional target date of data. Must follow the format 'YYYY-MM-DD'.", + "examples": [ + "2024-01-24" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Optional asset ID filter.", + "explode": false, + "in": "query", + "name": "asset_id", + "schema": { + "description": "Optional asset ID filter.", + "pattern": "^[a-zA-Z0-9.\\-_ /:@+]*$", + "type": "string" + } + }, + { + "description": "Optional map ID filter.", + "explode": false, + "in": "query", + "name": "map_id", + "schema": { + "description": "Optional map ID filter.", + "pattern": "^[a-zA-Z0-9.\\-_ /:@+]*$", + "type": "string" + } + }, + { + "description": "Optional asset type filter.", + "explode": false, + "in": "query", + "name": "asset_type", + "schema": { + "description": "Optional asset type filter.", + "enum": [ + "device", + "inverter", + "edge", + "panel", + "panel_group", + "sensor" + ], + "type": "string" + } + }, + { + "description": "Optional map type filter.", + "explode": false, + "in": "query", + "name": "map_type", + "schema": { + "description": "Optional map type filter.", + "enum": [ + "device", + "string", + "edge", + "inverter", + "combiner", + "panel", + "panel_group", + "tracker", + "sensor" + ], + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of asset registration records for the specified plant and date", + "items": { + "$ref": "#/components/schemas/RegistryOutputBody" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Filter Plant Registry Logs", + "tags": [ + "v3/plants/registry" + ] + } + }, + "/api/v3/plants/{plant_id}/registry/register": { + "post": { + "description": "Registers a new asset (device, inverter, etc.) to the specified plant with mapping and metadata information.", + "operationId": "register_asset_to_plant", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Confirmation message indicating successful asset registration or operation details", + "type": "string" + } + } + }, + "description": "OK" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Register Asset to Plant", + "tags": [ + "v3/plants/registry" + ] + } + }, + "/api/v3/plants/{plant_id}/registry/snapshots": { + "get": { + "description": "Retrieves registry snapshot records for the specified plant and date, with optional asset_id, map_id, asset_type, and map_type filters.", + "operationId": "get_plant_registry_snapshots", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } + }, + { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", + "required": true, + "schema": { + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" + } + }, + { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "in": "path", + "name": "plant_id", + "required": true, + "schema": { + "description": "Patch Plant ID. A unique alphanumeric identifier representing the plant to fetch data for. Must be exactly 15 characters in length.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" + } + }, + { + "description": "Target date of data. Must follow the format 'YYYY-MM-DD'.", + "explode": false, + "in": "query", + "name": "date", + "required": true, + "schema": { + "description": "Target date of data. Must follow the format 'YYYY-MM-DD'.", + "examples": [ + "2024-01-24" + ], + "format": "date", + "type": "string" + } + }, + { + "description": "Optional asset ID filter.", + "explode": false, + "in": "query", + "name": "asset_id", + "schema": { + "description": "Optional asset ID filter.", + "pattern": "^[a-zA-Z0-9.\\-_ /:@+]*$", + "type": "string" + } + }, + { + "description": "Optional map ID filter.", + "explode": false, + "in": "query", + "name": "map_id", + "schema": { + "description": "Optional map ID filter.", + "pattern": "^[a-zA-Z0-9.\\-_ /:@+]*$", + "type": "string" + } + }, + { + "description": "Optional asset type filter.", + "explode": false, + "in": "query", + "name": "asset_type", + "schema": { + "description": "Optional asset type filter.", + "enum": [ + "device", + "inverter", + "edge", + "panel", + "panel_group", + "sensor" + ], + "type": "string" + } + }, + { + "description": "Optional map type filter.", + "explode": false, + "in": "query", + "name": "map_type", + "schema": { + "description": "Optional map type filter.", + "enum": [ + "device", + "string", + "edge", + "inverter", + "combiner", + "panel", + "panel_group", + "tracker", + "sensor" + ], + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource matches one of the passed values.", + "in": "header", + "name": "If-Match", + "schema": { + "description": "Succeeds if the server's resource matches one of the passed values.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "in": "header", + "name": "If-None-Match", + "schema": { + "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "in": "header", + "name": "If-Modified-Since", + "schema": { + "description": "Succeeds if the server's resource date is more recent than the passed date.", + "format": "date-time-http", + "type": "string" + } + }, + { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "in": "header", + "name": "If-Unmodified-Since", + "schema": { + "description": "Succeeds if the server's resource date is older or the same as the passed date.", + "format": "date-time-http", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Array of asset registration records for the specified plant and date", + "items": { + "$ref": "#/components/schemas/RegistryOutputBody" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK", + "headers": { + "ETag": { + "schema": { + "description": "Weak entity tag for conditional requests.", + "type": "string" + } + } + } }, "default": { "content": { @@ -5863,9 +10667,9 @@ "bearer": [] } ], - "summary": "Retrieve Time-Series Metrics Data", + "summary": "Retrieve Plant Registry Snapshots", "tags": [ - "v3/plants/metrics" + "v3/plants/registry" ] } }, @@ -5885,16 +10689,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -5987,7 +10791,7 @@ "application/json": { "schema": { "$ref": "#/components/schemas/StatPoint", - "description": "Installed panel capacity, module model counts, and device model stats at the end of the requested day." + "description": "Installed panel capacity, module model counts, device model stats, and inverter model stats at the end of the requested day." } } }, @@ -6023,10 +10827,10 @@ ] } }, - "/api/v3/plants/{plant_id}/registry/{record_type}": { - "get": { - "description": "Retrieves asset registry information for a specific plant filtered by record type. Includes registration status, mapping information, and historical data for the specified asset type. Authorization header and Account-Type header are required.", - "operationId": "get_asset_registration_on_plant", + "/api/v3/plants/{plant_id}/registry/unregister": { + "post": { + "description": "Unregisters an existing asset (device, inverter, etc.) from the specified plant when equipment is replaced or removed.", + "operationId": "unregister_asset_from_plant", "parameters": [ { "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", @@ -6039,16 +10843,16 @@ } }, { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", "name": "Account-Type", "required": true, "schema": { - "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'admin' grants full system access.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ "viewer", "manager", - "admin" + "temporary" ], "type": "string" } @@ -6068,107 +10872,225 @@ "patternDescription": "alphanum", "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnregisterBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "Confirmation message indicating successful asset unregistration or operation details", + "type": "string" + } + } + }, + "description": "OK" + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Unregister Asset from Plant", + "tags": [ + "v3/plants/registry" + ] + } + }, + "/api/v3/plants/{plant_id}/weather/forecast": { + "get": { + "description": "Retrieves weather forecast rows for the specified plant by using the plant metadata address and forwarding to weather-api.", + "operationId": "get_plant_weather_forecast", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, + "schema": { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "type": "string" + } }, { - "description": "The type of data to retrieve. Possible values:'logs': Returns all records that have changed on the specified date., 'snapshots': Returns all records that can be evaluated as 'registered' on the specified date.", - "in": "path", - "name": "record_type", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "in": "header", + "name": "Account-Type", "required": true, "schema": { - "description": "The type of data to retrieve. Possible values:'logs': Returns all records that have changed on the specified date., 'snapshots': Returns all records that can be evaluated as 'registered' on the specified date.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "enum": [ - "logs", - "snapshots" - ], - "examples": [ - "logs" + "viewer", + "manager", + "temporary" ], "type": "string" } }, { - "description": "Target date of data. Must follow the format 'YYYY-MM-DD'.", - "explode": false, - "in": "query", - "name": "date", + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch weather forecast data for.", + "in": "path", + "name": "plant_id", "required": true, "schema": { - "description": "Target date of data. Must follow the format 'YYYY-MM-DD'.", + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch weather forecast data for.", "examples": [ - "2024-01-24" + "unw4id41ud2p0wt" ], - "format": "date", + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", "type": "string" } }, { - "description": "The unique identifier for the asset.", + "description": "Number of forecast days to request from weather-api, including the current day.", "explode": false, "in": "query", - "name": "asset_id", + "name": "days", "schema": { - "description": "The unique identifier for the asset.", - "type": "string" + "default": 7, + "description": "Number of forecast days to request from weather-api, including the current day.", + "format": "int64", + "maximum": 7, + "minimum": 1, + "type": "integer" } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/WeatherForecastRow" + }, + "type": [ + "array", + "null" + ] + } + } + }, + "description": "OK" }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error" + } + }, + "security": [ { - "description": "The unique identifier for the map.", - "explode": false, - "in": "query", - "name": "map_id", + "bearer": [] + } + ], + "summary": "Retrieve Plant Weather Forecast", + "tags": [ + "v3/plants/weather" + ] + } + }, + "/api/v3/plants/{plant_id}/weather/observed": { + "get": { + "description": "Retrieves observed weather rows for the specified plant and date by using the plant metadata address and forwarding to weather-api.", + "operationId": "get_plant_weather_observed", + "parameters": [ + { + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", + "in": "header", + "name": "Authorization", + "required": true, "schema": { - "description": "The unique identifier for the map.", + "description": "Authorization header containing the Bearer token used for user authentication and access control. Format: 'Bearer '", "type": "string" } }, { - "description": "Succeeds if the server's resource matches one of the passed values.", + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", "in": "header", - "name": "If-Match", + "name": "Account-Type", + "required": true, "schema": { - "description": "Succeeds if the server's resource matches one of the passed values.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] + "description": "Specifies the type of user account making the request. Determines the access level and available operations. 'viewer' provides read-only access, 'manager' allows administrative operations, and 'temporary' provides scoped plant access.", + "enum": [ + "viewer", + "manager", + "temporary" + ], + "type": "string" } }, { - "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", - "in": "header", - "name": "If-None-Match", + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch observed weather data for.", + "in": "path", + "name": "plant_id", + "required": true, "schema": { - "description": "Succeeds if the server's resource matches none of the passed values. On writes, the special value * may be used to match any existing value.", - "items": { - "type": "string" - }, - "type": [ - "array", - "null" - ] + "description": "Plant ID. A unique alphanumeric identifier representing the plant to fetch observed weather data for.", + "examples": [ + "unw4id41ud2p0wt" + ], + "maxLength": 15, + "minLength": 15, + "patternDescription": "alphanum", + "type": "string" } }, { - "description": "Succeeds if the server's resource date is more recent than the passed date.", - "in": "header", - "name": "If-Modified-Since", + "description": "Local date to fetch observed weather for, formatted as YYYY-MM-DD.", + "explode": false, + "in": "query", + "name": "date", + "required": true, "schema": { - "description": "Succeeds if the server's resource date is more recent than the passed date.", - "format": "date-time-http", + "description": "Local date to fetch observed weather for, formatted as YYYY-MM-DD.", + "examples": [ + "2026-04-13" + ], + "format": "date", "type": "string" } }, { - "description": "Succeeds if the server's resource date is older or the same as the passed date.", - "in": "header", - "name": "If-Unmodified-Since", + "description": "Number of observed days to include up to and including date.", + "explode": false, + "in": "query", + "name": "before", "schema": { - "description": "Succeeds if the server's resource date is older or the same as the passed date.", - "format": "date-time-http", - "type": "string" + "default": 1, + "description": "Number of observed days to include up to and including date.", + "format": "int64", + "maximum": 31, + "minimum": 1, + "type": "integer" } } ], @@ -6177,9 +11099,8 @@ "content": { "application/json": { "schema": { - "description": "Array of asset registration records for the specified plant and date", "items": { - "$ref": "#/components/schemas/RegistryOutputBody" + "$ref": "#/components/schemas/WeatherObservedRow" }, "type": [ "array", @@ -6188,15 +11109,7 @@ } } }, - "description": "OK", - "headers": { - "ETag": { - "schema": { - "description": "Weak entity tag for conditional requests.", - "type": "string" - } - } - } + "description": "OK" }, "default": { "content": { @@ -6214,9 +11127,9 @@ "bearer": [] } ], - "summary": "Retrieve Plant Registry by Record Type", + "summary": "Retrieve Plant Observed Weather", "tags": [ - "v3/plants/registry" + "v3/plants/weather" ] } }