⌘K

Partner APIReference

Webhooks

Registration, signature verification, delivery and retry semantics.

On this page

Registration

The laboratory registers your HTTPS endpoint in Integrations → Webhooks, selects the event types to deliver, and the subscription is bound to one API client and one environment. A subscription never spans environments or clients.

  • Laboratory administrators manage subscriptions in the Control Plane; Health Company administrators and developers can manage subscriptions for their own client and environment in the Health Company workspace.
  • A webhook can only be configured once an eligible integration and an active key exist in the same environment; until then the Control Plane shows that prerequisite instead of an endpoint form.
  • Delivery logs, the event catalog and activity default to the currently selected environment, so a Production view never mixes in Sandbox deliveries.
  • A subscription signing secret is revealed once at creation or rotation. Store it securely; it cannot be retrieved later.
  • Rotation immediately changes the signing generation and reveals the replacement once. Coordinate receiver deployment around rotation: there is no overlap period, and the replacement is not available beforehand. Reconcile notification gaps through the event stream.

Delivery headers

  • x-solia-signature — v1=<hex HMAC-SHA256>.
  • x-solia-timestamp — Unix seconds used in the signed material.
  • x-solia-event-id — the event identifier to deduplicate on.
  • x-solia-delivery-id — this delivery attempt's identifier.
  • x-solia-api-version — the API version of the payload.

Signature verification

The signed material is exactly <x-solia-timestamp>.<raw request body>. Compute HMAC-SHA256 with your signing secret over that string, compare in constant time against the presented signature, and reject a timestamp outside a 300-second tolerance. Verify before parsing the body.

javascript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifySolia(rawBody, headers, secret) {
  if (!Buffer.isBuffer(rawBody) || !headers || typeof headers !== "object") return false;
  if (typeof secret !== "string" || !secret) return false;
  const timestamp = headers["x-solia-timestamp"];
  const presented = headers["x-solia-signature"];
  const deliveryId = headers["x-solia-delivery-id"];
  const eventId = headers["x-solia-event-id"];
  if (typeof timestamp !== "string" || !/^\d+$/.test(timestamp)) return false;
  if (typeof presented !== "string" || !/^v1=[0-9a-f]{64}$/.test(presented)) return false;
  if (typeof deliveryId !== "string" || !deliveryId.trim()) return false;
  if (typeof eventId !== "string" || !eventId.trim()) return false;
  const seconds = Number(timestamp);
  if (!Number.isSafeInteger(seconds) || Math.abs(Date.now() / 1000 - seconds) > 300) return false;
  const expected = createHmac("sha256", secret).update(timestamp + ".").update(rawBody).digest();
  const actual = Buffer.from(presented.slice(3), "hex");
  return actual.length === expected.length && timingSafeEqual(expected, actual);
}

Important

A delivery identifier that has already been processed must be ignored. Replay protection is your responsibility as the subscriber.

Test delivery versus normal delivery

Send test event is an immediate configuration check from the Control Plane. Normal operational events are persisted to the canonical event stream and delivery outbox, then claimed by the scheduled delivery process. A successful test proves endpoint reachability and signing configuration; it does not prove that a later operational event traversed the scheduled path.

Delivery and retries

  • Each attempt uses a 10-second request timeout; respond 2xx quickly and process asynchronously.
  • A failed attempt is retried on a bounded schedule of 1m, 5m, 30m, 2h, 12h, 24h, for at most 7 attempts in total.
  • A Retry-After response header is honoured up to 60 minutes.
  • Timeouts, network failures, 408, 409, 425, 429 and 5xx responses are temporary. Other 4xx responses are permanent failures; blocked destinations are never retried.
  • Once attempts are exhausted the delivery is dead-lettered and stays visible. Supported replay creates an auditable new attempt rather than rewriting history.
  • The event itself remains readable through the reconciliation stream.
  • Delivery is at-least-once: the same event may arrive more than once.
  • Events may arrive out of order; use sequence from GET /v1/events to order them.
  • Subscribers must be idempotent on event_id and ignore a delivery id already seen.
  • Verify the HMAC-SHA256 signature over <timestamp>.<raw body> before parsing.
  • Reject a delivery whose timestamp is outside a 5 minute tolerance.
  • The Partner API is authoritative: reconcile with GET /v1/events after any gap.

Endpoint safety

  • Endpoints must use public HTTPS and cannot contain user information in the URL.
  • Localhost, private, link-local, reserved and documentation address ranges are blocked.
  • DNS is resolved again at delivery time; any non-public destination is refused, and redirects are not followed.

Payload boundary

A delivery body is the same event object returned by GET /v1/events. Only an allow-listed set of identifier and coarse-state fields can appear in data: order_id, external_order_id, program_id, client_id, environment, lifecycle, next_action, specimen_state, result_id, result_version, result_provenance, result_is_critical, action_required_code, links.

Important

Analyte values, demographics, report documents, headers and secrets are never delivered in an event, and no permanent or signed document URL is ever included.

Replay and current Program scope

Notifications do not replace canonical result read-back. Current client/Program/environment scope remains required for queued and historical access; verify signatures and deduplicate replays using the existing contract. Do not confuse test-only callback replay custody with deployed canonical dedupe.

Reference laboratory engineering boundary

Reference/send-out is source-only and unavailable in Sandbox or Production. Existing integration, buyer, publication and clinical authorities remain unchanged; a directory listing or relationship never authorizes a referral.

Related resources