Voltair Assets API

The Assets API from Voltair — 6 operation(s) for assets.

Operations 7

GET /assets List assets #
GET /assets/{assetId} Get asset #
GET /defects List defects for an asset #
PATCH /defects/{defectId} Update a defect #
POST /defects:bulk-priority Bulk-update defect priority for a single (assetType, defectName) tuple #
GET /defect-priority-mapping Get the active defect priority mapping for the caller's organization #
PUT /defect-priority-mapping Replace the active defect priority mapping (priority values only) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/voltair-assets-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

voltair-assets-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Voltair ApiKeys Assets API
  version: 0.1.0
  description: 'Infrastructure inspection platform API. All endpoints are scoped to the authenticated organization via Bearer JWT or API key.

    All timestamp fields on this API (createdAt, updatedAt, scheduledFor, capturedAt, expiresAt, deletedAt, etc.) are Unix timestamps in milliseconds since the epoch (UTC). Both request and response bodies use this representation.'
servers:
- url: /
security:
- BearerAuth: []
- ApiKeyAuth: []
tags:
- name: Assets
paths:
  /assets:
    get:
      tags:
      - Assets
      operationId: listAssets
      summary: List assets
      description: 'Lists assets filtered by `siteId` (required). RLS confines results

        to the caller''s organization.

        '
      parameters:
      - name: siteId
        in: query
        required: true
        schema:
          type: string
          format: uuid
      - name: priority
        in: query
        schema:
          type: string
        description: Comma-separated priorities (high, medium, low, none)
      - $ref: '#/components/parameters/LimitParam'
      - $ref: '#/components/parameters/CursorParam'
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Asset'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /assets/{assetId}:
    parameters:
    - name: assetId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    get:
      tags:
      - Assets
      operationId: getAsset
      summary: Get asset
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                properties:
                  data:
                    $ref: '#/components/schemas/Asset'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /defects:
    get:
      tags:
      - Assets
      operationId: listDefects
      summary: List defects for an asset
      description: 'Lists defects filtered by `assetId` (required). RLS confines

        results to the caller''s organization.

        '
      parameters:
      - name: assetId
        in: query
        required: true
        schema:
          type: string
          format: uuid
      - $ref: '#/components/parameters/LimitParam'
      - $ref: '#/components/parameters/CursorParam'
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Defect'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /defects/{defectId}:
    parameters:
    - name: defectId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    patch:
      tags:
      - Assets
      operationId: updateDefect
      summary: Update a defect
      description: "Partial update of one or more mutable fields:\n  * `workDoneAt` -- Unix ms to mark the defect as addressed,\n    null to clear the mark (undo).\n  * `defectPriority` -- 1..4. Sending this field always sets\n    `prioritySource='manual'` so a subsequent bulk schema edit\n    cannot overwrite the reviewer's decision. May be sent\n    standalone; sending `prioritySource: 'manual'` alongside\n    is allowed but redundant.\n  * `prioritySource: 'mapping'` (alone, without\n    `defectPriority`) -- revert path. Re-derives\n    `defectPriority` from the org's active\n    `defect_priority_mappings` entry for this row's\n    `(asset_type, defect_name)` tuple. 409 with\n    `code='no_mapping_baseline'` if no live row in the same\n    tuple currently carries a mapping-derived priority (the\n    schema must be updated or the CVAT export re-run before a\n    revert can succeed).\n  * `prioritySource: 'cvat_other_defect'` -- read-only via\n    this endpoint; only the CVAT export can assign that source.\n  * `reviewed` -- bool. true stamps `reviewedAt=now()` and\n    `reviewedByActorId=<caller>` (the customer-portal \"Reviewed\n    by\" mark); false clears both. The reviewer identity is taken\n    from the auth context, never the body.\nAll writes trigger the `stream_priority_defects` database\ntrigger, which cascades through voltair-streams-priority to\nrecompute the owning asset and (if changed) site priority.\n"
      parameters:
      - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                workDoneAt:
                  type:
                  - number
                  - 'null'
                  description: Unix ms timestamp. Null clears the field.
                defectPriority:
                  type: integer
                  minimum: 1
                  maximum: 4
                  description: Manual priority override. Sets prioritySource='manual'.
                prioritySource:
                  type: string
                  enum:
                  - manual
                  - mapping
                  description: 'Sent alone with value ''mapping'' to revert a manual

                    override (re-derives priority from the active

                    mapping). Sent with defectPriority and value

                    ''manual'' is allowed but redundant -- defectPriority

                    alone implies manual.

                    '
                reviewed:
                  type: boolean
                  description: 'Customer-portal "Reviewed by" mark. true stamps

                    reviewedAt + reviewedByActorId from the auth context;

                    false clears both.

                    '
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - transactionId
                properties:
                  data:
                    $ref: '#/components/schemas/Defect'
                  transactionId:
                    type: string
                    format: uuid
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /defects:bulk-priority:
    post:
      tags:
      - Assets
      operationId: bulkUpdateDefectPriority
      summary: Bulk-update defect priority for a single (assetType, defectName) tuple
      description: 'Remaps every existing defect row in the caller''s organization

        whose `(assetType, defectName)` matches the request to a new

        priority. Optionally also rewrites the org''s active

        `defect_priority_mapping` so future CVAT exports use the new

        value as the schema-derived default.


        Reviewer manual overrides are NEVER touched -- the bulk UPDATE

        filters by `priority_source=''mapping''`. CVAT "Other" catch-all rows

        (`priority_source=''cvat_other_defect''`) are also excluded.


        At least one of `applyToExisting` or `alsoUpdateMapping` must be

        true (otherwise the request is a no-op and is rejected).


        Permission: `defects:write`.

        '
      parameters:
      - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - assetType
              - defectName
              - priority
              properties:
                assetType:
                  type: string
                  description: Slug of the asset_type column (e.g. 'wood_pole').
                defectName:
                  type: string
                  description: 'Composed defect_name string (e.g. ''Pole (Wood): Crack/Split - major'').'
                priority:
                  type: integer
                  minimum: 1
                  maximum: 4
                applyToExisting:
                  type: boolean
                  default: false
                  description: When true, update every matching mapping-derived defect row in the org.
                alsoUpdateMapping:
                  type: boolean
                  default: false
                  description: When true, also bump defect_priority_mappings to a new active version with this tuple's priority changed.
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - transactionId
                properties:
                  data:
                    type: object
                    required:
                    - defectsUpdated
                    - assetsRecomputed
                    - sitesRecomputed
                    properties:
                      defectsUpdated:
                        type: integer
                      assetsRecomputed:
                        type: integer
                      sitesRecomputed:
                        type: integer
                      mappingVersion:
                        type: integer
                        description: Present only when alsoUpdateMapping=true.
                      mappingId:
                        type: string
                        format: uuid
                        description: Present only when alsoUpdateMapping=true.
                  transactionId:
                    type: string
                    format: uuid
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: alsoUpdateMapping=true but the active mapping has no entry for (assetType, defectName).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
  /defect-priority-mapping:
    get:
      tags:
      - Assets
      operationId: getDefectPriorityMapping
      summary: Get the active defect priority mapping for the caller's organization
      description: 'Returns the active row from `app.defect_priority_mappings` (a

        per-org JSONB schema mapping `(Equipment, Type, Defect)` to

        `(asset_type, priority)`). 404 when none exists; the org must be

        seeded via the admin API before customer-facing edits are allowed.


        Permission: `defects:read`.

        '
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                properties:
                  data:
                    $ref: '#/components/schemas/DefectPriorityMapping'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
    put:
      tags:
      - Assets
      operationId: putDefectPriorityMapping
      summary: Replace the active defect priority mapping (priority values only)
      description: 'Versioned write: deactivates the prior active row and inserts a

        new one with `version+1`. Customer-facing edits are LIMITED TO

        priority values: the `(Equipment, Type, Defect)` keyset is

        immutable from this endpoint. Adding or removing keys requires a

        super-admin re-import via the admin API after the CVAT project

        schema is updated -- 409 with `code=''schema_keys_immutable''`

        when the request would change the keyset.


        Optional `applyToExisting` (default false) bulk-rewrites every

        existing defect row whose `(asset_type, defect_name)`

        tuple has a changed priority value, while preserving rows

        marked `priority_source=''manual''`.


        Permission: `defects:write`.

        '
      parameters:
      - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - mapping
              properties:
                mapping:
                  type: object
                  description: 'Three-level Equipment / Type / Defect JSONB. See

                    voltair-admin-api/src/service/defect_priority_mapping.go::validateMappingShape

                    for the canonical shape; it is enforced server-side.

                    '
                applyToExisting:
                  type: boolean
                  default: false
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                - transactionId
                properties:
                  data:
                    type: object
                    required:
                    - id
                    - version
                    - mapping
                    - defectsUpdated
                    - assetsRecomputed
                    - sitesRecomputed
                    properties:
                      id:
                        type: string
                        format: uuid
                      version:
                        type: integer
                      mapping:
                        type: object
                      defectsUpdated:
                        type: integer
                      assetsRecomputed:
                        type: integer
                      sitesRecomputed:
                        type: integer
                  transactionId:
                    type: string
                    format: uuid
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: Mapping keyset differs from the active version (or asset_type changed).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  responses:
    BadRequest:
      description: Bad request or validation error
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Insufficient permissions
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Authentication required
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalError:
      description: Internal server error
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    TooManyRequests:
      description: Rate limit exceeded
      headers:
        X-Request-Id:
          $ref: '#/components/headers/XRequestId'
        X-RateLimit-Limit:
          schema:
            type: integer
          description: Maximum requests per window
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: Requests remaining in current window
        X-RateLimit-Reset:
          schema:
            type: number
          description: Unix timestamp (ms) when the window resets
        Retry-After:
          schema:
            type: integer
          description: Seconds until the next rate limit window
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    ErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: Machine-readable error code
            message:
              type: string
              description: Human-readable description
            details:
              type: object
              additionalProperties: true
              description: Optional structured info (e.g. field-level validation errors, conflictingEventIds)
    Asset:
      type: object
      required:
      - id
      - organizationId
      - siteId
      - name
      - priority
      - deletedAt
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          format: uuid
        organizationId:
          type: string
          format: uuid
        siteId:
          type: string
          format: uuid
        name:
          type: string
        priority:
          $ref: '#/components/schemas/AssetPriority'
        deletedAt:
          type:
          - number
          - 'null'
        createdAt:
          type: number
        updatedAt:
          type: number
    AssetPriority:
      type: string
      enum:
      - high
      - medium
      - low
      - none
    DefectEvidence:
      type: object
      required:
      - id
      - defectId
      - reviewUncertain
      - deletedAt
      - createdAt
      description: 'Per-CVAT-shape evidence row tied to a Defect (merged defect).

        Multiple rows for the same Defect mean the same defect was

        annotated across multiple photos. Returned from the API only when

        explicitly expanded.

        '
      properties:
        id:
          type: string
          format: uuid
        defectId:
          type: string
          format: uuid
        cvatShapeUid:
          type:
          - string
          - 'null'
          description: Stable id `<cvat_task_id>:<cvat_shape_id>` of the source CVAT shape. NULL on non-CVAT-sourced rows (none today).
        evidenceMediaId:
          type:
          - string
          - 'null'
          format: uuid
          description: Original (uncropped) mission_media row that the lineman annotated.
        bbox:
          type:
          - object
          - 'null'
          description: 'Bounding box in evidence_media_id pixel coordinates: {"x1": int, "y1": int, "x2": int, "y2": int}.'
        description:
          type:
          - string
          - 'null'
          description: Free-form text the lineman attached to this specific CVAT shape (Comment attribute on regular labels, Description on the Other catch-all).
        reviewUncertain:
          type: boolean
          description: True when this shipped evidence crop came from QA consensus=agreed_uncertain. The customer platform rolls this up to an Uncertain tag on the merged defect.
        deletedAt:
          type:
          - number
          - 'null'
        createdAt:
          type: number
    DefectPriorityMapping:
      type: object
      required:
      - id
      - organizationId
      - mapping
      - version
      - isActive
      description: 'Per-org JSONB rule set mapping a CVAT

        `(Equipment, Type, Defect)` tuple to an `asset_type` and a

        `priority` integer (1..4). One active row per organization at a

        time; saving a new version deactivates the prior active row.

        '
      properties:
        id:
          type: string
          format: uuid
        organizationId:
          type: string
          format: uuid
        mapping:
          type: object
          description: '`{ "<Equipment>": { "<Type>": { "asset_type": "<slug>", "defects": { "<Defect>": <int 1-4> } } } }`.

            Use `"n/a"` as the `<Type>` key for CVAT labels with no

            real subtype.

            '
        version:
          type: integer
        isActive:
          type: boolean
    Defect:
      type: object
      required:
      - id
      - organizationId
      - assetId
      - assetInspectionId
      - assetType
      - defectName
      - defectPriority
      - prioritySource
      - workDoneAt
      - deletedAt
      - createdAt
      description: 'Merged defect. One row per (assetInspectionId, defectName) at a

        time. Per-CVAT-shape evidence (bbox, evidence photo, original

        per-shape description) lives on DefectEvidence and is exposed via

        `expand[]=evidence` (or, on listInspections,

        `expand[]=asset.defects.evidence`). The `description` field on

        this schema is the denormalized "longest non-empty live evidence

        description" cache maintained by the CVAT export.

        '
      properties:
        id:
          type: string
          format: uuid
        organizationId:
          type: string
          format: uuid
        assetId:
          type: string
          format: uuid
        assetInspectionId:
          type: string
          format: uuid
        assetType:
          type: string
        defectName:
          type: string
        defectPriority:
          type: integer
          minimum: 1
          maximum: 4
          description: 1 = highest severity, 4 = none/normal
        prioritySource:
          type: string
          enum:
          - mapping
          - manual
          - cvat_other_defect
          description: 'Where `defectPriority` came from. Bulk schema edits only

            touch rows where `prioritySource=''mapping''` so reviewer

            manual overrides survive. `cvat_other_defect` is excluded

            because that priority is contractor-self-rated per shape,

            not derived from the org-wide schema.

            '
        defectPriorityMappingId:
          type:
          - string
          - 'null'
          format: uuid
          description: Active defect_priority_mappings.id at the time defectPriority was last derived. Informational/audit; nullable on legacy rows imported before this column existed.
        workDoneAt:
          type:
          - number
          - 'null'
          description: Unix ms timestamp marking this defect as addressed in the field. Null while unaddressed. When non-null, the defect is excluded from the asset priority rollup.
        reviewedAt:
          type:
          - number
          - 'null'
          description: Unix ms timestamp the customer marked this defect reviewed (the 'Reviewed by' mark). Null when not reviewed. Set/cleared via PATCH /defects/{id} { reviewed }.
        reviewedByName:
          type:
          - string
          - 'null'
          description: Display name of the actor who set the current reviewed state. Null when not reviewed; empty string for api-key actors with no user row. Resolved from reviewedByActorId; present on list/expand reads, omitted on the mutation response.
        description:
          type:
          - string
          - 'null'
          description: Longest non-empty `description` across the parent's live DefectEvidence rows. Maintained by the CVAT export — the API and UI read this column directly rather than aggregating across evidence on every fetch.
        deletedAt:
          type:
          - number
          - 'null'
        createdAt:
          type: number
        evidence:
          type: array
          description: Populated only when `evidence` is requested via expand[]. Otherwise omitted.
          items:
            $ref: '#/components/schemas/DefectEvidence'
    PaginationMeta:
      type: object
      required:
      - cursor
      properties:
        cursor:
          type:
          - string
          - 'null'
          description: Opaque cursor for the next page; null when no more results
        total:
          type: integer
          description: Total matching results across all pages; included when cheaply computable
  headers:
    XRequestId:
      description: Unique request identifier (UUID)
      schema:
        type: string
        format: uuid
  parameters:
    CursorParam:
      name: cursor
      in: query
      schema:
        type: string
      description: Opaque pagination cursor from a previous response
    LimitParam:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 200
      description: Page size (default 50, max 200)
    IdempotencyKeyHeader:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        format: uuid
      description: Idempotency key for POST requests. If a transaction with the same key already exists for the org, the server returns the original response without re-executing. Keys are valid for 48 hours.
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Cognito JWT access token
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Organization-scoped API key