Documentation

Helix developer docs

REST endpoints, webhook signing, deployment checklists, and platform guarantees.

Webhooks

Helix delivers risk decisions to your systems by HTTP POST. Every delivery is signed with HMAC-SHA256 so you can prove the request came from us.

Signature format

Header: x-helix-signature: t=<unix-seconds>,v1=<hex>

Where v1 = HMAC_SHA256(secret, "<t>.<raw-body>").

Verifying in your handler (Node)

import { createHmac, timingSafeEqual } from "crypto";

const TOLERANCE = 300; // seconds

function verify(rawBody: string, header: string | null, secret: string) {
  if (!header) return false;
  const parts = Object.fromEntries(
    header.split(",").map((p) => p.split("=")),
  );
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > TOLERANCE) return false;
  const expected = createHmac("sha256", secret)
    .update(`${t}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(parts.v1 ?? "", "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}

Rules

  • Use the raw request body string. JSON-parse only after verifying.
  • Reject anything older than 5 minutes (replay protection).
  • Use timingSafeEqual — never ===.
  • The secret is shown ONCE when the webhook is created. If you lose it, rotate the webhook to mint a new one.
  • We retry failed deliveries (2xx = success) up to 5 times with exponential backoff. Idempotency: dedupe on event.id.

Testing

The control plane has a Send test event button on each webhook. It POSTs a signed payload of {"type":"helix.test", ...} and records the result in the audit log.

Delivery history

Workspace owners can review every signed delivery (status code, payload hash, attempt number) under Webhooks → Delivery log. The payload hash is a SHA-256 of the body — useful for proving to a bank's audit team that a specific event was sent without storing the body itself.