# Synced copy of vendor/harrishill/packages/services/BoardDeveloperBridgeService/assets/web/openapi.yaml — do not edit here; update the source spec and re-sync.
openapi: 3.1.0
info:
  title: Board Connect device API
  version: "1"
  description: >
    HTTP API hosted on a Board device (port 8843) for developer tooling — discovery, pairing,
    and managing installed apps (APKs) and web-app bundles. Consumed by the Board Connect web
    UI and board-connect-cli. Served unauthenticated at
    GET /openapi.yaml for discovery. See
    board-eng-docs/design/sdk-platform/board-connect-webapp-workflow.md.
servers:
  - url: http://{host}:8843
    variables:
      host: { default: board.local }
security:
  - bearerAuth: []
paths:
  /board/info:
    get:
      summary: Discovery probe (unauthenticated)
      security: []
      responses:
        "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/BoardInfo" } } } }
  /openapi.yaml:
    get:
      summary: This spec (unauthenticated)
      security: []
      responses: { "200": { description: OK } }
  /v1/pair:
    post:
      summary: Pair with a code shown on the device (manual / human flow)
      security: []
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/PairRequest" } } }
      responses:
        "200": { description: Paired, content: { application/json: { schema: { $ref: "#/components/schemas/PairResponse" } } } }
        "401": { $ref: "#/components/responses/Error" }
        "409": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/Error" }
  /v1/pair/request:
    post:
      summary: Tap-to-approve pairing (agent flow) — long-polls until the user taps Approve on the device
      security: []
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/PairRequestStart" } } }
      responses:
        "200": { description: Approved, content: { application/json: { schema: { $ref: "#/components/schemas/PairResponse" } } } }
        "403": { $ref: "#/components/responses/Error", description: "pairing_disabled (the device setting is off)" }
        "408": { $ref: "#/components/responses/Error", description: "timed out awaiting approval; retry" }
        "429": { $ref: "#/components/responses/Error" }
  /v1/board/status:
    get: { summary: Readiness, responses: { "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/Status" } } } } } }
  /v1/board/capabilities:
    get: { summary: Protocol version + capability tags, responses: { "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/Capabilities" } } } } } }
  /v1/board/version:
    get: { summary: OS version, responses: { "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/Version" } } } } } }
  /v1/apps:
    get:
      summary: List dev-installed apps (APKs + web apps)
      responses: { "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/AppsResponse" } } } } }
    post:
      summary: Install an app (multipart). The bundle type (APK vs packed web app) is detected by content; the response reports packageName for an APK or appId for a web app.
      requestBody:
        required: true
        content: { multipart/form-data: { schema: { type: object, properties: { file: { type: string, format: binary } } } } }
      responses:
        "200": { description: Installed, content: { application/json: { schema: { $ref: "#/components/schemas/InstallResult" } } } }
        "400": { $ref: "#/components/responses/Error", description: "invalid_bundle / incomplete_transfer" }
        "422": { $ref: "#/components/responses/Error", description: "validation_failed / invalid_apk / ambiguous_bundle / unrecognized_bundle / missing_config / invalid_app_id / missing_entry / no_sdk" }
        "503": { $ref: "#/components/responses/Error", description: browser_unavailable }
  /v1/apps/cleanup:
    post: { summary: Uninstall all dev-managed apps, responses: { "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/CleanupResponse" } } } } } }
  /v1/apps/{id}/launch:
    post:
      summary: Launch an app by id (APK package name or web-app appId)
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "204": { description: Launched }, "404": { $ref: "#/components/responses/Error" } }
  /v1/apps/{id}/stop:
    post:
      summary: Force-stop an APK by package. Web apps cannot be stopped individually (422 stop_unsupported).
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "204": { description: Stopped }, "404": { $ref: "#/components/responses/Error" }, "422": { $ref: "#/components/responses/Error", description: stop_unsupported } }
  /v1/apps/{id}:
    delete:
      summary: Uninstall an app by id (APK package name or web-app appId)
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { "204": { description: Removed }, "404": { $ref: "#/components/responses/Error" } }
  /v1/apps/{id}/logs:
    get:
      summary: Dump recent logs for an app by id (APK package or web-app appId)
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
        - { name: tag, in: query, required: false, schema: { type: string } }
        - { name: level, in: query, required: false, schema: { type: string, enum: [V, D, I, W, E, F] } }
      responses: { "200": { description: Log lines, content: { text/plain: { schema: { type: string } } } }, "404": { $ref: "#/components/responses/Error" } }
  # Deprecated split aliases — prefer the unified /v1/apps surface above. Kept for back-compat.
  /v1/webapps:
    post:
      deprecated: true
      summary: "[deprecated: use POST /v1/apps] Install or update a web-app bundle (multipart zip)"
      requestBody:
        required: true
        content: { multipart/form-data: { schema: { type: object, properties: { file: { type: string, format: binary } } } } }
      responses:
        "200": { description: Installed, content: { application/json: { schema: { $ref: "#/components/schemas/InstallResult" } } } }
        "400": { $ref: "#/components/responses/Error", description: "invalid_bundle / incomplete_transfer" }
        "422": { $ref: "#/components/responses/Error", description: "missing_config / invalid_app_id / missing_entry / no_sdk" }
        "503": { $ref: "#/components/responses/Error", description: browser_unavailable }
  /v1/webapps/{appId}/launch:
    post:
      deprecated: true
      summary: "[deprecated: use POST /v1/apps/{id}/launch] Launch a web app by appId"
      parameters: [{ name: appId, in: path, required: true, schema: { type: string, format: uuid } }]
      responses: { "204": { description: Launched }, "404": { $ref: "#/components/responses/Error", description: webapp_not_found } }
  /v1/webapps/{appId}:
    delete:
      deprecated: true
      summary: "[deprecated: use DELETE /v1/apps/{id}] Remove a web app by appId"
      parameters: [{ name: appId, in: path, required: true, schema: { type: string, format: uuid } }]
      responses: { "204": { description: Removed }, "404": { $ref: "#/components/responses/Error", description: webapp_not_found } }
  /v1/webapps/{appId}/logs:
    get:
      deprecated: true
      summary: "[deprecated: use GET /v1/apps/{id}/logs] Dump recent logs for a web app (tag BoardWebApp:<appId>)"
      parameters:
        - { name: appId, in: path, required: true, schema: { type: string, format: uuid } }
        - { name: level, in: query, required: false, schema: { type: string, enum: [V, D, I, W, E, F] } }
      responses: { "200": { description: Log lines, content: { text/plain: { schema: { type: string } } } }, "404": { $ref: "#/components/responses/Error", description: webapp_not_found } }
  /v1/screenshot:
    get:
      summary: Capture a PNG screenshot (rate-limited 1/sec)
      responses: { "200": { description: PNG, content: { image/png: { schema: { type: string, format: binary } } } }, "429": { $ref: "#/components/responses/Error" } }
  /v1/media:
    post:
      summary: Push a media file to BoardMediaPlayer (multipart)
      requestBody:
        required: true
        content: { multipart/form-data: { schema: { type: object, properties: { file: { type: string, format: binary } } } } }
      responses: { "200": { description: Pushed, content: { application/json: { schema: { $ref: "#/components/schemas/PushMediaResult" } } } } }
  /v1/media/launch:
    post: { summary: Open BoardMediaPlayer, responses: { "204": { description: Launched } } }
  /v1/logs/dump:
    get:
      summary: Dump recent logs for a `bdb`-installed package
      parameters:
        - { name: package, in: query, required: true, schema: { type: string } }
        - { name: tag, in: query, required: false, schema: { type: string } }
        - { name: level, in: query, required: false, schema: { type: string, enum: [V, D, I, W, E, F] } }
      responses: { "200": { description: Log lines, content: { text/plain: { schema: { type: string } } } } }
  /v1/logs:
    get:
      summary: Stream logs over WebSocket (token via ?token= query param)
      security: []
      responses: { "101": { description: Switching Protocols (WebSocket) } }
  /v1/paired-clients:
    get: { summary: List paired clients, responses: { "200": { description: OK, content: { application/json: { schema: { $ref: "#/components/schemas/PairedClients" } } } } } }
  /v1/paired-clients/{id}:
    delete:
      summary: Revoke a paired client
      parameters: [{ name: id, in: path, required: true, schema: { type: integer, format: int64 } }]
      responses: { "204": { description: Revoked }, "400": { $ref: "#/components/responses/Error" } }
