Voltair Clusters API
The Clusters API from Voltair — 3 operation(s) for clusters.
The Clusters API from Voltair — 3 operation(s) for clusters.
openapi: 3.0.3
info:
title: Voltair ApiKeys Clusters 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: Clusters
paths:
/clusters:
get:
tags:
- Clusters
operationId: listClusters
summary: List clusters
parameters:
- name: siteId
in: query
schema:
type: string
format: uuid
- name: bounds
in: query
schema:
type: string
description: 'Bounding box: minLng,minLat,maxLng,maxLat'
- $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/Cluster'
meta:
$ref: '#/components/schemas/PaginationMeta'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
post:
tags:
- Clusters
operationId: createCluster
summary: Create cluster
parameters:
- $ref: '#/components/parameters/IdempotencyKeyHeader'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateClusterRequest'
responses:
'201':
description: Created
headers:
X-Request-Id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
type: object
required:
- data
- transactionId
properties:
data:
$ref: '#/components/schemas/Cluster'
transactionId:
type: string
format: uuid
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
/clusters/bulk:
post:
tags:
- Clusters
operationId: bulkCreateClusters
summary: Bulk create clusters
parameters:
- $ref: '#/components/parameters/IdempotencyKeyHeader'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- clusters
properties:
clusters:
type: array
items:
$ref: '#/components/schemas/CreateClusterRequest'
responses:
'201':
description: Created
headers:
X-Request-Id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
type: object
required:
- data
- transactionId
properties:
data:
type: array
items:
$ref: '#/components/schemas/Cluster'
transactionId:
type: string
format: uuid
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
/clusters/{clusterId}:
parameters:
- name: clusterId
in: path
required: true
schema:
type: string
format: uuid
get:
tags:
- Clusters
operationId: getCluster
summary: Get cluster
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/Cluster'
'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:
- Clusters
operationId: updateCluster
summary: Update cluster
description: Updates a cluster. Cannot edit id or organizationId.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateClusterRequest'
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/Cluster'
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':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
delete:
tags:
- Clusters
operationId: deleteCluster
summary: Delete cluster
description: Soft-deletes a cluster. Sites in the cluster retain their clusterId but the cluster is hidden from queries.
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/Cluster'
transactionId:
type: string
format: uuid
'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'
components:
schemas:
PaginationMeta:
type: object
required:
- cursor
properties:
cursor:
type: string
nullable: true
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
ClusterLevel:
type: integer
minimum: 1
maximum: 3
description: 1 = coarsest, 3 = finest
MultiLineStringGeometry:
type: object
required:
- type
- coordinates
properties:
type:
type: string
enum:
- MultiLineString
coordinates:
type: array
items:
type: array
items:
type: array
items:
type: number
minItems: 2
maxItems: 3
minItems: 2
ClusterGeometry:
description: GeoJSON geometry for clusters (Polygon, LineString, or MultiLineString)
oneOf:
- $ref: '#/components/schemas/PolygonGeometry'
- $ref: '#/components/schemas/LineStringGeometry'
- $ref: '#/components/schemas/MultiLineStringGeometry'
discriminator:
propertyName: type
mapping:
Polygon: '#/components/schemas/PolygonGeometry'
LineString: '#/components/schemas/LineStringGeometry'
MultiLineString: '#/components/schemas/MultiLineStringGeometry'
Cluster:
type: object
required:
- id
- organizationId
- count
- parentId
- level
- siteTypes
- geometryCounts
- name
- centroid
- bounds
- geometry
- deletedAt
- createdAt
- updatedAt
properties:
id:
type: string
format: uuid
organizationId:
type: string
format: uuid
count:
type: integer
parentId:
type: string
format: uuid
nullable: true
level:
$ref: '#/components/schemas/ClusterLevel'
siteTypes:
type: array
items:
type: string
geometryCounts:
$ref: '#/components/schemas/GeometryCounts'
name:
type: string
centroid:
type: array
description: '[longitude, latitude]'
items:
type: number
minItems: 2
maxItems: 2
bounds:
type: array
description: '[minLng, minLat, maxLng, maxLat]'
items:
type: number
minItems: 4
maxItems: 4
geometry:
$ref: '#/components/schemas/ClusterGeometry'
deletedAt:
type: number
nullable: true
createdAt:
type: number
updatedAt:
type: number
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)
CreateClusterRequest:
type: object
required:
- name
- count
- level
- centroid
- bounds
- geometry
properties:
name:
type: string
count:
type: integer
parentId:
type: string
format: uuid
level:
$ref: '#/components/schemas/ClusterLevel'
siteTypes:
type: array
items:
type: string
geometryCounts:
$ref: '#/components/schemas/GeometryCounts'
centroid:
type: array
items:
type: number
minItems: 2
maxItems: 2
bounds:
type: array
items:
type: number
minItems: 4
maxItems: 4
geometry:
$ref: '#/components/schemas/ClusterGeometry'
GeometryCounts:
type: object
required:
- point
- line
- area
properties:
point:
type: integer
line:
type: integer
area:
type: integer
UpdateClusterRequest:
type: object
properties:
name:
type: string
count:
type: integer
parentId:
type: string
format: uuid
level:
$ref: '#/components/schemas/ClusterLevel'
siteTypes:
type: array
items:
type: string
geometryCounts:
$ref: '#/components/schemas/GeometryCounts'
centroid:
type: array
items:
type: number
minItems: 2
maxItems: 2
bounds:
type: array
items:
type: number
minItems: 4
maxItems: 4
geometry:
$ref: '#/components/schemas/ClusterGeometry'
LineStringGeometry:
type: object
required:
- type
- coordinates
properties:
type:
type: string
enum:
- LineString
coordinates:
type: array
items:
type: array
items:
type: number
minItems: 2
maxItems: 3
minItems: 2
PolygonGeometry:
type: object
required:
- type
- coordinates
properties:
type:
type: string
enum:
- Polygon
coordinates:
type: array
items:
type: array
items:
type: array
items:
type: number
minItems: 2
maxItems: 3
minItems: 4
responses:
BadRequest:
description: Bad request or validation 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'
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'
Conflict:
description: Conflict (duplicate resource, in-use resource, or undo conflict)
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'
Unauthorized:
description: Authentication required
headers:
X-Request-Id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
parameters:
CursorParam:
name: cursor
in: query
schema:
type: string
description: Opaque pagination cursor from a previous response
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.
LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 200
description: Page size (default 50, max 200)
headers:
XRequestId:
description: Unique request identifier (UUID)
schema:
type: string
format: uuid
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