HotSpotVU is currently available to businesses in Canada only.
Reference · API

Implementation candidate · managed-site validation required

One contract for your integration.

The API root is /api/iot/v1. Download /iot/openapi.json for authoritative operations, authentication requirements, request fields, response models, and errors.

Integration at a glance

Scoped authority

Partner, browser, device and infrastructure credentials have different roles.

One versioned contract

Use implemented operation IDs, schemas and clients from the downloadable kit.

Explicit failure handling

Paginate results and use bounded retries with operation-specific idempotency.

Target supported hardware and an enabled site. Your application retains its own health and recovery responsibilities.

Public interface groups

GET/POST /account supports account state and sandbox registration. /products, /batches, and /devices organize the fleet. /devices/{id}/credentials accepts a CSR; /devices/{id}/revoke handles credential revocation.

/requests shows manufacturer request state. /sites and /sites/{location}/requests provide authorized site views. /requests/{id}/decision accepts allowed, denied, or revoked decisions from the human site owner. /sessions provides authorized connection history.

/tokens, /delegations, /webhooks, /webhook-deliveries, and /production-application support integration and operations. Consult the contract for supported verbs and payloads.

Use stable device identifiers returned by the service. Do not use a MAC address as the durable primary identity or allow device-supplied location identifiers to establish authorization.

Authentication and access

Trusted partner services use scoped, expiring service-account bearer credentials. Browser sessions, device credentials, and partner automation credentials are separate.

Authorize every operation within the caller’s company, assigned fleet, or site permissions. A manufacturer can see its own devices without being able to enumerate unrelated businesses or their clients.

Device runtime interfaces

/device/config, /device/health, and /device/renew use certificate-bound device reporting bearer tokens. They do not accept partner enrollment authority as a substitute for device identity.

/device/renew also requires a signed private-key proof and idempotent event identifier. See the Linux guide and OpenAPI contract for exact signing bytes and validation. Reporting tokens expire with their associated certificate, and earlier valid tokens are not immediately invalidated by renewal.

AAA, gateway-management, and service-administration endpoints in the contract have separate credentials and permissions. Those secrets never belong in manufacturer firmware, public code, or support messages.

Reliable integration

Device enrollment is repeatable using the same external_id, product_id, and batch_id. A conflicting product or batch returns 409. Repeating an unchanged permission decision does not advance the site revision. A generic Idempotency-Key header is not implemented.

Inventory, request, session, and delivery lists use Laravel pagination with 50 records per page. Follow the page query parameter and retain stable identifiers in your own system.

Treat the returned policy decision and observed enforcement separately. An accepted revocation does not prove an offline gateway has received it. Retry transient failures with backoff and retain operation identifiers for support.

Read your authorized inventory

Set HOTSPOTVU_API to your assigned service origin and HOTSPOTVU_IOT_TOKEN to a valid scoped service credential in a trusted backend environment. This example reads your inventory; never put the token in a public frontend or device image.

Read your authorized inventory shell
curl --fail --silent --show-error \
  --header "Authorization: Bearer $HOTSPOTVU_IOT_TOKEN" \
  "$HOTSPOTVU_API/api/iot/v1/devices"

Backend client examples

The bundled Python client and JavaScript client with TypeScript declarations expose partner-authenticated operations. Client-side bindings omit human-only site decisions and token creation. Install them from the downloaded kit; see the developer overview for installation.

Read request.status for current connection freshness. device.state is an inventory lifecycle field. GET list responses use data and pagination metadata; create-product and enroll-device responses are resource objects.

Read fleet inventory · Python backend python
import os
from hotspotvu_connect import Client, ApiError

api = Client(os.environ["HOTSPOTVU_API"], os.environ["HOTSPOTVU_IOT_TOKEN"])
try:
    for device in api.pages("/devices"):
        # state is registry lifecycle (active/revoked), not Wi-Fi status.
        print(device["id"], device["state"])
    for request in api.pages("/requests"):
        status = request["status"]
        print(request["endpoint_id"], status["authorization"],
              status["connection"], status["internet_reachable"],
              status["last_confirmed_at"], status["fresh_until"])
except ApiError as error:
    # Log the status, never the token or a credential bundle.
    print(f"HotSpotVU request failed: HTTP {error.status}")
    raise
fleet.mjs · JavaScript backend with TypeScript declarations javascript
import { HotSpotVuClient, HotSpotVuError } from '@hotspotvu/connectivity'

const api = new HotSpotVuClient(
  process.env.HOTSPOTVU_API, process.env.HOTSPOTVU_IOT_TOKEN
)
try {
  for await (const device of api.devices()) {
    console.log(device.id, device.state) // active/revoked; not connectivity
  }
  for await (const request of api.connectionRequests()) {
    const { authorization, connection, internet_reachable, fresh_until } = request.status
    console.log(request.endpoint_id, { authorization, connection, internet_reachable, fresh_until })
  }
} catch (error) {
  if (error instanceof HotSpotVuError) console.error('HotSpotVU HTTP status:', error.status)
  throw error
}

Versioning and evaluation

Build generated clients from the published contract and pin the compatible client version. Validate requests and examples in the sandbox before production use.

Sandbox limits default to ten devices and one authorized test site. Production activation and supported deployment limits are reviewed separately.

API operations

