Webhooks

Your backend receives a signed event for every scan — the verified record of what happened, independent of anything the device says.

Setup

Set the webhook URL on your app in the portal (https only). Saving it issues your signing secret whsec_… — shown once. Rotating issues a new one and invalidates the old.

Events

EventWhenCarries
scan.completedA member finished a scanscan_id, host_user_ref, model version, status. Never a measurement.
scan.failedA scan ended in failureSame shape, failed status.
scan.results_availableOnly with results delivery enabledThe member's result in data.result. See Results delivery.

Verifying a delivery

Every delivery is signed. Compute HMAC-SHA256 of the raw request body with your whsec_… secret and compare it to the header — before parsing the JSON.

HeaderValue
Visualize-SignatureHex HMAC-SHA256 of the raw body, keyed with your secret
Visualize-Event-IdStable per scan, across retries — dedupe on it
Visualize-Event-TypeSame as type in the body
Node
import crypto from "node:crypto";

app.post("/hooks/visualize", express.raw({ type: "*/*" }), (req, res) => {
  const expected = crypto
    .createHmac("sha256", process.env.VISUALIZE_WEBHOOK_SECRET)
    .update(req.body)                 // raw bytes, before any parsing
    .digest("hex");

  const given = req.get("Visualize-Signature") ?? "";
  const ok = given.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
  if (!ok) return res.status(400).end();

  const event = JSON.parse(req.body);
  // handle event.type, dedupe on Visualize-Event-Id
  res.status(200).end();
});

Delivery semantics

At-least-once. Any 2xx acknowledges; anything else retries with exponential backoff from 30 seconds, capped at 6 hours, for 17 attempts (about 50 hours). Redirects are not followed. Because retries can duplicate, dedupe on Visualize-Event-Id.

scan.completed is deliberately measurement-free: it is the billing-grade record that a scan happened. Measurements reach your app from the SDK on the device — or server-side via the separate, consent-gated results delivery.