components:
  securitySchemes:
    bearerAuth: { type: http, scheme: bearer }
  responses:
    Error:
      description: Error
      content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } }
  schemas:
    BoardInfo:
      type: object
      properties:
        name: { type: string }
        serial: { type: string }
        model: { type: string }
        os: { type: string }
        apiVersions: { type: array, items: { type: string } }
        capabilities: { type: array, items: { type: string } }
    Status: { type: object, properties: { ready: { type: boolean } } }
    Capabilities:
      type: object
      properties:
        protocolVersion: { type: integer }
        osVersion: { type: string }
        capabilities: { type: array, items: { type: string } }
    Version: { type: object, properties: { version: { type: string } } }
    App:
      type: object
      properties:
        packageName: { type: string }
        label: { type: string }
        versionName: { type: string }
        versionCode: { type: integer, format: int64 }
        kind: { type: string, enum: [apk, webapp] }
        appId: { type: string, format: uuid, nullable: true }
      required: [packageName, kind]
    AppsResponse: { type: object, properties: { apps: { type: array, items: { $ref: "#/components/schemas/App" } } } }
    InstallResult:
      type: object
      properties: { packageName: { type: string }, appId: { type: string, format: uuid, nullable: true } }
    CleanupResponse: { type: object, properties: { removed: { type: integer } } }
    PushMediaResult: { type: object, properties: { name: { type: string }, sizeBytes: { type: integer, format: int64 } } }
    PairRequest: { type: object, required: [code, label], properties: { code: { type: string }, label: { type: string } } }
    PairRequestStart: { type: object, required: [clientName], properties: { clientName: { type: string } } }
    PairResponse: { type: object, properties: { token: { type: string }, id: { type: integer, format: int64 } } }
    PairedClient:
      type: object
      properties:
        id: { type: integer, format: int64 }
        label: { type: string }
        pairedAt: { type: integer, format: int64 }
        lastSeenAt: { type: integer, format: int64 }
        sourceIp: { type: string, nullable: true }
    PairedClients: { type: object, properties: { clients: { type: array, items: { $ref: "#/components/schemas/PairedClient" } } } }
    Error:
      type: object
      properties:
        error:
          type: string
          description: >
            Machine-readable code. Web-app install: invalid_bundle, missing_config,
            invalid_app_id, missing_entry, no_sdk, webapp_not_found, webapp_install_failed.
            Others: package_not_installed, package_not_managed, validation_failed, invalid_apk,
            incomplete_transfer, wrong_code, expired, not_pairing, pairing_disabled,
            rate_limited, locked_out.
        message: { type: string }
      required: [error, message]