Search the implemented operations below. Authentication, field schemas, and response codes come from the downloadable OpenAPI contract.

38 operations · API base path /api/iot/v1

GET/accountRead active company IoT account

Operation ID: getAccount

Returns account=null before registration. Partner token with read scope, or an authenticated active-company member session. Read-only delegation includes authorized owner fleets.

Authentication: PartnerToken or BrowserSession

Successful response

{
  "type": "object",
  "properties": {
    "account": {
      "anyOf": [
        {
          "$ref": "#/components/schemas/Account"
        },
        {
          "type": "null"
        }
      ]
    },
    "can_manage": {
      "type": "boolean"
    },
    "network_enabled": {
      "type": "boolean"
    },
    "limits": {
      "type": "object",
      "properties": {
        "sandbox_devices": {
          "type": "integer",
          "const": 10
        },
        "sandbox_sites": {
          "type": "integer",
          "const": 1
        }
      },
      "required": [
        "sandbox_devices",
        "sandbox_sites"
      ]
    }
  },
  "required": [
    "account",
    "can_manage",
    "network_enabled",
    "limits"
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/accountCreate or return self-service sandbox

Operation ID: registerAccount

Register active company once. Existing registration is returned without changing capabilities. Defaults: ten devices, one sandbox site. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "capabilities": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "manufacturer",
          "provider",
          "operator"
        ]
      },
      "minItems": 1,
      "maxItems": 3
    }
  },
  "required": [
    "capabilities"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "tenant_id": {
      "type": "string",
      "format": "uuid"
    },
    "status": {
      "type": "string",
      "enum": [
        "sandbox",
        "production",
        "suspended"
      ]
    },
    "capabilities": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "manufacturer",
          "provider",
          "operator"
        ]
      }
    },
    "device_limit": {
      "type": "integer",
      "minimum": 1
    },
    "site_limit": {
      "type": "integer",
      "minimum": 1
    },
    "infrastructure_issuer": {
      "type": "boolean"
    },
    "production_application": {
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": true
    },
    "review_notes": {
      "type": [
        "string",
        "null"
      ]
    },
    "reviewed_by": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "created_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "updated_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    }
  },
  "required": [
    "id",
    "tenant_id",
    "status",
    "capabilities",
    "device_limit",
    "site_limit",
    "created_at",
    "updated_at"
  ],
  "description": "Persisted account record. Capabilities and submitted application JSON are decoded in API responses."
}

Response codes: 201, 401, 403, 404, 409, 419, 422, 429, 503

GET/productsList own and delegated products

Operation ID: listProducts

Partner token with read scope, or an authenticated active-company member session. Read-only delegation includes authorized owner fleets.

Authentication: PartnerToken or BrowserSession

page · query · optional

Successful response

{
  "allOf": [
    {
      "$ref": "#/components/schemas/PaginationMetadata"
    },
    {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Product"
          }
        }
      },
      "required": [
        "data"
      ]
    }
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/productsCreate product

Operation ID: createProduct

Partner token with write scope, or a company administrator session. Writes affect the own account; delegated access is read-only.

Authentication: PartnerToken or BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 120,
      "minLength": 1
    },
    "platform": {
      "type": "string",
      "maxLength": 80,
      "minLength": 1
    },
    "category": {
      "type": "string",
      "maxLength": 80,
      "minLength": 1
    }
  },
  "required": [
    "name",
    "platform",
    "category"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "account_id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string",
      "maxLength": 120
    },
    "platform": {
      "type": "string",
      "maxLength": 80
    },
    "category": {
      "type": "string",
      "maxLength": 80
    },
    "created_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "updated_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    }
  },
  "required": [
    "id",
    "account_id",
    "name",
    "platform",
    "category",
    "created_at",
    "updated_at"
  ]
}

Response codes: 201, 401, 403, 404, 409, 419, 422, 429, 503

GET/batchesList own and delegated batches

Operation ID: listBatches

Partner token with read scope, or an authenticated active-company member session. Read-only delegation includes authorized owner fleets.

Authentication: PartnerToken or BrowserSession

page · query · optional

Successful response

{
  "allOf": [
    {
      "$ref": "#/components/schemas/PaginationMetadata"
    },
    {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Batch"
          }
        }
      },
      "required": [
        "data"
      ]
    }
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/batchesCreate manufacturing batch

Operation ID: createBatch

Product must belong to own account. Partner token with write scope, or a company administrator session. Writes affect the own account; delegated access is read-only.

Authentication: PartnerToken or BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 120,
      "minLength": 1
    },
    "product_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    }
  },
  "required": [
    "name",
    "product_id"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "product_id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string",
      "maxLength": 120
    },
    "created_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "updated_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    }
  },
  "required": [
    "id",
    "product_id",
    "name",
    "created_at",
    "updated_at"
  ]
}

Response codes: 201, 401, 403, 404, 409, 419, 422, 429, 503

GET/devicesList own and delegated devices

Operation ID: listDevices

Partner token with read scope, or an authenticated active-company member session. Read-only delegation includes authorized owner fleets.

Authentication: PartnerToken or BrowserSession

page · query · optional

Successful response

{
  "allOf": [
    {
      "$ref": "#/components/schemas/PaginationMetadata"
    },
    {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/DeviceListItem"
          }
        }
      },
      "required": [
        "data"
      ]
    }
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/devicesEnroll individual device

