Developers / API
The fulfillment rail, as an API.
neolife is infrastructure, not an app you log into. A REST API with a generated OpenAPI spec, typed SDK, a CLI, signed webhooks, and an MCP server — the same rail our own product runs on, open to yours. Build the whole integration against synthetic patients before a BAA is ever signed.
OpenAPI 3.1
Generated spec at /openapi.json
Sandbox
Synthetic patients only — no BAA needed to start
PHI-free
Webhooks carry ids and status, never patient data
The primitives
Everything you need to build against, and nothing you don't.
One rail, exposed six ways. Pick the surface that fits your stack — they all hit the same order pipeline, with the same rules behind them.
API keys
Sandbox and live, cleanly split
Live keys move real orders. Sandbox keys resolve only to synthetic patients, so you can build and break the whole integration before a BAA is signed. Restricted keys scope down to exactly the permissions an integration needs.
nk_live_… · nk_sandbox_… · rk_…
Node SDK
@neolifehealth/sdk — typed, zero-dependency
A typed client for orders, catalog, intake, keys, and webhook endpoints, plus verifyWebhook for signature checking. Native fetch, Node 18+, no runtime dependencies. Every mutation takes an idempotency key.
pnpm add @neolifehealth/sdk
CLI
neolife — the terminal front door
Log in, mint keys, list orders, browse the catalog, and draft orders from your shell. The headline command, trigger, fires a correctly signed test event straight at a local receiver — no relay service to stand up.
neolife trigger order.shipped
Webhooks
Signed, retried, and PHI-free by contract
Standard-Webhooks HMAC-SHA256 signatures, automatic retry with backoff, and a ±5-minute replay window. Payloads carry ids and status — never patient data — so you can point our webhooks anywhere.
webhook-signature: v1,<hmac>
Idempotency
Retries that can't double-ship
Every mutation accepts an Idempotency-Key. The API replays the first response for a repeated key and returns 409 on the same key with a different body. A double-submit is a second physical shipment — this is what stops it.
Idempotency-Key: <uuid>
MCP server
Agent-native access to the same rail
The same order pipeline, exposed over the Model Context Protocol so your agents can draft and track orders directly. A licensed provider still approves every order. Documented in full on the MCP docs page.
POST /mcp/tools/call
Start with a key
One Bearer header, and you're drafting orders.
Authenticate with a machine key and the prefix decides the world you're in. A nk_sandbox_ key resolves only to synthetic patients, so this exact call is safe to run on day zero — nothing touches a real person until you swap in a nk_live_ key under a BAA.
The Idempotency-Key makes the request safe to retry — the API replays the first response instead of creating a duplicate.
curl
# Sandbox keys resolve ONLY to synthetic patients — no BAA needed to start.
curl https://api.neolife.health/v1/orders \
-H "Authorization: Bearer nk_sandbox_7f21a9c4e0b8…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"clinicId": "cln_8f21",
"patientRef": "pt_4b9c",
"items": [{ "productId": "prod_tdf30", "quantity": 1, "sig": "Apply nightly" }],
"shipping": { "method": "standard", "to": "addr_77a2" }
}'
# → 201 Created { "id": "ord_19d4", "status": "draft", "approvalRequired": true }The Node SDK
Create, approve, submit — idempotent, and webhook-verified.
@neolifehealth/sdk is a typed client with no runtime dependencies. It creates a draft, routes it for provider approval, and submits it with an idempotency key so a retried call can never double-ship. verifyWebhook does the constant-time signature check on the way back.
@neolifehealth/sdk
import { Neolife, verifyWebhook, NeolifeError } from "@neolifehealth/sdk";
import crypto from "node:crypto";
// A nk_sandbox_ key => client.mode === "sandbox" (synthetic patients only).
const client = new Neolife({ apiKey: process.env.NEOLIFE_API_KEY! });
// Create → approve (provider-gated) → submit. The Idempotency-Key makes the
// retry safe: a double-submit would be a second physical shipment.
const draft = await client.orders.create({
clinicId: "cln_8f21",
patientRef: "pt_4b9c",
items: [{ productId: "prod_tdf30", quantity: 1, sig: "Apply nightly" }],
shipping: { method: "standard", to: "addr_77a2" },
});
await client.orders.approve(draft.id); // a licensed provider signs
await client.orders.submit(draft.id, { // idempotent by key
idempotencyKey: crypto.randomUUID(),
});
// Verify an incoming webhook — pass the RAW body, before JSON.parse.
app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => {
try {
const event = verifyWebhook(req.body, req.headers, process.env.NEOLIFE_WEBHOOK_SECRET!);
console.log(event.type, event.data); // PHI-free: ids + status only
res.sendStatus(204);
} catch (err) {
res.sendStatus(400); // bad signature => reject
}
});The CLI
Test a signed webhook without standing up a relay.
The neolife CLI is the human-in-terminal front door. It defaults to sandbox and prints a persistent mode badge on every command, so you never fire at live by accident. There is no --submit: the terminal is never a compliance backdoor.
trigger builds and signs the exact envelope the server emits, so an event fired locally verifies against the same receiver code a production event would.
neolife
# Default mode is SANDBOX. Every command prints a persistent SANDBOX / LIVE badge.
neolife login --key nk_sandbox_7f21a9c4e0b8…
neolife keys create --name "shopify-integration" --mode sandbox --type secret
neolife orders list --status draft
neolife catalog list
# Draft only — there is no --submit. A provider approves server-side.
neolife orders create --items '[{"productId":"prod_tdf30","quantity":1}]'
# Fire a correctly Standard-Webhooks-signed event at your local receiver — no relay.
neolife trigger order.shipped \
--forward-to http://localhost:3000/webhooks \
--secret whsec_your_signing_secretThe webhook you can point anywhere
Our webhooks never carry PHI. By contract, not by convention.
Every event payload carries ids, status, and reason codes — never patient data. That single design choice means you can route neolife webhooks to Slack, a queue, a Lambda, or a third-party tool with no BAA in the path. When you need the PHI behind an event, you hydrate it with an authenticated, BAA-covered GET — on your terms, not pushed at you.
Event catalog
Signed to the Standard-Webhooks spec (HMAC-SHA256), retried automatically, and rejected outside a ±5-minute replay window.
The reference
A generated spec, and docs you can call.
The API is described by an OpenAPI 3.1 document generated from the server itself, so the docs never drift from the code. Pull the spec into your own tooling, or use the interactive reference to try a call against sandbox in the browser.
- OpenAPI 3.1
/openapi.json - A generated, machine-readable spec you can pull into your own tooling, generate clients from, or diff on every release.
- Interactive docs
/docs - The full API reference — every endpoint, request, and response — rendered from the same spec and callable in-browser against sandbox.
- Bearer auth
Authorization - One header. Sent as Authorization: Bearer nk_live_… or nk_sandbox_… — the key prefix decides whether you touch real patients or synthetic ones.
- Request ids
X-Request-Id - Every response carries a req_… id. Errors surface it too, so a support conversation starts from the exact call that failed.
Build the whole thing on synthetic patients first.
Read the reference, wire up a sandbox key, and get an order from draft to shipped — before a single real patient is in the loop.