Cookbook: triage form on a tablet
Build an ER triage app — record, extract, print — in 200 lines.
End-to-end recipe: an ER nurse picks up a tablet, taps Record, narrates the patient's vitals + complaint, and a structured triage card is printed and shared with the consulting doctor — under 30 seconds.
Studio side
- Clone Emergency Triage as your starter API.
- Trim the schema to:
vitals,chiefComplaint,onset,triageCategory(choice: red / orange / yellow / green),actions(textarea). - Add an input
stationId: string. - Publish as slug
er-triage. - Mint an
ohms_test_*key for the demo build.
Web build (Vite + React)
import { OHM } from "@ohm_studio/sdk";
import { OhmProvider, useRecorder } from "@ohm_studio/sdk/react";
const ohm = new OHM({
apiKey: import.meta.env.VITE_OHM_TEST_KEY!,
baseUrl: "https://api.ohm.doctor",
});
export default function App() {
return (
<OhmProvider client={ohm}>
<Triage />
</OhmProvider>
);
}
function Triage() {
const r = useRecorder({
apiSlug: "er-triage",
extractInputs: { stationId: "ER-2" },
speakerMode: "doctor_patient", // nurse + patient triage
extractLanguage: "auto",
silenceAutoStop: { ms: 6000 },
maxDurationMs: 5 * 60_000,
persist: { metadata: { stationId: "ER-2" } },
wakeLock: true,
});
return (
<div className="flex flex-col items-center justify-center min-h-screen p-6">
{!r.data && !r.extracting && (
<button
onClick={r.isRecording ? r.stop : r.start}
className={`w-40 h-40 rounded-full text-2xl font-bold text-white ${
r.isRecording ? "bg-red-600 animate-pulse" : "bg-emerald-700"
}`}
>
{r.isRecording ? `STOP\n${r.durationSec.toFixed(0)}s` : "RECORD"}
</button>
)}
{r.extracting && <div className="text-lg">Extracting…</div>}
{r.data && (
<article className="max-w-md w-full p-6 rounded-xl border bg-white">
<h1 className="text-xl font-semibold">Triage card</h1>
<p className="text-sm text-slate-600 mb-4">{r.transcript}</p>
<pre className="text-xs">{JSON.stringify(r.data, null, 2)}</pre>
<button onClick={() => window.print()}>Print</button>
</article>
)}
</div>
);
}Note the
useRecorderhook in@ohm_studio/sdk/reactdoes the codec cascade, mic permission, VU metering, silence auto-stop, wake-lock, and theaudio.extractcall for you.persist: truewrites the recording to IndexedDB before the upload — if the tablet reboots mid-upload you can recover viausePendingRecordings()and retry. See Browser Recorder.
Hardening for production
- Move the live key behind a backend proxy (see RN key handling — same pattern works for web).
- Add a
OhmRateLimitErrorhandler — show "busy, retry in N seconds". - Wire
onUsageinto your hospital's metrics so finance can track volume per station. - Set
retainPayloads: trueon this API if you want the audit log to keep transcripts for medico-legal review.
Performance
End-to-end on a 30-second consult: typically 2–4 seconds wall clock (STT + extraction). The doctor receives the structured card before they reach the patient bay.