Operation ID: enrollDevice

external_id is the own-account idempotency key. Same external_id/product_id/batch_id returns the existing row; conflicting product/batch returns 409. Generic Idempotency-Key header is not implemented. Satellite role additionally requires infrastructure_issuer approval. Partner token with enroll scope, or a company administrator session. Only the own account may provision devices; delegation is read-only.

Authentication: PartnerToken or BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "product_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "batch_id": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "external_id": {
      "type": "string",
      "maxLength": 120,
      "minLength": 1
    },
    "name": {
      "type": "string",
      "maxLength": 120,
      "minLength": 1
    },
    "role": {
      "type": "string",
      "enum": [
        "endpoint",
        "satellite"
      ],
      "default": "endpoint"
    }
  },
  "required": [
    "product_id",
    "external_id",
    "name"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "account_id": {
      "type": "string",
      "format": "uuid"
    },
    "product_id": {
      "type": "string",
      "format": "uuid"
    },
    "batch_id": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "infrastructure_device_id": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "external_id": {
      "type": "string",
      "maxLength": 120
    },
    "name": {
      "type": "string",
      "maxLength": 120
    },
    "role": {
      "type": "string",
      "enum": [
        "endpoint",
        "satellite"
      ]
    },
    "state": {
      "type": "string",
      "enum": [
        "active",
        "revoked"
      ]
    },
    "last_report_at": {
      "type": [
        "string",
        "null"
      ],
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "last_health": {
      "anyOf": [
        {
          "$ref": "#/components/schemas/HealthObservation"
        },
        {
          "type": "null"
        }
      ]
    },
    "created_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "updated_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    }
  },
  "required": [
    "id",
    "account_id",
    "product_id",
    "batch_id",
    "external_id",
    "name",
    "role",
    "created_at"
  ],
  "description": "Persisted device record. No report-token hash or private key is returned; device list endpoints use the documented reduced projection."
}

Response codes: 201, 401, 403, 404, 409, 419, 422, 429, 503

POST/devices/{device}/credentialsIssue or renew device credentials

Operation ID: issueDeviceCredentials

CSR CN must equal the device UUID. Never submit private keys. Reporting tokens are returned only by issuance/renewal and expire with their associated certificate; earlier valid tokens and certificates continue until expiry or revocation. Response private, no-store. Partner token with enroll scope, or own-company administrator session required.

Authentication: PartnerToken or BrowserSession + CsrfHeader

device · path · required

Request fields

