Concepts

Sandbox & testing

nk_sandbox_ keys resolve to synthetic patients and a mock pharmacy, so you can build and verify the whole integration before going live.

Sandbox lets you exercise the entire rail — orders, intake, provider approval, routing, webhooks — against synthetic data, then go live by swapping one key. The API behaves identically in both environments; only what a key resolves to changes.

Sandbox vs. live#

A key's prefix decides the environment. Nothing else about your code needs to change.

Prefix Environment Resolves to
nk_sandbox_… Sandbox Synthetic patients + a mock pharmacy
nk_live_… Live Real patients and pharmacies (requires an active agreement)

Because a sandbox key can never touch a real patient or a real pharmacy, you can build and run your whole integration end to end with zero risk — no shipment ever leaves, no PHI is ever involved. See Authentication for how keys, scopes, and prefixes work.

Identical behavior#

Sandbox is not a stub. It runs the same request validation, the same state machine, the same idempotency guarantees, and emits the same PHI-free events as live. That means the things you test in sandbox are the things that happen in production:

  • Submitting an unapproved order is refused in sandbox exactly as it is in live.
  • A missing scope returns 403; a bad key returns 401.
  • A replayed Idempotency-Key returns the stored response with Idempotent-Replayed: true.
  • submitted → downstream fulfillment status transitions arrive over webhooks as PHI-free { id, status } payloads.

The mock pharmacy accepts routed orders and advances them through fulfillment status so you can watch a complete lifecycle without a real pharmacy connection.

Switching = swap the key#

Going live is a one-line change. Keep the key in an environment variable and point it at a live key when you're ready:

# .env — sandbox
NEOLIFE_API_KEY=nk_sandbox_…

# .env — live
NEOLIFE_API_KEY=nk_live_…

Your code never mentions the environment:

import { Neolife } from "@neolifehealth/sdk";

const neolife = new Neolife({ apiKey: process.env.NEOLIFE_API_KEY! });

The SDK infers the environment from the key prefix, so there is no separate flag to flip.

Testing webhooks locally#

Fulfillment status flows back over webhooks, and your callback endpoint usually isn't reachable from the internet while you develop. The CLI forwards live sandbox events to a local URL:

neolife listen --forward-to http://localhost:3000/webhooks/neolife

listen streams sandbox events as they happen and replays them to your handler, printing each delivery so you can confirm your endpoint parses the { id, status } shape and verifies signatures. Trigger events by running real sandbox flows — submit an order and watch the transitions arrive — so your reconciliation logic is exercised against the same payloads live will send.

Ready for live — a checklist#

Before you swap in a live key, confirm your integration handles the whole rail in sandbox:

  • You create orders, capture the returned id, and never assume a status client-side.
  • Every mutation sends a stable Idempotency-Key (UUID), and your retries reuse it — see Idempotency.
  • You handle the refusal to submit an unapproved order rather than treating it as an error to swallow.
  • Your webhook endpoint verifies signatures, is idempotent on redelivery, and returns 2xx quickly.
  • You reconcile on the PHI-free { id, status } payload and hydrate detail over authenticated GETs only when needed (the PHI boundary).
  • 401/403/409 responses are handled distinctly, not as one generic failure.
  • Your live key is server-side only, scoped to what this integration needs, and stored as a secret.

When those hold in sandbox, they hold in live. Walk through the full sequence in the go-live guide.