SageOx Firmware Admin API

The Firmware Admin API from SageOx — 9 operation(s) for firmware admin.

OpenAPI Specification

sageox-firmware-admin-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: SageOx Admin Firmware Admin API
  version: 1.0.0
  description: "# API Reference\n\n## Overview\n\nSageOx is a platform that captures team knowledge from discussions, decisions, and work context into a Ledger (per-repo historical record) and Team Context (team-wide shared knowledge). The SageOx API provides programmatic access to manage repositories, recordings, notifications, and team data with high performance and reliability.\n\n## Base URLs\n\n| Environment | URL |\n|---|---|\n| Local Development | `http://localhost:3000` |\n| Test | `https://test.sageox.ai` |\n| Production | `https://sageox.ai` |\n\n## Authentication\n\nAll requests to the SageOx API require authentication via JWT tokens issued by the Better Auth service.\n\nInclude your token in the `Authorization` header:\n```\nAuthorization: Bearer <token>\n```\n\n**Initial Auth Flow (CLI)**\n- CLI initiates device flow to obtain initial credentials\n- Exchange device code for JWT token\n- Store token securely for subsequent API requests\n- Refresh token when expired\n\n**Authenticated Endpoints**\n- Pass JWT in `Authorization: Bearer <token>` header\n- Tokens contain user identity and team membership\n- Invalid or expired tokens return 401 Unauthorized\n\n## Response Format\n\n### Success\nStandard response format with optional pagination metadata:\n```json\n{\n  \"success\": true,\n  \"data\": { ... }\n}\n```\n\n### Error\nAll errors follow a consistent format:\n```json\n{\n  \"success\": false,\n  \"error\": \"Human-readable error description\"\n}\n```\n\n**Status Codes**\n- `400` — Bad Request: Invalid parameters or malformed request\n- `401` — Unauthorized: Missing or invalid authentication token\n- `403` — Forbidden: Insufficient permissions for this resource\n- `404` — Not Found: Resource does not exist\n- `409` — Conflict: Request conflicts with current state (e.g., duplicate)\n- `429` — Rate Limited: Too many requests; retry with backoff\n- `500` — Internal Error: Server error; contact support if persistent\n\n## Pagination\n\nList endpoints support cursor-based pagination via query parameters:\n\n**Query Parameters**\n- `limit` — Number of results per page (default: 20, max: 100)\n- `offset` — Number of results to skip (default: 0)\n\n**Response Metadata**\nList responses include pagination info:\n```json\n{\n  \"success\": true,\n  \"data\": [ ... ],\n  \"meta\": {\n    \"total\": 150,\n    \"limit\": 20,\n    \"offset\": 0,\n    \"hasMore\": true\n  }\n}\n```\n\n## ID Formats\n\nResources use standardized ID formats for easy identification:\n\n| Resource | Format | Length | Example |\n|---|---|---|---|\n| Team | `team_<cuid2>` | 10 chars | `team_abc1234567` |\n| User | `usr_<cuid2>` | 10 chars | `usr_xyz9876543` |\n| Repository | `repo_<uuidv7>` | 36 chars | `repo_01934f5a-8b9c-7def-abcd-0123456789ab` |\n| Recording | `rec_<uuidv7>` | 36 chars | `rec_01934f5a-8b9c-7def-abcd-0123456789ab` |\n| Image | `img_<uuidv7>` | 36 chars | `img_01934f5a-8b9c-7def-abcd-0123456789ab` |\n| Notification | `notif_<cuid2>` | 14 chars | `notif_ab1cd2ef3g` |\n\nUse these IDs for all API operations. IDs are unique across environments.\n\n## Rate Limiting\n\nThe API applies rate limiting to protect against abuse and ensure fair access:\n\n**Authenticated Endpoints**\n- Limited per user: depends on subscription tier\n- Quota resets hourly\n\n**Unauthenticated Endpoints**\n- Limited per IP address\n- More restrictive than authenticated limits\n\nWhen rate limited, the API returns `429 Too Many Requests`. Retry requests with exponential backoff:\n```\nwait = min(2^attempt * 100ms, 10s)\n```\n\n## Timestamps\n\nAll timestamps in API responses use RFC 3339 / ISO 8601 format in UTC:\n```\n2025-12-03T10:30:00Z\n```\n\nUse this format when providing timestamps in requests as well. The API rejects timestamps in other formats.\n\n## SDKs & Tools\n\n- **OpenAPI Spec** — Full schema available at `/api/openapi.json`\n- **Postman Collection** — Import from `/api/postman.json` for interactive exploration\n- **CLI** — Use `ox` CLI for command-line access\n"
  contact:
    name: SageOx Team
  license:
    name: MIT