{
  "type": "object",
  "properties": {
    "csr": {
      "type": "string",
      "maxLength": 16384,
      "description": "PEM PKCS#10 CSR. Common name must exactly match the device UUID. Generate and retain the private key locally; never submit it.",
      "minLength": 1
    }
  },
  "required": [
    "csr"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "certificate_pem": {
      "type": "string",
      "description": "PEM client certificate."
    },
    "chain_pem": {
      "type": "string",
      "description": "Issuing CA chain in PEM."
    },
    "ca_pem": {
      "type": "string",
      "description": "Trusted CA bundle in PEM."
    },
    "fingerprint": {
      "type": "string",
      "pattern": "^[a-fA-F0-9]{64}$"
    },
    "expires_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "device_id": {
      "type": "string",
      "format": "uuid"
    },
    "report_token": {
      "type": "string",
      "description": "Secret hsv_device_ reporting bearer token returned by issuance or renewal, never inventory reads. It expires with its associated certificate. Earlier reporting tokens remain usable while their own certificates and tokens are unexpired and unrevoked, the device is active, and its account is not suspended. Renewal does not immediately invalidate earlier tokens. Store securely; never log."
    },
    "profile": {
      "$ref": "#/components/schemas/NetworkProfile"
    }
  },
  "required": [
    "certificate_pem",
    "chain_pem",
    "ca_pem",
    "fingerprint",
    "expires_at",
    "device_id",
    "report_token",
    "profile"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 502, 503

POST/devices/{device}/revokeRevoke device credentials and site permissions

Operation ID: revokeDevice

Revokes all device certificates/reporting token and marks every permission revoked, enforcement pending. Gateway acknowledgement is separate. Partner token with write scope, or a company administrator session. Writes affect the own account; delegated access is read-only.

Authentication: PartnerToken or BrowserSession + CsrfHeader

device · path · required

Successful response

{
  "type": "object",
  "properties": {
    "state": {
      "type": "string",
      "const": "revoked"
    },
    "enforcement": {
      "type": "string",
      "const": "pending"
    }
  },
  "required": [
    "state",
    "enforcement"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 503

GET/requestsList own and delegated requests

Operation ID: listConnectionRequests

Includes computed freshness-aware status (reference freshness 90 seconds). Partner token with read scope, or an authenticated active-company member session. Read-only delegation includes authorized owner fleets.

Authentication: PartnerToken or BrowserSession

page · query · optional

Successful response

{
  "allOf": [
    {
      "$ref": "#/components/schemas/PaginationMetadata"
    },
    {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/ConnectionRequestView"
          }
        }
      },
      "required": [
        "data"
      ]
    }
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/requests/{connection}/decisionAllow, deny or revoke device at site

Operation ID: decideConnectionRequest

Active company must own the request location. Repeating same decision does not advance revision. Changed decisions mark enforcement pending and advance site revision. No ownership transfer. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

connection · path · required

Request fields

{
  "type": "object",
  "properties": {
    "decision": {
      "type": "string",
      "enum": [
        "allowed",
        "denied",
        "revoked"
      ]
    }
  },
  "required": [
    "decision"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "endpoint_id": {
      "type": "string",
      "format": "uuid"
    },
    "site_id": {
      "type": "string",
      "format": "uuid"
    },
    "decision": {
      "type": "string",
      "enum": [
        "pending",
        "allowed",
        "denied",
        "revoked"
      ]
    },
    "enforcement": {
      "type": "string",
      "enum": [
        "pending",
        "applied"
      ]
    },
    "decided_by": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "decided_at": {
      "type": [
        "string",
        "null"
      ],
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "last_attempt_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "last_connected_at": {
      "type": [
        "string",
        "null"
      ],
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "last_disconnected_at": {
      "type": [
        "string",
        "null"
      ],
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "health": {
      "anyOf": [
        {
          "$ref": "#/components/schemas/HealthObservation"
        },
        {
          "type": "null"
        }
      ]
    },
    "health_at": {
      "type": [
        "string",
        "null"
      ],
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "created_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "updated_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "reported_connection": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "connected",
        "disconnected",
        "not_reporting",
        null
      ],
      "description": "Last transition recorded for webhook purposes. Use computed status for current freshness."
    }
  },
  "required": [
    "id",
    "endpoint_id",
    "site_id",
    "decision",
    "enforcement",
    "last_attempt_at"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 503

GET/sitesList company locations and IoT state

Operation ID: listSites

Any active-company member can read; unconfigured locations have null IoT fields. Partner bearer tokens not accepted.

Authentication: BrowserSession

Successful response

{
  "type": "array",
  "items": {
    "$ref": "#/components/schemas/LocationSite"
  }
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

PUT/sites/{location}Configure local IoT policy

Operation ID: configureSite

Requires location ownership. Enabling requires global network flag and manual hardware qualification. sandbox_account_id counts toward its account site limit. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

location · path · required

Request fields

{
  "type": "object",
  "properties": {
    "enabled": {
      "type": "boolean"
    },
    "sandbox_account_id": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "down_mbps": {
      "type": "integer",
      "minimum": 1,
      "maximum": 2500
    },
    "up_mbps": {
      "type": "integer",
      "minimum": 1,
      "maximum": 2500
    }
  },
  "required": [
    "enabled",
    "down_mbps",
    "up_mbps"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "location_id": {
      "type": "string",
      "format": "uuid"
    },
    "sandbox_account_id": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid"
    },
    "enabled": {
      "type": "boolean"
    },
    "hardware_qualified": {
      "type": "boolean"
    },
    "down_mbps": {
      "type": "integer",
      "minimum": 1,
      "maximum": 2500
    },
    "up_mbps": {
      "type": "integer",
      "minimum": 1,
      "maximum": 2500
    },
    "revision": {
      "type": "integer",
      "minimum": 0
    },
    "created_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "updated_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "qualification_expires_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "A service-admin test window for the explicitly assigned sandbox, never proof of hardware qualification."
    }
  },
  "required": [
    "id",
    "location_id",
    "sandbox_account_id",
    "enabled",
    "hardware_qualified",
    "down_mbps",
    "up_mbps",
    "revision",
    "created_at",
    "updated_at"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 503

GET/sites/{location}/requestsList requests at owned location

Operation ID: listSiteRequests

Requires existing IoT site at owned location. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession

location · path · required

page · query · optional

Successful response

{
  "allOf": [
    {
      "$ref": "#/components/schemas/PaginationMetadata"
    },
    {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/ConnectionRequestView"
          }
        }
      },
      "required": [
        "data"
      ]
    }
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

GET/tokensList own token metadata

Operation ID: listPartnerTokens

Secrets/hashes not returned. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession

Successful response

{
  "type": "array",
  "items": {
    "$ref": "#/components/schemas/ApiToken"
  }
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/tokensCreate scoped expiring partner token

Operation ID: createPartnerToken

Scopes read/write/enroll are independent. Lifetime 1-90 days, default 30. Secret returned once; response private, no-store. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 120,
      "minLength": 1
    },
    "scopes": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "read",
          "write",
          "enroll"
        ]
      },
      "minItems": 1
    },
    "days": {
      "type": "integer",
      "minimum": 1,
      "maximum": 90,
      "default": 30
    }
  },
  "required": [
    "name",
    "scopes"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "token": {
      "type": "string",
      "description": "Secret hsv_iot_ partner bearer token, returned once. Store securely."
    },
    "expires_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    }
  },
  "required": [
    "id",
    "token",
    "expires_at"
  ]
}

Response codes: 201, 401, 403, 404, 409, 419, 422, 429, 503

DELETE/tokens/{token}Revoke own partner token

Operation ID: revokePartnerToken

Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

token · path · required

Response codes: 204, 401, 403, 404, 409, 419, 422, 429, 503

GET/delegationsList grants made by own account

Operation ID: listDelegations

Includes revoked grants. Partner token with read scope, or an authenticated active-company member session. Read-only delegation includes authorized owner fleets.

Authentication: PartnerToken or BrowserSession

Successful response

