> For the complete documentation index, see [llms.txt](https://docs.healthuniverse.com/overview/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.healthuniverse.com/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/sdk-reference.md).

# SDK Reference

`@health-universe/react` exposes typed React Query hooks grouped by clinical domain, plus a generic escape hatch (`useApiQuery` / `useApiMutation` / `api`) for any scoped operation that doesn't yet have a first-class hook.

Every hook's arguments and return type are inferred from the generated client, so the bundled TypeScript declarations (`@health-universe/react` ships `.d.ts`) are the canonical source of truth — your editor's autocomplete is the fastest reference.

> **Org id vs workspace slug.** Two different identifiers address the organization, and mixing them up is the most common mistake. Both come from `useHealthUniverseAuth().organization` (see [Auth](#auth)):
>
> * **Org id** (`org_…`, `organization.id`) — used by org-scoped hooks as a query param: `usePatients({ organizationId })`, `useRoutines(organizationId)`, `useOrgRoutineRuns(organizationId)`. (Org-scoped hooks also default to the provider's active org, so you often don't pass it.)
> * **Workspace slug** (e.g. `acme-health`, `organization.slug`) — used by workspace-scoped hooks as a path param: `useSources(slug)`, `useBundleDetail(slug, bundleId)`.
>
> If a workspace-scoped hook returns nothing, check the slug and your org membership first.

***

## Auth

The SDK manages Clerk internally — **you never import `@clerk/clerk-react`**. It loads Clerk's official script from Clerk's CDN at runtime (the same way `@clerk/clerk-react` does), so the prebuilt widgets are fully styled. Read sign-in state and the active organization, and trigger sign-in/out/org-switching, through the SDK.

```tsx
import { useHealthUniverseAuth, SignedIn, SignedOut, OrganizationSwitcher, UserButton } from '@health-universe/react'

function Header() {
  const { isSignedIn, user, organization } = useHealthUniverseAuth()
  return (
    <header>
      <OrganizationSwitcher />   {/* Clerk's prebuilt widget, via clerk-js */}
      <UserButton />
    </header>
  )
}

// Gate UI on sign-in state:
<SignedIn><App /></SignedIn>
<SignedOut><SignInScreen /></SignedOut>
```

`useHealthUniverseAuth()` returns:

* `isLoaded`, `isSignedIn`
* `user` — the signed-in user (id + display fields)
* `organization` — the active org as `{ id, slug, name }` (the source of both the org **id** and the workspace **slug** above), or `null`
* `organizations` — the user's org memberships, for a custom switcher
* `signIn()`, `signOut()`, `setActiveOrg(organizationId)`

Components: `<SignedIn>` / `<SignedOut>` gate on sign-in state; `<OrganizationSwitcher>` / `<UserButton>` render Clerk's prebuilt, styled widgets (use [`useWorkspace().isPinned`](/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/your-first-health-universe-app.md) to hide the switcher in a pinned app). See [Your First App](/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/your-first-health-universe-app.md) for provider setup.

> **Locked by platform policy.** These widgets are pinned to a deployed app's scope and **can't be unlocked by props**: `<OrganizationSwitcher>` is switch-only (no personal workspace, no Create/Manage organization) and `<UserButton>` hides "Manage account" (Sign out stays). The org **slug** for workspace-scoped hooks still comes from `useHealthUniverseAuth().organization.slug`. The hiding is a UI lock — for an airtight boundary, also disable org creation and restrict the manage/account permissions in the Clerk Dashboard.

***

## Patients

```tsx
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', … } })
}
```

The full set:

* `usePatients(params, options)` — list / search the org's patients
* `usePatient(id)` — a single patient
* `useCreatePatient()`, `useUpdatePatient()`, `useDeletePatient()`
* `useUpsertPatientByExternalId()` — idempotent EHR sync, keyed by your external id

***

## Documents

```tsx
import { useDocumentSearch, useDocumentStatus, useDocumentChunks } from '@health-universe/react'

const { data } = useDocumentSearch({ body: { query: 'discharge summary' } })
const { data: status } = useDocumentStatus(documentId)
```

The full set:

* `useDocumentSearch()` — structured search
* `useSemanticDocumentSearch()` — semantic search (e.g. `{ body: { query: 'aml relapse', threadId } }`)
* `useDocumentTextSearch()` — text search
* `useDocumentStatus(id)`, `useDocumentStatuses()` — status by document, or by thread/patient
* `useDocumentChunks(id)` — a document's extracted text, returned as ordered chunks
* `useExportDocument()`

Render a document's text by sorting its chunks:

```tsx
function DocumentText({ documentId }: { documentId: string }) {
  const { data } = useDocumentChunks(documentId)
  const text = (data?.chunks ?? [])
    .sort((a, b) => a.chunkIndex - b.chunkIndex)
    .map((c) => c.content)
    .join('\n\n')
  return <pre>{text}</pre>
}
```

***

## Routines & runs

```tsx
import { useRoutines, useTriggerRoutine, useRoutineRun } from '@health-universe/react'

const { data: routines } = useRoutines()   // org from the provider default
const trigger = useTriggerRoutine()         // trigger.mutate({ routineId, body: { … } })
const { data: run } = useRoutineRun(runId)  // polls every 3s, stops at a terminal status
```

The full set:

* `useRoutines(orgId)`, `useRoutine(id)` — list/read routines (name, description, `steps[]` pipeline, `inputs[]`)
* `useCreateRoutine()`, `useUpdateRoutine()`, `useDeleteRoutine()`, `useDiscoverRoutines()`
* Routine-secret hooks
* `useTriggerRoutine()` — start a run; returns the new `run_id`
* `useRoutineRun(runId)` — a single run, with **terminal-aware polling** (default 3s, stops at a terminal status)
* `useRoutineRuns(routineId)`, `useOrgRoutineRuns(orgId)` — run history
* `useRetryRun()`, `useCreateRunThread()`, `useDeleteRunThread()`

A run's generated documents live on each step's `output_artifacts` as an A2A artifact array — `[{ name, parts: [{ kind: 'text', text }] }]`:

```tsx
function runOutputs(run) {
  return (run?.steps ?? []).flatMap((step) => {
    const arr = Array.isArray(step.output_artifacts) ? step.output_artifacts : []
    return arr.map((a) => ({
      name: a.name,
      text: (a.parts ?? []).map((p) => p.text).filter(Boolean).join('\n\n'),
    }))
  })
}
```

> Generated text often embeds citation tags like `<file name="x.pdf">x.pdf</file>`. `react-markdown` won't render raw HTML, so strip or transform those before rendering.

***

## Sources (bundles) & run review

`useSources` and `useBundleDetail` are **workspace-scoped** — they take the org **slug** as a path param, not the org id.

```tsx
import { useSources, useBundleDetail } from '@health-universe/react'

// workspaceSlug is the org SLUG — useHealthUniverseAuth().organization?.slug
const { data: bundles } = useSources(workspaceSlug)
const { data: bundle } = useBundleDetail(workspaceSlug, bundleId)
// bundle.documents → input files, bundle.patient, bundle.runs
```

A routine **run** carries its source bundle on `trigger_context.bundle_id`. To build a full run-review surface, pair:

* `useRoutineRun(id)` — steps + `output_artifacts` (the generated documents), and
* `useBundleDetail(slug, bundleId)` — the input documents + patient the run started from.

See the [`react-starter`](https://github.com/Health-Universe/react-starter) `RunView` for a worked example.

***

## Chat threads

Navigator threads are where context documents live.

```tsx
import { useThreads, useThread } from '@health-universe/react'

const { data } = useThreads({ patientId, organizationId }) // search / list
```

The full set: `useThreads`, `useThread`, `useCreateThread`, `useUpdateThread`, `useDeleteThread`.

***

## Other clinical domains

These domains are available as hooks and/or through the escape hatch below:

* **Providers** — `useProviders`, `useProvider`, `useCreateProvider`, `useUpsertProviderByNpi`
* **Rosters** — `useRosters`, `useRoster`, `useRosterPatients`, …
* **Appointments** — `useAppointments`, …
* **Connectors** — `useEnabledConnectors`
* **Clinical tools** — `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. Options and the returned data type are inferred from the operation.

```tsx
import { useApiQuery, useApiMutation, api } from '@health-universe/react'

// Query — options and the returned 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.

***

## Invalidating queries & realtime

Query-key factories are exported so you can invalidate after a mutation or an out-of-band update: `patientKeys`, `runKeys`, `documentKeys`, `sourceKeys`, `threadKeys`, and so on.

```tsx
import { useQueryClient } from '@tanstack/react-query'
import { runKeys } from '@health-universe/react'

const qc = useQueryClient()
qc.invalidateQueries({ queryKey: runKeys.detail(runId) })
```

For run progress, `useRoutineRun` already 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.).

***

## Scope

The client is intentionally limited to the clinical domains above (routines, documents, patients, providers, rosters, appointments, sources/bundles, chat threads, clinical tools, connectors). Admin, deployments, billing, and internal tooling are **not** part of this package and cannot be called through it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.healthuniverse.com/overview/building-apps-in-health-universe/developing-your-health-universe-app/working-in-react/sdk-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
