Helium MVNEdocs

Webhooks

Webhooks are Helium MVNE's outbound async channel. Register an HTTPS endpoint in the dashboard, tell us which events you care about, and we'll POST each event to you. Deliveries are signed, retried, and replayable.

Registration

Webhook endpoints are registered from the operator dashboard, not via the API. Operator API keys can't mint or delete endpoints — that's a deliberate security boundary so a compromised integration key can't redirect your event stream.

In the dashboard:

  1. Open Settings → Webhook endpoints in the operator workspace.
  2. Click Add endpoint, paste your receive URL, and select the events you want (subscription.*, sim.*, etc.).
  3. Save. The dashboard shows the signing secret (whsec_...) once — copy it into your backend's secret store.

Endpoints are scoped to the dashboard's currently-selected environment. Your sandbox endpoint and live endpoint are separate records with separate signing secrets.

Signature verification

Every delivery carries Platform-Signature: t=<ts>,v1=<hex>. The signed payload is <ts>.<raw-body>, HMAC-SHA256, keyed on the signing secret. Reject requests where:

  • The timestamp is older than 5 minutes (replay protection).
  • The computed signature doesn't match v1.
  • The event's livemode doesn't match the endpoint's mode.
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
  header.split(",").map((p) => p.split("=")),
);
const t = Number(parts.t);
if (Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac("sha256", secret)
  .update(`${t}.${rawBody}`)
  .digest("hex");
return timingSafeEqual(
  Buffer.from(expected, "hex"),
  Buffer.from(parts.v1, "hex"),
);
}

Retries & dead-letter

Failed deliveries retry with exponential backoff (30s, 2m, 10m, 30m, 1h, 2h, 6h, 12h, 24h — 9 total attempts over ~48h). After the ninth, the event lands in the dead-letter queue, viewable in the dashboard's event stream and replayable with one click — there is no operator-key API for replay, replay is a dashboard-only action.

Ordering

Delivery is not ordered. If two events land milliseconds apart, they may be delivered out of order. Keep your handlers idempotent and resolve state from the event payload rather than assumed sequence.