{
  "type": "array",
  "items": {
    "$ref": "#/components/schemas/Delegation"
  }
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/delegationsGrant or revoke provider read delegation

Operation ID: setDelegation

Recipient must be a different non-suspended account with provider capability. Repeating pair updates grant; revoke=true removes future delegated reads. No ownership transfer or local network permission. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "provider_account_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "revoke": {
      "type": "boolean",
      "default": false
    }
  },
  "required": [
    "provider_account_id"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "const": "saved"
    },
    "access": {
      "type": "string",
      "const": "read"
    }
  },
  "required": [
    "status",
    "access"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 503

GET/sessionsList own and delegated usage sessions

Operation ID: listSessions

Operational cumulative AAA counters, not invoice-grade billing or proof of Internet/application reachability. Partner token with read scope, or an authenticated active-company member session. Read-only delegation includes authorized owner fleets.

Authentication: PartnerToken or BrowserSession

page · query · optional

Successful response

{
  "allOf": [
    {
      "$ref": "#/components/schemas/PaginationMetadata"
    },
    {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Session"
          }
        }
      },
      "required": [
        "data"
      ]
    }
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/production-applicationSubmit production application

Operation ID: applyForProduction

Stores latest application and timestamp; sandbox remains available while reviewed. Does not automatically activate production or networking. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "platforms": {
      "type": "string",
      "maxLength": 2000,
      "minLength": 1
    },
    "expected_devices": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000000
    },
    "use_case": {
      "type": "string",
      "maxLength": 4000,
      "minLength": 1
    },
    "support_contact": {
      "type": "string",
      "format": "email",
      "maxLength": 255,
      "minLength": 1
    }
  },
  "required": [
    "platforms",
    "expected_devices",
    "use_case",
    "support_contact"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "const": "submitted"
    },
    "message": {
      "type": "string",
      "const": "Your sandbox remains available while production access is reviewed."
    }
  },
  "required": [
    "status",
    "message"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 503

GET/webhooksList own subscriptions

Operation ID: listWebhooks

Verification secrets not returned. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession

Successful response

{
  "type": "array",
  "items": {
    "$ref": "#/components/schemas/Webhook"
  }
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/webhooksCreate approved HTTPS subscription

Operation ID: createWebhook

Host must be operator-allowlisted and resolve only to public addresses, HTTPS port443. Secret returned once; private,no-store. All account audit events; no event-filter field. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

Request fields

{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "format": "uri",
      "maxLength": 2048,
      "description": "HTTPS URL on an operator-approved host, port 443, no userinfo or fragment. DNS must resolve only to public IPv4 addresses. Redirects are not followed.",
      "minLength": 1
    }
  },
  "required": [
    "url"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "secret": {
      "type": "string",
      "description": "Verification secret returned only once; keep on receiving backend."
    }
  },
  "required": [
    "id",
    "secret"
  ]
}

Response codes: 201, 401, 403, 404, 409, 419, 422, 429, 503

DELETE/webhooks/{webhook}Disable own subscription

Operation ID: disableWebhook

Sets enabled=false; retains record and history. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession + CsrfHeader

webhook · path · required

Response codes: 204, 401, 403, 404, 409, 419, 422, 429, 503

GET/webhook-deliveriesList own webhook delivery attempts

Operation ID: listWebhookDeliveries

At most eight attempts. Retry backoff 30 seconds up to one hour; actual timing depends on worker scheduling. Human company owner/admin or member with hotspotvu.iot.manage, using the active company session and CSRF protection. Partner tokens cannot perform this action.

Authentication: BrowserSession

page · query · optional

Successful response

{
  "allOf": [
    {
      "$ref": "#/components/schemas/PaginationMetadata"
    },
    {
      "type": "object",
      "properties": {
        "data": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/WebhookDelivery"
          }
        }
      },
      "required": [
        "data"
      ]
    }
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/infrastructure/authorizeInternal: authorize EAP-TLS identity

Operation ID: authorizeInfrastructureDevice

INTERNAL AAA integration, not a manufacturer API. Requires separate deployment-managed AAA bearer secret and both IoT feature flags enabled. Never embed this secret in endpoints, SDKs, browser code or public examples. Caller must first validate EAP-TLS and establish trusted gateway/NAS context; a fingerprint alone is not identity proof. Pending decisions return allow=false and no ordinary Internet access.

Authentication: AaaToken

Request fields

{
  "type": "object",
  "properties": {
    "gateway_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "fingerprint": {
      "type": "string",
      "pattern": "^[a-fA-F0-9]{64}$",
      "description": "SHA-256 client certificate fingerprint supplied only after AAA validates EAP-TLS.",
      "minLength": 1
    },
    "nas_id": {
      "type": "string",
      "format": "uuid",
      "description": "Mutual-TLS peer UUID overwritten by the trusted proxy. Satellite NAS permissions are rechecked by the backend."
    },
    "certificate_serial": {
      "type": "string",
      "pattern": "^[a-fA-F0-9:]{1,128}$",
      "description": "Validated TLS leaf serial from FreeRADIUS 3. Use with certificate_common_name and a dedicated CA enforcing unique serials."
    },
    "certificate_common_name": {
      "type": "string",
      "format": "uuid",
      "description": "CN of the validated TLS leaf; never outer User-Name."
    },
    "expected_role": {
      "type": "string",
      "enum": [
        "endpoint",
        "satellite"
      ],
      "default": "endpoint",
      "description": "SSID-specific role. Satellite NAS peers cannot admit chained backhauls."
    }
  },
  "required": [
    "gateway_id"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted.",
  "anyOf": [
    {
      "required": [
        "fingerprint"
      ]
    },
    {
      "required": [
        "certificate_serial",
        "certificate_common_name"
      ]
    }
  ]
}

