{
  "openapi": "3.1.0",
  "info": {
    "title": "Reefy Dashboard API",
    "version": "1.0.0",
    "summary": "Manage Reefy devices and apps programmatically.",
    "description": "REST API powering the reefy.ai dashboard. Authentication is cookie-based via Google OAuth (see [/auth.md](https://reefy.ai/auth.md) for the walkthrough). All responses are JSON unless noted. Errors return a consistent `{\"error\": \"...\"}` envelope. \n\nThis spec documents the agent-relevant subset of the operator-facing surface. Admin and internal endpoints are deliberately omitted.",
    "contact": {
      "name": "Reefy",
      "url": "https://x.com/reefy_ai"
    },
    "license": {
      "name": "Open source",
      "url": "https://github.com/reefyai/reefy"
    }
  },
  "servers": [
    {
      "url": "https://reefy.ai",
      "description": "Production"
    },
    {
      "url": "https://reefy.dev",
      "description": "Development"
    }
  ],
  "x-api-versioning": {
    "strategy": "url-path",
    "current": "v1",
    "supported": [
      "v1"
    ],
    "deprecation-policy": "Breaking changes ship under a new major (/api/v2/). Old majors stay live for 6 months after the new major's GA. Deprecated endpoints return RFC 8594 `Sunset` and `Deprecation` response headers naming the cutover date and a `Link: rel=\"sunset\"` to the migration guide. Policy page: https://reefy.ai/auth.md#api-versioning.",
    "deprecation-headers": [
      "Sunset",
      "Deprecation",
      "Link"
    ],
    "deprecation-policy-url": "https://reefy.ai/auth.md#api-versioning"
  },
  "externalDocs": {
    "description": "Long-form product reference",
    "url": "https://reefy.ai/llms-full.txt"
  },
  "tags": [
    {
      "name": "devices",
      "description": "List, adopt, manage Reefy devices."
    },
    {
      "name": "apps",
      "description": "Install/uninstall apps on a device."
    },
    {
      "name": "auth",
      "description": "Sign-in, session, identity."
    },
    {
      "name": "mqtt",
      "description": "Mint short-lived JWTs for device MQTT."
    },
    {
      "name": "health",
      "description": "Service-level health probes."
    }
  ],
  "components": {
    "securitySchemes": {
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "session_id",
        "description": "Set by `/api/auth/login/google` after successful OAuth. All `/api/*` endpoints except `/api/auth/*` require this cookie."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message."
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "Device": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "hostname": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "adopted": {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "online",
              "offline",
              "pending"
            ]
          },
          "stage": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "fw_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "pool_pct": {
            "type": [
              "integer",
              "null"
            ],
            "description": "LVM thin-pool fill 0-100."
          },
          "instances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AppInstance"
            }
          }
        },
        "required": [
          "id",
          "uuid",
          "hostname",
          "adopted",
          "status"
        ]
      },
      "AppInstance": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "device_id": {
            "type": "integer"
          },
          "app_slug": {
            "type": "string",
            "examples": [
              "openclaw",
              "hermes",
              "ollama"
            ]
          },
          "instance_name": {
            "type": "string"
          },
          "instance_uuid": {
            "type": "string",
            "format": "uuid"
          },
          "display_name": {
            "type": "string"
          },
          "slot": {
            "type": "integer"
          },
          "host_port": {
            "type": "integer"
          },
          "tty_port": {
            "type": [
              "integer",
              "null"
            ]
          },
          "app_image": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "app_slug",
          "instance_name",
          "host_port"
        ]
      },
      "CatalogApp": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "image": {
            "type": "string"
          },
          "gpu": {
            "type": "boolean"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "slug",
          "name",
          "version",
          "image"
        ]
      },
      "User": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "integer"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "public_id": {
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "email"
        ]
      },
      "MqttToken": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "JWT for EMQX, signed with the deployment's MQTT_JWT_SECRET."
          },
          "expires_in": {
            "type": "integer",
            "description": "Seconds until expiry."
          }
        },
        "required": [
          "token"
        ]
      },
      "Job": {
        "type": "object",
        "description": "Async job snapshot for long-running mutations.",
        "properties": {
          "job_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed"
            ]
          },
          "kind": {
            "type": "string",
            "examples": [
              "install_app",
              "factory_reset"
            ]
          },
          "device_uuid": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "progress": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 100
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "description": "Populated when status=completed."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Populated when status=failed."
          }
        },
        "required": [
          "job_id",
          "status",
          "kind"
        ]
      },
      "Page": {
        "type": "object",
        "description": "Pagination envelope around any list response.",
        "properties": {
          "items": {
            "type": "array",
            "items": {}
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass to the next request's `cursor` query param. null when there are no more pages."
          },
          "total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Total items across all pages, if cheap to compute."
          }
        },
        "required": [
          "items",
          "next_cursor"
        ]
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Client-supplied UUID to make this mutation idempotent. Subsequent requests with the same key (within 24h) return the original result without re-executing. Recommended for any agent-driven mutation - safe retry on network failure without risk of double-execution."
      },
      "PageLimit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        },
        "description": "Max items to return (1-200)."
      },
      "PageCursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Opaque cursor from previous response's `next_cursor` field. Omit to start from the beginning."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid session cookie.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            },
            "description": "Bearer challenge with resource_metadata URL (RFC 9728)."
          }
        }
      },
      "AsyncAccepted": {
        "description": "Mutation accepted; running asynchronously. Poll the URL in the `Location` header for status. Body carries the initial job snapshot.",
        "headers": {
          "Location": {
            "schema": {
              "type": "string",
              "format": "uri"
            },
            "description": "URL to poll for job status (GET)."
          },
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Suggested seconds before polling."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Job"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found, not visible to the caller, or wrong owner.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "Unix epoch when the bucket resets."
          },
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds until next allowed request."
          }
        }
      }
    }
  },
  "security": [
    {
      "sessionCookie": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Service health probe",
        "description": "Returns 200 OK if the Flask app is up and the database is reachable. Public endpoint.",
        "security": [],
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Get the signed-in user",
        "operationId": "getMe",
        "responses": {
          "200": {
            "description": "Current session user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Drop the session cookie",
        "operationId": "logout",
        "responses": {
          "200": {
            "description": "Logged out.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/devices": {
      "get": {
        "tags": [
          "devices"
        ],
        "summary": "List the caller's devices",
        "operationId": "listDevices",
        "description": "Returns devices owned by the authenticated user with their installed app instances inline. Adopted devices come first, ordered by display_order then created_at DESC. Supports cursor-based pagination.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageLimit"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "online",
                "offline",
                "pending",
                "all"
              ],
              "default": "all"
            },
            "description": "Filter by device status."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated devices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "devices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Device"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "total": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "devices",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/devices/catalog": {
      "get": {
        "tags": [
          "apps"
        ],
        "summary": "List the app catalog",
        "operationId": "listCatalog",
        "description": "Apps that can be installed on a Reefy device: OpenClaw, Hermes, Ollama, vLLM, SGLang, qr-access, etc. Supports pagination for forward compatibility when the catalog grows.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageLimit"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Catalog page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CatalogApp"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "apps",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/devices/{device_id}/adopt": {
      "post": {
        "tags": [
          "devices"
        ],
        "summary": "Adopt an unadopted device",
        "operationId": "adoptDevice",
        "description": "Move a pending/online device into the caller's fleet. Signs the device's bootstrap CSR with the user's CA, allocates a Cloudflared tunnel, and pushes the initial desired state.",
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "luks_key": {
                    "type": "string",
                    "description": "Optional pre-existing LUKS key. Generated if omitted."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Adopted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "device": {
                      "$ref": "#/components/schemas/Device"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/devices/{device_id}/sync": {
      "post": {
        "tags": [
          "devices"
        ],
        "summary": "Force desired-state push to the device",
        "operationId": "syncDevice",
        "description": "Recomputes the device's desired state and republishes it over MQTT. Idempotent: safe to retry. The device's reconciler picks it up on receipt.",
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "state_hash": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/devices/{device_id}/reboot": {
      "post": {
        "tags": [
          "devices"
        ],
        "summary": "Reboot the device",
        "operationId": "rebootDevice",
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Reboot command sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/devices/{device_id}/instances": {
      "post": {
        "tags": [
          "apps"
        ],
        "summary": "Install an app on the device (async)",
        "operationId": "installApp",
        "description": "Allocates ports + slot, writes the instance row, and pushes a new desired state that includes the new container. Returns 202 Accepted with a Job in the body; poll the Location header for completion. Typical install time: 10-60s depending on image size and network.",
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_slug": {
                    "type": "string"
                  },
                  "display_name": {
                    "type": "string"
                  },
                  "env": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "app_slug"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Installed synchronously (small images).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppInstance"
                }
              }
            }
          },
          "202": {
            "$ref": "#/components/responses/AsyncAccepted"
          },
          "400": {
            "description": "Bad input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/jobs/{job_id}": {
      "get": {
        "tags": [
          "devices"
        ],
        "summary": "Poll an async-job status",
        "operationId": "getJob",
        "description": "Polled by clients that started a 202-accepted mutation. Returns the same Job envelope until status reaches `completed` or `failed`.",
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/devices/{device_id}/instances/{instance_id}": {
      "delete": {
        "tags": [
          "apps"
        ],
        "summary": "Uninstall an app instance",
        "operationId": "uninstallApp",
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "instance_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Uninstalled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/devices/{device_id}/rename": {
      "post": {
        "tags": [
          "devices"
        ],
        "summary": "Rename a device",
        "operationId": "renameDevice",
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/devices/{device_id}": {
      "delete": {
        "tags": [
          "devices"
        ],
        "summary": "Delete a device from the fleet",
        "operationId": "deleteDevice",
        "description": "Removes the device row, tears down its Cloudflared tunnel, and stops accepting its MQTT publishes. The physical device keeps running until reflashed.",
        "parameters": [
          {
            "name": "device_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/devices/scrub-stale-online": {
      "post": {
        "tags": [
          "devices"
        ],
        "summary": "Demote stale-online rows to offline",
        "operationId": "scrubStaleOnline",
        "description": "Operator-callable trigger for the same scrubber that runs every 5 minutes in the background. Idempotent.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "threshold_seconds": {
                    "type": "integer",
                    "default": 1800
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Number demoted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scrubbed": {
                      "type": "integer"
                    },
                    "threshold_seconds": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/mqtt/token": {
      "post": {
        "tags": [
          "mqtt"
        ],
        "summary": "Mint a short-lived MQTT JWT",
        "operationId": "mintMqttToken",
        "description": "Returns a JWT signed with the deployment's MQTT_JWT_SECRET. The dashboard UI uses this to authenticate to EMQX over WebSockets; programmatic agents subscribing to device events use the same.",
        "responses": {
          "200": {
            "description": "Token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MqttToken"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/dashboard/events": {
      "get": {
        "tags": [
          "devices"
        ],
        "summary": "Subscribe to dashboard events (SSE)",
        "operationId": "streamDashboardEvents",
        "description": "Server-Sent Events stream of device + instance lifecycle events (adopted, online/offline transitions, install progress). Mirrors what the in-process dashboard UI sees. Each event is a JSON object on its own `data:` line.",
        "responses": {
          "200": {
            "description": "SSE stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Stream of `data: {...}\\n\\n` events."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  }
}