Voltair Assets API
The Assets API from Voltair — 6 operation(s) for assets.
The Assets API from Voltair — 6 operation(s) for assets.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/voltair-assets-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
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: 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