Successful response

{
  "type": "object",
  "properties": {
    "allow": {
      "type": "boolean"
    },
    "reason": {
      "type": "string",
      "enum": [
        "site_disabled",
        "credential_unknown",
        "device_revoked",
        "sandbox_site_required",
        "infrastructure_not_approved",
        "pending",
        "allowed",
        "denied",
        "revoked",
        "network_role_mismatch",
        "qualification_site_required"
      ]
    },
    "device_id": {
      "type": "string",
      "format": "uuid"
    },
    "request_id": {
      "type": "string",
      "format": "uuid"
    },
    "role": {
      "type": "string",
      "enum": [
        "endpoint",
        "satellite"
      ]
    },
    "site_id": {
      "type": "string",
      "format": "uuid"
    },
    "revision": {
      "type": "integer",
      "minimum": 0
    },
    "session_seconds": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "allow",
    "reason"
  ],
  "description": "Early rejections have only allow/reason. Eligible identities also return request details; pending/denied/revoked remain allow=false."
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/infrastructure/accountingInternal: ingest AAA accounting

Operation ID: recordInfrastructureAccounting

INTERNAL AAA integration, not a manufacturer API. Requires separate deployment-managed AAA bearer secret and both IoT feature flags enabled. Never embed this secret in endpoints, SDKs, browser code or public examples. event_id is idempotent per gateway. Identical validated content succeeds; changed content with same ID returns 409. Cumulative counters never decrease. A new session requires an allowed request at gateway site. Accounting confirms association, not Internet/application reachability.

Authentication: AaaToken

Request fields

{
  "type": "object",
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "gateway_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "request_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "accounting_id": {
      "type": "string",
      "maxLength": 120,
      "minLength": 1
    },
    "mac": {
      "type": "string",
      "pattern": "^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$",
      "minLength": 1
    },
    "observed_at": {
      "type": "string",
      "format": "date-time",
      "description": "At most 30 seconds in the future.",
      "minLength": 1
    },
    "status": {
      "type": "string",
      "enum": [
        "start",
        "interim",
        "stop"
      ]
    },
    "bytes_up": {
      "type": "integer",
      "minimum": 0
    },
    "bytes_down": {
      "type": "integer",
      "minimum": 0
    },
    "nas_id": {
      "type": "string",
      "format": "uuid"
    },
    "bssid": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^(?:[a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$"
    }
  },
  "required": [
    "event_id",
    "gateway_id",
    "request_id",
    "accounting_id",
    "mac",
    "observed_at",
    "status",
    "bytes_up",
    "bytes_down"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid"
    },
    "committed": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "event_id",
    "committed"
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

GET/gateway/configInternal: retrieve site enforcement policy

Operation ID: getGatewayConfig

INTERNAL gateway management. X-Device-Id and X-API-Key must identify an active non-deleted gateway. These deployment secrets do not belong in manufacturer devices or browsers. Enabled only when global flag, site enablement and manual qualification all hold. No site returns minimal disabled configuration. An unqualified site can be enabled only in a bounded service-admin sandbox test window; permissions are limited to that assigned account and expire automatically.

Authentication: GatewayDeviceId + GatewayApiKey

Successful response

{
  "type": "object",
  "properties": {
    "schema": {
      "type": "string",
      "const": "hotspotvu.iot.network.v1"
    },
    "enabled": {
      "type": "boolean"
    },
    "site_id": {
      "type": "string",
      "format": "uuid"
    },
    "gateway_id": {
      "type": "string",
      "format": "uuid"
    },
    "revision": {
      "type": "integer",
      "minimum": 0
    },
    "down_mbps": {
      "type": "integer",
      "minimum": 1
    },
    "up_mbps": {
      "type": "integer",
      "minimum": 1
    },
    "eap_server_name": {
      "type": "string"
    },
    "authorizations": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/GatewayAuthorization"
      }
    }
  },
  "required": [
    "schema",
    "enabled",
    "revision",
    "authorizations"
  ],
  "description": "Unconfigured locations return schema, enabled=false, revision=0 and empty authorizations only."
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/gateway/acknowledgeInternal: acknowledge applied revision

Operation ID: acknowledgeGatewayConfig

INTERNAL gateway management. X-Device-Id and X-API-Key must identify an active non-deleted gateway. These deployment secrets do not belong in manufacturer devices or browsers. Acknowledge only after applying revision. Stale revision returns409; fetch again. Success marks site request enforcement applied.

Authentication: GatewayDeviceId + GatewayApiKey

Request fields

{
  "type": "object",
  "properties": {
    "revision": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "revision"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "applied_revision": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "applied_revision"
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

GET/device/configDevice runtime: retrieve allowed site references

Operation ID: getDeviceConfig

DEVICE RUNTIME, separate from partner automation. An unexpired, unrevoked hsv_device_ reporting token tied to an unexpired, unrevoked certificate is required for an active device in a non-suspended account. Overlapping valid tokens remain usable across renewal. Lists allowed requests at enabled qualified sites. Not first-connect credential bootstrap; does not grant Internet before approval. An unqualified site appears only for its explicitly assigned account during an active service-admin qualification window.

Authentication: DeviceReportToken

Successful response

{
  "type": "object",
  "properties": {
    "device_id": {
      "type": "string",
      "format": "uuid"
    },
    "role": {
      "type": "string",
      "enum": [
        "endpoint",
        "satellite"
      ]
    },
    "network_enabled": {
      "type": "boolean"
    },
    "sites": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/DeviceSite"
      }
    },
    "report_interval_seconds": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "device_id",
    "role",
    "network_enabled",
    "sites",
    "report_interval_seconds"
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/device/healthDevice runtime: report observed connectivity

Operation ID: reportDeviceHealth

DEVICE RUNTIME, separate from partner automation. An unexpired, unrevoked hsv_device_ reporting token tied to an unexpired, unrevoked certificate is required for an active device in a non-suspended account. Overlapping valid tokens remain usable across renewal. Request must belong to endpoint and remain allowed. event_id idempotent per device; conflicting content409. Over30seconds future422. Older health does not replace newer health.

Authentication: DeviceReportToken

Request fields

{
  "type": "object",
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "request_id": {
      "type": "string",
      "format": "uuid",
      "minLength": 1
    },
    "observed_at": {
      "type": "string",
      "format": "date-time",
      "description": "At most 30 seconds in the future.",
      "minLength": 1
    },
    "connected": {
      "type": "boolean"
    },
    "internet_reachable": {
      "type": [
        "boolean",
        "null"
      ]
    },
    "ap_active": {
      "type": [
        "boolean",
        "null"
      ]
    },
    "signal_dbm": {
      "type": [
        "integer",
        "null"
      ],
      "minimum": -120,
      "maximum": 0
    },
    "config_version": {
      "type": [
        "string",
        "null"
      ],
      "maxLength": 128
    },
    "guest_ap_bssid": {
      "type": [
        "string",
        "null"
      ],
      "description": "Managed satellites only: observed guest AP BSSID. Not returned in manufacturer health."
    },
    "associated_clients": {
      "type": "array",
      "maxItems": 128,
      "items": {
        "type": "string"
      },
      "description": "Managed satellites only; used for scoped guest attribution. Excluded from manufacturer health and webhook payloads."
    }
  },
  "required": [
    "event_id",
    "request_id",
    "observed_at",
    "connected"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid"
    },
    "committed": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "event_id",
    "committed"
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

GET/administrationInternal: inspect accounts and sites

Operation ID: listAdministration

INTERNAL service administration. Requires auth:sanctum and service-user permission hotspotvu.infrastructure.manage. Partner tokens and ordinary company-admin status do not satisfy this permission. Accounts paginated50; sites at most100 most recent rows.

Authentication: ServiceSession

page · query · optional

Successful response

{
  "type": "object",
  "properties": {
    "accounts": {
      "$ref": "#/components/schemas/AdministrationAccountsPage"
    },
    "sites": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/AdministrationSite"
      },
      "maxItems": 100
    }
  },
  "required": [
    "accounts",
    "sites"
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503

POST/administration/accounts/{account}/reviewInternal: review account access and limits

Operation ID: reviewAccount

INTERNAL service administration. Requires auth:sanctum and service-user permission hotspotvu.infrastructure.manage. Partner tokens and ordinary company-admin status do not satisfy this permission. Suspension revokes partner tokens and site permissions with enforcement pending. Records reviewer and audit. Does not bypass local permission or enable global flags.

Authentication: ServiceSession + CsrfHeader

account · path · required

Request fields

{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "sandbox",
        "production",
        "suspended"
      ]
    },
    "device_limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000000
    },
    "site_limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100000
    },
    "infrastructure_issuer": {
      "type": "boolean"
    },
    "review_notes": {
      "type": "string",
      "minLength": 10,
      "maxLength": 4000
    }
  },
  "required": [
    "status",
    "device_limit",
    "site_limit",
    "infrastructure_issuer",
    "review_notes"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "sandbox",
        "production",
        "suspended"
      ]
    }
  },
  "required": [
    "status"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 503

POST/administration/sites/{site}/qualificationInternal: record manual qualification

Operation ID: recordSiteQualification

INTERNAL service administration. Requires auth:sanctum and service-user permission hotspotvu.infrastructure.manage. Partner tokens and ordinary company-admin status do not satisfy this permission. Records manual pass/fail with evidence reference and increments revision. Passing does not itself enable site or global network.

Authentication: ServiceSession + CsrfHeader

site · path · required

Request fields

{
  "type": "object",
  "properties": {
    "passed": {
      "type": "boolean"
    },
    "evidence_reference": {
      "type": "string",
      "minLength": 10,
      "maxLength": 1000,
      "description": "Reference to manual validation evidence; never submit secrets."
    }
  },
  "required": [
    "passed",
    "evidence_reference"
  ],
  "additionalProperties": true,
  "description": " Unknown request fields are ignored by validation, not persisted."
}

Successful response

{
  "type": "object",
  "properties": {
    "hardware_qualified": {
      "type": "boolean"
    },
    "qualification_expires_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "A service-admin test window for the explicitly assigned sandbox, never proof of hardware qualification."
    }
  },
  "required": [
    "hardware_qualified"
  ]
}