servers:
- url: http://localhost:3000
  description: Devcontainer
- url: https://test.sageox.ai
  description: Test
- url: https://sageox.ai
  description: Production
security:
- bearerAuth: []
tags:
- name: Firmware Admin
paths:
  /api/v1/internal/hq/firmware:
    post:
      operationId: uploadFirmwareRelease
      summary: Upload new firmware release
      description: 'Create a firmware release record and get a pre-signed S3 upload URL for

        the binary. The release starts in `staged` status and must be explicitly

        activated via the activate endpoint.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - device_type
              - version
              - sha256
              - size_bytes
              properties:
                device_type:
                  type: string
                  example: scribe
                version:
                  type: string
                  maxLength: 31
                  example: 1.2.0
                sha256:
                  type: string
                  pattern: ^[0-9a-f]{64}$
                signature:
                  type: string
                  description: Base64 RSA-2048 signature. Required for production releases.
                size_bytes:
                  type: integer
                  format: int64
                  maximum: 4194304
                changelog:
                  type: string
                  maxLength: 2048
                release_date:
                  type: string
                  format: date
                min_version:
                  type: string
                  maxLength: 31
                channel:
                  type: string
                  default: stable
                  enum:
                  - stable
                  - early-access
                  - beta
                  - nightly
      responses:
        '201':
          description: Release created. Upload the binary to the returned URL.
          content:
            application/json:
              schema:
                type: object
                properties:
                  release:
                    $ref: '#/paths/~1api~1v1~1internal~1hq~1firmware~1%7Bfirmware_id%7D/patch/responses/200/content/application~1json/schema'
                  upload_url:
                    type: string
                    description: Pre-signed S3 PUT URL for uploading the firmware binary. Expires in 15 minutes.
        '400':
          description: Invalid request (missing fields, bad SHA-256 format, etc.).
        '409':
          description: Version already exists for this device type and channel.
    get:
      operationId: listFirmwareReleases
      summary: List firmware releases
      description: 'List all firmware releases for a device type, newest first.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: device_type
        in: query
        description: Filter by device type. Defaults to `scribe`.
        schema:
          type: string
          default: scribe
      - name: limit
        in: query
        schema:
          type: integer
          default: 50
          maximum: 100
      - name: offset
        in: query
        schema:
          type: integer
          default: 0
      responses:
        '200':
          description: Paginated list of firmware releases.
          content:
            application/json:
              schema:
                type: object
                required:
                - items
                - pagination
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/paths/~1api~1v1~1internal~1hq~1firmware~1%7Bfirmware_id%7D/patch/responses/200/content/application~1json/schema'
                  pagination:
                    type: object
                    required:
                    - total
                    - limit
                    - offset
                    - has_more
                    properties:
                      total:
                        type: integer
                        format: int64
                      limit:
                        type: integer
                      offset:
                        type: integer
                      has_more:
                        type: boolean
  /api/v1/internal/hq/firmware/{firmware_id}:
    patch:
      operationId: updateFirmwareRelease
      summary: Update min_version security floor and/or rollout percentage
      description: "Update mutable fields on a firmware release. At least one of `min_version`\nor `rollout_percent` must be supplied.\n\n- `min_version`: devices running below this floor get a forced update (no\n  skip option). Send an empty string to clear the floor.\n- `rollout_percent`: percentage of the cohort (0-100) eligible to receive\n  this release. Device IDs are hashed into a 0-99 bucket; a device is\n  served only if its bucket is below the configured percent. Use this to\n  gradually expand a release from canary to full rollout.\n"
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: firmware_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: At least one of min_version or rollout_percent is required.
              properties:
                min_version:
                  type: string
                  nullable: true
                  description: Semver minimum version. Devices below this get forced update. Empty string clears.
                  maxLength: 31
                rollout_percent:
                  type: integer
                  nullable: true
                  minimum: 0
                  maximum: 100
                  description: Cohort fraction served this release (0-100). 100 means everyone in cohort.
      responses:
        '200':
          description: Updated release.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    example: fw_019d68dd-eda3-70d6-84bf-000000000001
                  device_type:
                    type: string
                  version:
                    type: string
                  s3_key:
                    type: string
                  sha256:
                    type: string
                  signature:
                    type: string
                  size_bytes:
                    type: integer
                    format: int64
                  changelog:
                    type: string
                  release_date:
                    type: string
                    format: date
                  min_version:
                    type: string
                  rollout_percent:
                    type: integer
                    minimum: 0
                    maximum: 100
                    description: Cohort fraction (0-100) currently served this release.
                  status:
                    type: string
                    enum:
                    - staged
                    - active
                  channel:
                    type: string
                    enum:
                    - stable
                    - early-access
                    - beta
                    - nightly
                  uploaded_by:
                    type: string
                  activated_at:
                    type: string
                    format: date-time
                  paused_at:
                    type: string
                    format: date-time
                    nullable: true
                    description: When non-null, manifest resolver returns 204 for this release fleet-wide.
                  paused_by:
                    type: string
                    nullable: true
                    description: User ID of the operator who paused the release.
                  pause_reason:
                    type: string
                    nullable: true
                    description: Human-readable justification for the pause.
                  created_at:
                    type: string
                    format: date-time
                  updated_at:
                    type: string
                    format: date-time
        '400':
          description: Neither min_version nor rollout_percent supplied, or rollout_percent out of range.
        '404':
          description: Release not found.
  /api/v1/internal/hq/firmware/{firmware_id}/activate:
    post:
      operationId: activateFirmwareRelease
      summary: Activate a staged firmware release
      description: 'Atomically returns the current active release to `staged` and activates

        the specified one. Only releases in `staged` status can be activated.


        **Rollback:** To rollback, activate any previous staged release.

        The old active release returns to `staged`, so it can be re-activated later.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: firmware_id
        in: path
        required: true
        schema:
          type: string
          example: fw_019d68dd-eda3-70d6-84bf-000000000001
      responses:
        '200':
          description: Release activated.
          content:
            application/json:
              schema:
                $ref: '#/paths/~1api~1v1~1internal~1hq~1firmware~1%7Bfirmware_id%7D/patch/responses/200/content/application~1json/schema'
        '400':
          description: Release is not in staged status.
        '404':
          description: Release not found.
  /api/v1/internal/hq/firmware/{firmware_id}/pause:
    post:
      operationId: pauseFirmwareRelease
      summary: Pause a firmware release (fleet-wide kill-switch)
      description: 'Sets `paused_at` on the release. While paused, the manifest resolver

        returns 204 No Content for this release even for devices in its rollout

        cohort. Independent of `rollout_percent` — pause is the kill-switch primitive.


        The audit trail records `paused_by` and `pause_reason`.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: firmware_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - reason
              properties:
                reason:
                  type: string
                  description: Human-readable justification (e.g. 'elevated crash rate', 'customer complaint').
                  maxLength: 512
      responses:
        '200':
          description: Release paused.
          content:
            application/json:
              schema:
                $ref: '#/paths/~1api~1v1~1internal~1hq~1firmware~1%7Bfirmware_id%7D/patch/responses/200/content/application~1json/schema'
        '400':
          description: Missing reason or release already paused.
        '404':
          description: Release not found.
  /api/v1/internal/hq/firmware/{firmware_id}/resume:
    post:
      operationId: resumeFirmwareRelease
      summary: Resume a paused firmware release
      description: 'Clears `paused_at`, `paused_by`, and `pause_reason` on the release. The

        manifest resolver will once again serve this release to eligible devices.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: firmware_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Release resumed.
          content:
            application/json:
              schema:
                $ref: '#/paths/~1api~1v1~1internal~1hq~1firmware~1%7Bfirmware_id%7D/patch/responses/200/content/application~1json/schema'
        '400':
          description: Release is not paused.
        '404':
          description: Release not found.
  /api/v1/internal/hq/firmware/catalog:
    get:
      operationId: listFirmwareCatalog
      summary: List firmware catalog with fleet rollout signals
      description: 'Catalog view of firmware releases enriched with fleet-derived signals:

        device counts, adoption series, crash counts, and pause status. Used by

        the HQ web console to render the fleet dashboard. Distinct from the basic

        list endpoint because it joins against `physical_devices` and

        `device_update_events`.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: device_type
        in: query
        description: Filter by hardware type (e.g. scribe). Defaults to scribe.
        schema:
          type: string
          default: scribe
      - name: channel
        in: query
        description: Filter by release channel.
        schema:
          type: string
          enum:
          - stable
          - early-access
          - beta
          - nightly
      - name: status
        in: query
        description: Filter by release status.
        schema:
          type: string
          enum:
          - staged
          - active
      responses:
        '200':
          description: Catalog entries, newest first.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  required:
                  - id
                  - version
                  - device_type
                  - channel
                  - status
                  description: 'Catalog entry enriched with fleet-derived adoption and crash signals.

                    Computed by joining `firmware_releases` against `physical_devices` and

                    `device_update_events`.

                    '
                  properties:
                    id:
                      type: string
                      example: fw_019d68dd-eda3-70d6-84bf-000000000001
                    version:
                      type: string
                    device_type:
                      type: string
                    channel:
                      type: string
                      enum:
                      - stable
                      - early-access
                      - beta
                      - nightly
                    status:
                      type: string
                      enum:
                      - staged
                      - active
                    uploaded_by:
                      type: string
                    created_at:
                      type: string
                      format: date-time
                    activated_at:
                      type: string
                      format: date-time
                      nullable: true
                    paused_at:
                      type: string
                      format: date-time
                      nullable: true
                    paused_by:
                      type: string
                      nullable: true
                    pause_reason:
                      type: string
                      nullable: true
                    sha256:
                      type: string
                      pattern: ^[0-9a-f]{64}$
                    size_bytes:
                      type: integer
                      format: int64
                    current_device_count:
                      type: integer
                      description: Devices currently reporting this version via firmware check-in.
                    ever_updated_count:
                      type: integer
                      description: Distinct devices that have reached `verified` for this release.
                    crash_count_last_7d:
                      type: integer
                      description: Crash reports in the last 7 days from devices on this release.
                    adoption_series:
                      type: array
                      description: Daily adoption counts (newest last) for sparkline rendering.
                      items:
                        type: object
                        required:
                        - date
                        - count
                        properties:
                          date:
                            type: string
                            format: date
                          count:
                            type: integer
  /api/v1/internal/hq/firmware/{firmware_id}/dryrun:
    post:
      operationId: dryrunFirmwareRollout
      summary: Preview rollout cohort without targeting devices
      description: 'Evaluate a candidate cohort selector and report how many devices would

        match, how many are eligible (after policy/version/hardware filters), and

        which devices are excluded and why. Read-only — no devices are targeted.


        Used by the HQ console to give operators confidence before they create a

        real rollout.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: firmware_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        description: 'Optional cohort selector. Omit the body to preview the release against

          its default cohort.

          '
        content:
          application/json:
            schema:
              type: object
              description: 'Optional cohort selector for the dryrun preview. The body itself is

                optional — POSTing an empty body returns the default preview for the

                release.

                '
              properties:
                target_cohort:
                  type: object
                  description: Cohort selector to evaluate. Same shape as `firmware_rollouts.target_cohort`.
                  properties:
                    channel:
                      type: string
                      enum:
                      - stable
                      - early-access
                      - beta
                      - nightly
                    from_version_glob:
                      type: string
                      description: Semver glob (e.g. '1.2.*') matching device's current firmware_version.
                    group_ids:
                      type: array
                      items:
                        type: string
      responses:
        '200':
          description: Dryrun result.
          content:
            application/json:
              schema:
                type: object
                required:
                - firmware_id
                - matched_count
                - eligible_count
                - excluded_disabled_devices
                - excluded_pinned_elsewhere
                - below_min_version
                - already_on_target_version
                properties:
                  firmware_id:
                    type: string
                    description: fw_-prefixed release ID this dryrun was evaluated against.
                    example: fw_019d68dd-eda3-70d6-84bf-000000000001
                  matched_count:
                    type: integer
                    format: int64
                    description: Devices matching the cohort selector before policy filters.
                  eligible_count:
                    type: integer
                    format: int64
                    description: Devices that would actually be served the release.
                  excluded_disabled_devices:
                    type: integer
                    format: int64
                    description: Count of devices excluded because their `ota_policy` is `disabled`.
                  excluded_pinned_elsewhere:
                    type: integer
                    format: int64
                    description: Count of devices with `ota_policy=pinned` pinned to a different release.
                  below_min_version:
                    type: integer
                    format: int64
                    description: Count of devices below this release's `min_version` floor.
                  already_on_target_version:
                    type: integer
                    format: int64
                    description: Count of devices already running the release's target version.
                  notes:
                    type: string
                    description: 'Free-form operator-facing notes. Used to flag approximations or

                      degraded preview modes when precise cohort matching is unavailable.

                      '
        '400':
          description: Invalid cohort selector.
        '404':
          description: Release not found.
  /api/v1/internal/hq/devices/{device_id}/firmware-target:
    put:
      operationId: pinDeviceFirmwareTarget
      summary: Pin a device to a specific firmware release
      description: 'Creates or replaces the firmware pin for a single device. While pinned,

        the manifest resolver serves the targeted release regardless of the

        device''s channel — provided the device''s `ota_policy` is `pinned`.


        Pins are the primary pilot-testing primitive: bring a single rig onto

        beta firmware without affecting the rest of the fleet.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: device_id
        in: path
        required: true
        description: Hardware device ID (12-character lowercase hex MAC for scribe).
        schema:
          type: string
          example: aabbccddeeff
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - firmware_id
              - reason
              properties:
                firmware_id:
                  type: string
                  description: fw_-prefixed release ID to pin.
                  example: fw_019d68dd-eda3-70d6-84bf-000000000001
                reason:
                  type: string
                  description: Human-readable justification (e.g. 'pilot device for beta firmware').
                  maxLength: 512
      responses:
        '200':
          description: Pin created or replaced.
          content:
            application/json:
              schema:
                type: object
                required:
                - device_id
                - firmware_id
                - pinned_by
                - pinned_at
                description: A pin overriding channel-based resolution for one device.
                properties:
                  device_id:
                    type: string
                    description: Hardware device ID (e.g. 12-char lowercase hex MAC for scribe).
                    example: aabbccddeeff
                  firmware_id:
                    type: string
                    description: fw_-prefixed release ID this device is pinned to.
                    example: fw_019d68dd-eda3-70d6-84bf-000000000001
                  reason:
                    type: string
                    nullable: true
                  pinned_by:
                    type: string
                    description: User ID of the operator who created the pin.
                  pinned_at:
                    type: string
                    format: date-time
        '400':
          description: Invalid request (missing fields, unknown firmware_id).
        '404':
          description: Device not found.
    delete:
      operationId: unpinDeviceFirmwareTarget
      summary: Remove a device firmware pin
      description: 'Deletes the pin for a device. The device returns to channel-based

        resolution. Note this does not change `ota_policy`; if the device is

        still in `pinned` policy with no pin, the resolver returns 204

        (intentional: prevents accidental re-targeting onto the channel).


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: device_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '204':
          description: Pin removed (or never existed).
        '404':
          description: Device not found.
  /api/v1/internal/hq/devices/{device_id}/ota-policy:
    get:
      operationId: getDeviceOTAPolicy
      summary: Get the OTA policy for a device
      description: 'Returns the device''s OTA policy plus optional firmware pin (when policy is

        `pinned`). Precedence in the resolver: `disabled` > `pinned` > `auto`.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: device_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Device OTA policy state.
          content:
            application/json:
              schema:
                type: object
                required:
                - policy
                properties:
                  policy:
                    $ref: '#/paths/~1api~1v1~1internal~1hq~1devices~1%7Bdevice_id%7D~1ota-policy/put/requestBody/content/application~1json/schema/properties/policy'
                  reason:
                    type: string
                    nullable: true
                    description: Human-readable justification for non-`auto` policy.
                  set_by:
                    type: string
                    nullable: true
                    description: User ID of the operator who last set the policy.
                  set_at:
                    type: string
                    format: date-time
                    nullable: true
                  firmware_target:
                    allOf:
                    - $ref: '#/paths/~1api~1v1~1internal~1hq~1devices~1%7Bdevice_id%7D~1firmware-target/put/responses/200/content/application~1json/schema'
                    nullable: true
                    description: Present when `policy` is `pinned` and a pin exists.
        '404':
          description: Device not found.
    put:
      operationId: setDeviceOTAPolicy
      summary: Set the OTA policy for a device
      description: 'Set `ota_policy` to `auto`, `pinned`, or `disabled` for a single device.

        Records `set_by` and `set_at` for the audit trail.


        Switching to `pinned` does NOT create a pin — call PUT

        `/devices/{device_id}/firmware-target` separately. Until a pin exists,

        a `pinned` device receives 204 from the manifest resolver.


        Switching to `disabled` is the strongest signal: the resolver returns 204

        unconditionally. Used for engineering rigs and devices flashed manually.


        **Auth:** JWT + HQ admin only.

        '
      tags:
      - Firmware Admin
      security:
      - bearerAuth: []
      parameters:
      - name: device_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - policy
              - reason
              properties:
                policy:
                  type: string
                  enum:
                  - auto
                  - pinned
                  - disabled
                  description: 'Per-device OTA policy.

                    - `auto`: normal channel/rollout resolution.

                    - 

# --- truncated at 32 KB (33 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/sageox/refs/heads/main/openapi/sageox-firmware-admin-api-openapi.yml