Your first integration
An end-to-end walkthrough — gate a product with an intake, get a provider-approved order, submit it, receive webhooks, and go live.
This is the whole path, in order: a sandbox key, a product gated by an intake, an order that a licensed provider approves, a submit, and the webhooks that reconcile it. Everything here runs against sandbox with synthetic patients, so you can build and verify the full flow before any agreement is live.
What you're building#
The rail turns "a patient wants this compounded drug" into "an approved order shipped by a pharmacy," without your systems ever touching the clinical decision or the patient record. Your integration does four things:
- Put a compliance intake in front of the product so a patient can't order something they haven't cleared.
- Let an approved intake produce an order that a licensed provider signs off on.
- Submit the approved order to a pharmacy.
- React to webhooks as it ships, and hydrate PHI over authenticated
GETs only when you need it.
You never approve a clinical order yourself — a licensed provider does, and the rail refuses to ship anything they haven't stood behind.
1. Get a sandbox key#
Create a key in the dashboard under Developers → API keys. Use the nk_sandbox_… key for everything in this guide — it resolves to synthetic patients and a mock pharmacy. Install the SDK and point it at the key:
npm install @neolifehealth/sdk
import { Neolife } from "@neolifehealth/sdk";
const neolife = new Neolife({ apiKey: process.env.NEOLIFE_API_KEY! }); // nk_sandbox_…
Keys stay server-side. See Authentication for scopes and the sandbox/live split, and Quickstart if you just want the shortest possible path to a submitted order.
2. Gate the product with an intake#
Before a compounded drug can ship, someone has to establish it's appropriate for this patient. On the rail that's a versioned intake questionnaire, evaluated deterministically to a verdict. Drop the embed widget onto the storefront so the patient answers directly — it uses a per-tenant embed key (not your secret key), is origin-locked to your domains, and returns a patient-safe result:
<div id="neolife-intake"></div>
<script
src="https://api.neolife.health/embed/intake.js"
data-embed-key="pk_embed_…"
data-questionnaire="glp-weightloss"
data-target="#neolife-intake"></script>
An eligible submission clears the deterministic rules and is issued a PHI-free certificate; a disqualified submission is a hard stop that no one can override. Eligible submissions land in provider review. Full detail — verdicts, the certificate, in-app submission — is in Intake & provider approval.
The verdict is not a prescription. Clearing the rules only makes a submission eligible for a licensed provider to review. Nothing ships on the verdict alone.
3. An order gets created and provider-approved#
An eligible, provider-relevant intake produces an order — the unit of fulfillment, one patient's prescriptions bound for a pharmacy. neolife owns the order as the system of record, so its status is authoritative. A new order sits at pending_approval until a licensed reviewer decides:
| Status | Meaning |
|---|---|
pending_approval |
Ready for a licensed provider to review. |
approved |
A provider approved it. Eligible for submission. |
submitted |
Accepted by the pharmacy. |
shipped / delivered |
In transit / delivered. |
Approval is a licensure act — only a reviewer with prescribing licensure (an NPI) can perform it, and it's enforced server-side. In your integration you don't call approve; a provider does it in their console, or through their own credentialed session. When they approve, the order auto-submits to the pharmacy. The full lifecycle and the single-dispatch guarantee are in Orders & fulfillment.
4. Submit the approved order#
If your flow submits separately from approval — for example, you hold approved orders and release them on your own trigger — call submit on an already-approved order. It's safe to retry: the rail atomically claims the order for exactly one dispatch, and refuses any order a provider hasn't approved.
const order = await neolife.orders.submit("ord_123", {
// A stable UUID makes this retry-safe — see /concepts/idempotency
idempotencyKey: "3f1b0c2a-9d6e-4a51-8b2f-1c7e5a9d0e42",
});
console.log(order.status); // "submitted"
The raw HTTP call:
curl -X POST https://api.neolife.health/v1/orders/ord_123/submit \
-H "Authorization: Bearer $NEOLIFE_API_KEY" \
-H "Idempotency-Key: 3f1b0c2a-9d6e-4a51-8b2f-1c7e5a9d0e42"
Submitting an order that no provider approved returns order_not_approved. Every mutation takes an Idempotency-Key (a UUID) so a retry never ships twice — the full contract is in Idempotency.
5. Receive webhooks#
You do not poll for status. Fulfillment flows back over webhooks — and every event is PHI-free (ids and status only), so your callback endpoint stays out of PHI scope. After a submit you'll see order.submitted, then order.shipped, then delivery events. Verify the signature and switch on the event type:
import { verifyWebhook } from "@neolifehealth/sdk";
app.post("/neolife/webhooks", (req, res) => {
const event = verifyWebhook({
payload: req.rawBody,
headers: req.headers,
secret: process.env.NEOLIFE_WEBHOOK_SECRET!,
});
switch (event.type) {
case "order.submitted":
// orderId is present; patient data is not
markSubmitted(event.data.orderId);
break;
case "order.shipped":
markShipped(event.data.orderId);
break;
case "order.delivered":
markDelivered(event.data.orderId);
break;
}
res.sendStatus(200);
});
Set the endpoint up, verify signatures, and handle redelivery idempotently in Handling webhooks.
6. Hydrate detail when you need it#
The webhook told you what happened by id. When you need the patient-facing detail — the shipment tracking, the prescriptions, the timeline — pull it over an authenticated GET. That's the only place PHI crosses, and it's on demand:
const order = await neolife.orders.get("ord_123");
console.log(order.status); // "shipped"
console.log(order.shipment?.tracking); // present once shipped
curl https://api.neolife.health/v1/orders/ord_123 \
-H "Authorization: Bearer $NEOLIFE_API_KEY"
This split — PHI-free events for reconciliation, authenticated reads for detail — is the whole point of the PHI boundary. Keep the webhook path out of PHI scope and reach for a GET only when a human or a workflow actually needs the record.
7. Go live#
Everything above ran on nk_sandbox_…. Going live is a key swap plus a checklist — real patients, a live pharmacy connection, and signature verification on a public endpoint. Nothing about your code changes; the SDK behaves identically across environments. Walk the Go-live checklist before you flip the key.
Recap#
- Intake gates the product; the verdict is deterministic and the certificate is PHI-free.
- A licensed provider approves — the rail never ships a clinical order without it.
- Submit is single-dispatch and idempotent; retry freely.
- Webhooks are PHI-free and drive reconciliation;
GEThydrates PHI on demand.