@health-universe/react
v0.2.2
Published
Headless React SDK for the Health Universe API — hooks and a typed client for routines, designed to run on Clerk satellite domains.
Readme
@health-universe/react
React SDK for building clinical frontends on Health Universe. It gives clinical customers typed React Query hooks and a ready-wired client for the Health Universe API — routines, documents, patients and the rest of the clinical surface — so you can build your own UI instead of using ours.
It's designed for apps deployed on Health Universe Clerk satellite domains
(e.g. apps.healthuniverse.com/{external_id}), alongside Streamlit apps, where
the user already has an active Clerk session.
What's exposed
The client is deliberately scoped — only these domains are generated; the rest of the platform API (admin, deployments, billing, internal tooling) does not exist in this package and can't be called through it.
| Area | Domains | | --- | --- | | Routines | routines, routine runs, triggers, connector bindings | | Documents | search (structured / semantic / text), status, chunks, export, bundles | | Clinical data | patients, providers, rosters, appointments | | Sources | document bundles + detail (input docs, patient, runs) | | Chat | Navigator threads (the context documents live in) | | Tools & connectors | clinical-tools, connectors |
To add a domain, add its @ApiTags value to openapi-ts.config.ts and
regenerate.
Install
npm install @health-universe/react @tanstack/react-query reactreact and @tanstack/react-query are peer dependencies.
New here?
USAGE.mdis a worked guide with copy-pasteable examples for each domain, andhu-react-starteris a complete example app.
Auth model (read this)
The Health Universe API authenticates only from the
Authorization: Bearer <jwt> header. On a satellite domain the __hu_session
cookie is consumed by the Envoy ingress, not the API — so the SDK turns the
active Clerk session into a bearer token and sends it on every request
(credentials: 'include' still forwards cookies for the few cookie-based
endpoints).
By default the SDK reads the token from the Clerk satellite session via the
window.Clerk global. If you use @clerk/clerk-react, pass its getToken.
Quick start
import { HealthUniverseProvider } from '@health-universe/react'
import { useAuth } from '@clerk/clerk-react'
function Providers({ children }: { children: React.ReactNode }) {
const { getToken } = useAuth() // Clerk satellite session
return (
<HealthUniverseProvider
baseUrl="https://api.healthuniverse.com" // NO /api/v1 — paths include it
getToken={getToken}
organizationId={orgId} // default for org-scoped hooks
>
{children}
</HealthUniverseProvider>
)
}If you don't pass getToken, the SDK falls back to
window.Clerk.session.getToken(). The provider also creates a QueryClient
for you; pass your own via queryClient if your app already has one.
Hooks
Patients
import { usePatients, usePatient, useCreatePatient } from '@health-universe/react'
function Roster({ orgId }: { orgId: string }) {
const { data, isLoading } = usePatients({ organizationId: orgId, sortBy: 'last_name' })
const create = useCreatePatient()
// create.mutate({ body: { organization_id: orgId, first_name: 'Jane', ... } })
...
}usePatient(id), useUpdatePatient(), useDeletePatient(), and
useUpsertPatientByExternalId() (idempotent EHR sync, keyed by your external id)
round out the domain.
Documents
import { useDocumentSearch, useDocumentStatus, useDocumentChunks } from '@health-universe/react'
const { data } = useDocumentSearch({ body: { query: 'discharge summary' } })
const { data: status } = useDocumentStatus(documentId)Plus useSemanticDocumentSearch, useDocumentTextSearch, useDocumentStatuses
(by thread/patient), and useExportDocument().
Routines & runs
import { useRoutines, useTriggerRoutine, useRoutineRun } from '@health-universe/react'
const { data: routines } = useRoutines() // org from provider default
const trigger = useTriggerRoutine() // trigger.mutate({ routineId })
const { data: run } = useRoutineRun(runId) // terminal-aware pollingFull set: useRoutine, useCreateRoutine, useUpdateRoutine,
useDeleteRoutine, useDiscoverRoutines, routine-secret hooks, useRoutineRuns,
useOrgRoutineRuns, useRetryRun, useCreateRunThread/useDeleteRunThread.
Sources (bundles) & run review
import { useSources, useBundleDetail } from '@health-universe/react'
// workspaceId here is the org SLUG (not the org id) — Clerk's
// useOrganization().organization.slug. Workspace routes are slug-addressed.
const { data: bundles } = useSources(workspaceSlug)
const { data: bundle } = useBundleDetail(workspaceSlug, bundleId)
// bundle.documents → input files, bundle.patient, bundle.runsA routine run carries its source bundle on trigger_context.bundle_id; pair
useRoutineRun(id) (steps + output_artifacts = generated docs) with
useBundleDetail(slug, bundleId) (input docs + patient) to build a full
run-review surface. See the hu-react-starter
RunView for a worked example.
Chat threads
import { useThreads, useThread } from '@health-universe/react'
const { data } = useThreads({ patientId, organizationId }) // search/listPlus useCreateThread, useUpdateThread, useDeleteThread.
Other clinical domains
useProviders/useProvider/useCreateProvider/useUpsertProviderByNpi,
useRosters/useRoster/useRosterPatients/…, useAppointments/…,
useEnabledConnectors, and the clinical-tool hooks (useAutofillForm,
useExecuteTool, useGenerateFormSchema, useSuggestTools).
Any endpoint, fully typed — useApiQuery / useApiMutation
Every scoped operation can be turned into a typed query or mutation without a bespoke hook. Domains that don't yet have first-class hooks (providers, rosters, appointments, clinical-tools, connectors, bundles) are reachable this way today:
import { useApiQuery, useApiMutation, api } from '@health-universe/react'
// Query — options and the returned data type are inferred from the operation.
function Providers({ orgId }: { orgId: string }) {
const { data } = useApiQuery(
api.getProviders,
{ query: { organizationId: orgId } },
{ enabled: !!orgId },
)
}
// Mutation — variables are the operation's call options.
function NewAppointment() {
const create = useApiMutation(api.createAppointment)
// create.mutate({ body: { organization_id, provider_id, patient_id, start_time, ... } })
}api is the generated SDK for the scoped surface only. You can also grab the
raw client with useHealthUniverseClient() and call api.* imperatively.
Realtime
useRoutineRun polls (default 3s) and stops at a terminal status. The SDK does
not bundle Supabase — for sub-second updates, supply your own subscription and
invalidate the relevant query (runKeys.detail(runId), patientKeys.list(...),
etc.).
Development
yarn build # tsup → dist (ESM + CJS + d.ts)
yarn check:type # tsc --noEmit
yarn generate # regenerate the scoped src/client from the live OpenAPI specyarn generate requires the NestJS server running locally:
cd apps/nestjs && yarn start:dev # serves the spec at http://localhost:3002/api-json
yarn workspace @health-universe/react generateGeneration is tag-filtered (allowlist in openapi-ts.config.ts), preserves
src/client/client-config.ts (clean: false), and omits the
@hey-api/transformers plugin — date fields are returned as ISO strings.
