Hylabs LogoOrder

Developers

Show a client's blood test progress inside your coaching app: ordered, results back, report sent, next test due. Milestones only, never a result, and only for clients who said yes. A small REST API, signed webhooks, a sandbox and an OpenAPI file. Most teams are done in a day.

How it fits

Three ways in

  1. A link. Your coaches add their HYLABS partner link to the client's resources. Put your own client id on it as ?ext= and every order that follows is matched to that client for you. Nothing to build.
  2. Webhooks. Register an endpoint and we post an event at each milestone. Wire it into your automations or your CRM.
  3. A tile. Call GET /v1/clients and draw the status in your client card. The whole response is below.

Consent

The client decides

Nothing is shared by default. At checkout through a partner's link the client sees one unticked box: "Let {coach} see when my bloods are ordered, collected and back." They can turn it on or off at any time in their HYLABS portal under Account. We log the wording and the time of every change.

When a client stops sharing, your API view drops them within fifteen minutes and you receive a sharing.revoked event. Your agreement with HYLABS requires you to delete what you hold about that client within seven days. Attribution alone never grants access: a coach whose link was used sees nothing until the client says yes.

What you get

Fields

FieldExampleMeaning
client_refcl_3f9a...Opaque id, stable per client per partner. Not reversible.
external_idkah_88213Your own client id, when your link carried ?ext=.
order_refHY-TEST0001The HYLABS order id.
panelPhysique StandardThe panel ordered.
tierresults | reportResults only, or results with the doctor's written report and call.
statusordered | form_expired | in_review | results_back | report_sent | call_booked | call_held | retest_due | refundedWhere the order is up to. One value, highest precedence wins.
ordered_at, form_expires_atISO 8601When the order was placed and when the request form lapses.
in_review_at, results_at, report_atISO 8601 or nullEach set when it happens.
call_booked_forISO 8601 or nullThe doctor call, report tier only.
next_due_onYYYY-MM-DD or nullSuggested next test, 90 days after results or report.
order_urlhttps://hylabs.com.au/checkout?...Your link for the client's next order, with ext carried through.
{
  "client_ref": "cl_3f9a1c0b7d2e4f6a8b9c0d1e",
  "external_id": "kah_88213",
  "sharing": { "status": "on", "since": "2026-11-04T09:12:00+11:00" },
  "updated_at": "2026-11-12T16:40:12+11:00",
  "latest": {
    "order_ref": "HY-TEST0002",
    "panel": "Physique Standard",
    "tier": "report",
    "status": "report_sent",
    "ordered_at": "2026-11-04T09:11:40+11:00",
    "form_expires_at": "2026-12-04T09:11:40+11:00",
    "in_review_at": "2026-11-10T08:02:00+11:00",
    "results_at": "2026-11-12T16:40:12+11:00",
    "report_at": "2026-11-12T16:40:12+11:00",
    "call_booked_for": null,
    "next_due_on": "2027-02-10",
    "order_url": "https://hylabs.com.au/checkout?panel=standard&ref=SAMFIT&ext=kah_88213"
  },
  "orders": [ "..." ]
}

What you never get

Not in any response

  • markers, values, flags, scores or reference ranges
  • the report PDF or the print copy
  • what the client takes (medicines, supplements, anything recorded)
  • weight, waist or body measurements
  • the doctor's notes or anything said on a call
  • phone, email, address, date of birth or sex
  • marketing opt-out status or payment details
  • any order of the same client that was not attributed to you

The status names are deliberately plain (results_back, not a clinical word). Nothing in this API is clinical information, and nothing in it may be presented to a client as advice.

Access

Authentication

Base URL https://api.hylabs.com.au/v1. Every request carries Authorization: Bearer hk_live_.... Keys are issued by HYLABS per partner, shown once and stored hashed. A hk_test_ key answers every route from a fixed sandbox data set (two clients, nine orders, one in every status, twenty events) and still delivers real webhooks to your endpoint, so you can build the whole integration before a single real client shares anything. Ten requests a second; responses are never cached.

Errors are { "error": { "code": "...", "message": "..." } } with 400, 401, 403 (partner not active), 404 or 429.

Routes

Endpoints

GET/v1/meYour partner record: code, links, service area, webhook.
GET/v1/clients?updated_since=&limit=&cursor=Every consented client with their latest order and history.
GET/v1/clients/{client_ref}One client.
GET/v1/clients/by-external/{external_id}One client by your own id. 404 when unknown or not shared.
GET/v1/events?since=&limit=The same events the webhook sends, for polling.
GET/v1/webhookYour webhook endpoint (the secret is shown once, at creation).
PUT/v1/webhookRegister or replace the endpoint URL. Returns the signing secret this one time.
DELETE/v1/webhookRemove the endpoint.
POST/v1/webhook/testSend a ping event to your endpoint.
GET/v1/webhook/deliveries?limit=Recent delivery attempts and their outcomes.
GET/v1/openapi.jsonThe OpenAPI 3.1 document. No auth.

Lists page with cursor; updated_since keeps a poll cheap. The view behind the API is refreshed every fifteen minutes, which is instant for bloodwork.

openapi.json describes every route and schema.

Webhooks

Events

order.createdorder.form_expiredorder.in_reviewresults.readyreport.sentcall.bookedcall.heldretest.dueorder.refundedsharing.grantedsharing.revokedping

Each event is a JSON body { id, type, created_at, data } posted to your URL with the headers HYLABS-Event, HYLABS-Delivery and HYLABS-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of `${t}.${rawBody}` with your signing secret (header names arrive in whatever case the transport gives them; read them case-insensitively). Any 2xx within ten seconds counts as delivered; otherwise we retry every fifteen minutes for 24 hours. Verify with a constant-time compare and reject a timestamp more than five minutes old.

Node

import crypto from 'node:crypto';

export function verify(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map(kv => kv.split('=')),
  );
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex');
  const given = Buffer.from(parts.v1 || '', 'hex');
  const want = Buffer.from(expected, 'hex');
  return given.length === want.length && crypto.timingSafeEqual(given, want);
}

Python

import hmac, hashlib, time

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    parts = dict(kv.split("=", 1) for kv in signature_header.split(","))
    t = int(parts.get("t", "0") or 0)
    if not t or abs(time.time() - t) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Terms

Backed by HYLABS

The partnership is mutual and there is no fee either way: your coaches and their clients get doctor-backed private pathology with no referral, results in days and a written report, and your app gets a bloods status it did not have to build. While your integration is active you may describe it as "Bloods by HYLABS" or "Backed by HYLABS". You may not present HYLABS results, reports or this API as programming, treatment or an endorsement of any substance, and you may not make outcome claims using our name. Data stays in Australia on our side; the agreement covers purpose limitation, deletion on revoke, and breach notice. HYLABS collects at 1,000+ centres across NSW, VIC, QLD and ACT.

To get a sandbox key or talk through an integration, use the contact form and mention your platform.