Response codes: 200, 401, 403, 404, 409, 419, 422, 429, 503

POST/device/renewDevice runtime: renew credentials with private-key proof

Operation ID: renewDeviceCredentials

Use a valid certificate-bound reporting bearer token. For a fresh request, timestamp must be within 300 seconds of server time. Compute lowercase hex SHA-256 of the exact UTF-8 csr string, then sign event_id + "\n" + device_id + "\n" + decimal timestamp + "\n" + csr_sha256 with actual LF (U+000A) separators and no trailing LF, using SHA-256 and the private key of the certificate associated with that reporting token. Send the signature as standard base64. The enrolled device UUID is the authenticated device identity. Never transmit its private key. A successful response has the same bundle as partner issuance. Previous unexpired/unrevoked certificates and reporting tokens remain valid; install the new bundle safely. Repeating the identical event_id and payload while authenticating with a valid token returns the originally issued bundle, including its reporting token, without issuing again. The response is stored encrypted at rest. Reusing an event_id with different content returns 409. Exact replays use the saved receipt before checking original timestamp age; new events require fresh timestamps. The reference daemon attempts renewal seven days before expiry. Expired credentials require authorized recovery rather than bypassing authentication.

Authentication: DeviceReportToken

Request fields

{
  "type": "object",
  "properties": {
    "event_id": {
      "type": "string",
      "format": "uuid",
      "description": "Idempotency identifier for this device renewal."
    },
    "timestamp": {
      "type": "integer",
      "description": "Unix seconds. A fresh renewal must be within 300 seconds of server time."
    },
    "csr": {
      "type": "string",
      "minLength": 1,
      "maxLength": 16384,
      "description": "PEM CSR with common name equal to the enrolled device UUID. Hash the exact submitted UTF-8 string; never upload its private key."
    },
    "signature": {
      "type": "string",
      "minLength": 1,
      "maxLength": 4096,
      "description": "Standard base64 encoding of the SHA-256 signature made with the private key of the certificate associated with the supplied reporting token. Sign the exact proof described for this operation."
    }
  },
  "required": [
    "event_id",
    "timestamp",
    "csr",
    "signature"
  ],
  "additionalProperties": true
}

