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

Implementation candidate · managed-site validation required

React to permission and connection changes.

Use signed events when a device is enrolled, requests access, receives a decision, has credentials issued or revoked, or changes its observed connection state. Query request status to reconcile current freshness.

Integration at a glance

Verify the raw bytes

Check the timestamp and HMAC before parsing the body.

Accept duplicates safely

Persist event IDs and acknowledge only after durable acceptance.

Reconcile current state

Events notify your system; the authorized API provides current status.

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

Create an approved subscription

A human company administrator creates the webhook subscription. The destination must use HTTPS on port 443, resolve only to public addresses, and have an operator-approved host. Keep the once-returned verification secret in your backend’s protected configuration.

A subscription receives available audit and connection-transition events for its account. Event filtering is not implemented. Event names and payloads are defined in OpenAPI contract.

Connection transitions and stale reporting

device.connection_changed carries connected or disconnected when an accepted device health observation changes that state. The presence expiry worker emits not_reporting after observations become stale. The reference freshness interval is 90 seconds; minute-based scheduling and webhook delivery can delay the notification.

Connection events include the enrolled device UUID, an opaque request UUID, and the connection state. Reported transitions can also include internet_reachable; stale-report events omit it. Payloads do not expose the site or location identity.

This reports observed connectivity, not manufacturer application health. An endpoint that has never reported does not generate a stale-transition event merely by existing.

Verify before processing

X-HotSpotVU-Signature contains t=<Unix-seconds>,v1=<hex-HMAC-SHA256>. Verify HMAC-SHA256 over the timestamp, a period, and the exact raw UTF-8 body with your subscription secret. Apply a bounded timestamp tolerance in your receiver; do not parse or rewrite the body before verification.

The Node helper below uses a five-minute tolerance and a constant-time digest comparison. Integrate it into a receiver with raw-body capture and a request-size limit, before JSON parsing. Reject failed verification; never log the secret.

Record processed event identifiers, acknowledge with a 2xx only after durable acceptance, and perform slower work asynchronously. Deliveries can repeat. The worker tries up to eight times with backoff from 30 seconds to one hour, subject to its scheduling.

verify-webhook.mjs · Node backend verification helper javascript
import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyWebhook(rawBody, signature, secret, now = Math.floor(Date.now() / 1000)) {
  if (!Buffer.isBuffer(rawBody) || typeof signature !== 'string' || !secret) {
    throw new Error('Missing webhook verification inputs')
  }
  const fields = signature.split(',')
  if (fields.length !== 2) throw new Error('Malformed signature')
  const timestamp = fields[0].match(/^t=(\d+)$/)?.[1]
  const hex = fields[1].match(/^v1=([a-f0-9]{64})$/i)?.[1]
  if (!timestamp || !hex || !Number.isSafeInteger(Number(timestamp)) ||
      Math.abs(now - Number(timestamp)) > 300) throw new Error('Invalid or stale signature')
  const expected = createHmac('sha256', secret)
    .update(timestamp + '.').update(rawBody).digest()
  if (!timingSafeEqual(expected, Buffer.from(hex, 'hex'))) throw new Error('Invalid signature')
  return JSON.parse(rawBody.toString('utf8'))
}
// Call before any JSON middleware rewrites the raw request bytes:
// const event = verifyWebhook(rawBody, signatureHeader, process.env.HOTSPOTVU_WEBHOOK_SECRET)
// Persist/deduplicate event.id durably, enqueue work, then respond with 2xx.
// Timestamp validation alone does not prevent duplicate delivery.

Reconcile current state

Treat webhooks as notifications rather than a complete database replica. Query the authorized API resource when you need the latest state, especially after retries or a receiver outage.

An approval event is not a connection event. A recent connection event is not proof that telemetry remains fresh indefinitely. Preserve observation timestamps in your own UI.

Support and recovery

Inspect delivery status and the documented retry outcome in the partner dashboard. Test secret rotation and receiver unavailability in the sandbox.

Do not include private keys, bearer credentials, or unrelated venue information in webhook payloads or support tickets.