Successful response

{
  "type": "object",
  "properties": {
    "certificate_pem": {
      "type": "string",
      "description": "PEM client certificate."
    },
    "chain_pem": {
      "type": "string",
      "description": "Issuing CA chain in PEM."
    },
    "ca_pem": {
      "type": "string",
      "description": "Trusted CA bundle in PEM."
    },
    "fingerprint": {
      "type": "string",
      "pattern": "^[a-fA-F0-9]{64}$"
    },
    "expires_at": {
      "type": "string",
      "description": "UTC database timestamp or ISO-8601 serialized timestamp."
    },
    "device_id": {
      "type": "string",
      "format": "uuid"
    },
    "report_token": {
      "type": "string",
      "description": "Secret hsv_device_ reporting bearer token returned by issuance or renewal, never inventory reads. It expires with its associated certificate. Earlier reporting tokens remain usable while their own certificates and tokens are unexpired and unrevoked, the device is active, and its account is not suspended. Renewal does not immediately invalidate earlier tokens. Store securely; never log."
    },
    "profile": {
      "$ref": "#/components/schemas/NetworkProfile"
    }
  },
  "required": [
    "certificate_pem",
    "chain_pem",
    "ca_pem",
    "fingerprint",
    "expires_at",
    "device_id",
    "report_token",
    "profile"
  ]
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 502, 503

GET/infrastructure/nas-mapInternal AAA: active mutually authenticated NAS registry

Operation ID: getInfrastructureNasMap

AAA service only; both feature flags required. Includes qualified active gateways and explicitly approved infrastructure satellites. Used by the operator-installed 30-second NAS reconciler; contains no private keys or partner tokens.

Authentication: AaaToken

Successful response

{
  "type": "object",
  "required": [
    "nas_map",
    "revision"
  ],
  "properties": {
    "nas_map": {
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "format": "uuid"
      }
    },
    "revision": {
      "type": "string",
      "description": "SHA256 of the sorted map"
    }
  }
}

Response codes: 200, 403, 429, 503

POST/administration/sites/{site}/qualification-windowInternal: open or close bounded sandbox hardware test window

Operation ID: setQualificationWindow

Service infrastructure permission required. Only an unqualified site explicitly bound to a sandbox account can receive a new window. 1-8 hours; zero closes the window and disables the site. Never records hardware qualification. Global enablement, site enablement and factory firmware preflight remain required. Only the assigned account may authenticate at an unqualified test site.

Authentication: ServiceSession + CsrfHeader

site · path · required

Request fields

{
  "type": "object",
  "required": [
    "hours",
    "evidence_reference"
  ],
  "properties": {
    "hours": {
      "type": "integer",
      "minimum": 0,
      "maximum": 8
    },
    "evidence_reference": {
      "type": "string",
      "minLength": 10,
      "maxLength": 1000
    }
  }
}

Successful response

{
  "type": "object",
  "required": [
    "qualification_expires_at",
    "hardware_qualified"
  ],
  "properties": {
    "qualification_expires_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "hardware_qualified": {
      "type": "boolean"
    }
  }
}

Response codes: 200, 401, 403, 404, 409, 422, 429, 503