OHMOHM Studio

Configuration reference

Every env var, every per-API toggle, every default. Hospitals don't need to set anything to start; this is the menu when you want to.

View as Markdown

OHM Studio's defaults are tuned so a fresh hospital deployment works with zero configuration. This page is the full menu of what's available when you want to customise — every server env var, every per-API toggle in Studio, every SDK option.

All settings ship with a sensible default. None are required. Skip this page until you have a specific reason to deviate from the default.

Per-API settings (Studio → API → Settings tab)

SettingDefaultWhat changes when you flip it
enforceClinicalFoundationonOff → drops the clinical safety priors (vital sanity, negation, code-mix, narrative formatting). Requires an audit-logged reason. Almost no one should turn this off.
retainPayloadsoffOn → every invocation row stores retainedInput + retainedOutput. Default is privacy-first. Flip on for debugging or compliance audit retention.
redactPHIoffOn → server scrubs patient names (after Mr/Mrs/Dr), ABHA / Aadhaar / phone / MRN / UHID / IPD identifiers from the transcript before the LLM call. Replaced with typed tokens ([PATIENT_1]). The structured response carries the tokens; restore via restoreTokens().
rateLimitPerMinute60Per-API key request ceiling. Pilot hospitals can leave at 60. Bump to 600 for production volume.
temperature0.2LLM temperature for extraction. Higher = more creative, more variation, lower compliance with strict schemas. Don't touch unless you've measured.
modelIdengine defaultPer-API model override sent as modelHint to the engine. Default is whatever the engine has configured for the hospital. Use to A/B test specific models on a single API.
maxOutputTokens32 768LLM output cap. Default is "effectively unlimited for clinical extractions" — caps mid-JSON truncation.

Rate-limit behaviour: Per-key per-minute. When exceeded, returns 429 Too Many Requests with retry-after header. SDK throws OHMRateLimitError carrying retryAfterSec.

Per-call SDK options

Every SDK method accepts these (all optional, all default off):

OptionWhereEffect
signalevery methodAbortSignal — cancel mid-flight. Aborts surface as OHMAbortError. React hooks auto-attach on unmount.
idempotencyKeyevery extraction methodSent as Idempotency-Key header. Same key in same org returns the cached response for 24h. UUID v4 recommended.
patientHashextraction methodsOpaque hash of the hospital's patient ID (ABHA / MRN / IPD). Stored on the invocation row for audit search. OHM never sees raw PHI.
recordedByIdextraction methodsVerified user id from your session token — stamps the audit trail.
onProgressaudio.transcribe, audio.extractUpload progress callback ({loaded, total, percent}) => void. Routes through XHR for real progress events.
languageaudio methodsHint for STT. auto (default) lets the STT layer detect. Override only when you know the language reliably.
speakerModeaudio methodsdoctor (single-speaker dictation) or doctor_patient (two-speaker conversation). Default doctor.

Server environment variables

These live in apps/api/.env (or your container env). All optional — sensible defaults baked in.

Database / storage

VarDefaultEffect
DATABASE_URLrequiredPostgres connection string. Neon / RDS / self-host all work.
STUDIO_MAX_AUDIO_BYTES104857600 (100 MB)Per-request audio upload cap. Bump for multi-hour ICU recordings.

LLM / extraction

The hospital orchestrator has no LLM secrets — every AI feature (audio transcription, structured extraction, visit-notes chat with hospital-DB tools, Studio playgrounds, codes AI Assistant, all SDK endpoints) calls the OHM Engine, which holds the provider keys internally. The hospital boundary returns vendor-neutral results and error codes (see below).

Hospital-side AI config:

VarDefaultEffect
OHM_ENGINE_URLhttps://api.ohm-engine.inEngine base URL.
OHM_ENGINE_API_KEYrequiredPer-hospital bearer issued by OHM Ops.
LLM_HOURLY_QUOTA_PER_USER60Soft per-user token budget for the doctor app. Doesn't affect Studio API quotas.

Async-job worker

VarDefaultEffect
STUDIO_JOBS_WORKER_DISABLEDunsetSet true on read-only API replicas so they don't compete with the primary worker.
STUDIO_JOBS_IDLE_MS1000Polling interval when the queue is empty.
STUDIO_JOBS_WEBHOOK_TICK_MS30000How often the webhook dispatcher wakes up.

Rate limiting

VarDefaultEffect
STUDIO_DEFAULT_RATE_LIMIT_PER_MIN60Falls back to this when an API key has no rateLimitPerMinute override.

Studio UI

VarDefaultEffect
STUDIO_FEATURE_DEFAULT_ENABLEDfalseWhen true, every new org gets Studio access automatically. Otherwise platform admin flips per-org.

Branding (optional)

VarDefaultEffect
DEFAULT_BRAND_NAME"OHM"Falls back to this when an org hasn't set its studioBranding.name. Surfaced in default email templates.

SDK client options

OHM is hospital-deployed (see Versions → 0.12.0). Each hospital exposes its own API URL — point baseUrl at the hospital you're integrating with. https://api.ohm.doctor is OHM's own demo hospital and works for evaluation; production integrations should target the customer hospital.

new OHM({
  apiKey: "ohms_test_xxx",                         // required
  baseUrl: process.env.OHM_API_URL!,               // e.g. https://api.<hospital>.example
  timeoutMs: 60_000,                               // default 60s — bump for large audio
  maxRetries: 2,                                   // default 2 — auto-retry on transient
  fetch: globalThis.fetch.bind(globalThis),        // override for proxies / Node 18+
  onUsage: (event) => mySentry.capture(event),     // optional telemetry hook
  acknowledgeBundledKey: false,                    // RN only — required for ohms_live_*
  mock: false,                                     // unit-test mode
});

Webhook receiver (when using async + webhooks)

Verify signatures + dedupe deliveries:

import { createHmac } from "node:crypto";

const seenDeliveries = new Set();   // replace with Redis SET in prod

app.post("/ohm-callback", express.json(), (req, res) => {
  // 1. ACK fast — Stripe-style, return 200 BEFORE doing work.
  res.status(200).end();

  // 2. Idempotency — same delivery may arrive twice on retry.
  const id = req.headers["x-ohm-delivery-id"];
  if (seenDeliveries.has(id)) return;
  seenDeliveries.add(id);

  // 3. HMAC signature verification (per-job secret stored when you
  //    created the job — fetch from your DB by jobId).
  const job = await fetchJob(req.body.jobId);
  const expected = createHmac("sha256", job.webhookSecret)
    .update(JSON.stringify(req.body))
    .digest("hex");
  if (`sha256=${expected}` !== req.headers["x-ohm-signature"]) {
    console.warn("invalid signature; dropping");
    return;
  }

  // 4. Process.
  if (req.body.event === "extraction.job.completed") {
    await persistResult(req.body);
  }
});

"Just defaults, nothing custom" deployment

If you skip this page entirely, here's what a hospital gets out of the box:

  • 60 rpm per API key
  • 100 MB per audio upload
  • Foundation block enforced on every extraction
  • No payload retention (privacy-first)
  • No PHI redaction (LLM sees the transcript verbatim)
  • Auto-retry on transient LLM failures (2 retries)
  • 24-hour idempotency window
  • Stripe-style webhook retry schedule (5min → 30min → ... → 24h)
  • 100-row default invocation log on the Studio Logs tab
  • Async worker enabled (1 worker per API pod)

That's a working production setup. Tune only what you need.

Configuration order of precedence

When the same setting can be controlled in multiple places:

  1. Per-call SDK option — wins always (caller's intent is explicit).
  2. Per-API Studio setting — applied when the SDK didn't override.
  3. Server env var default — when neither of the above is set.

Example: rate limit

  • API key has rateLimitPerMinute = 200 set in Studio
  • STUDIO_DEFAULT_RATE_LIMIT_PER_MIN = 60 env var
  • → Effective limit: 200 (Studio setting wins).

Where to file feedback

Configuration UX is iterated based on what hospitals actually deploy. If a setting is missing or default is wrong for your workflow, drop a note in your deployment channel — we tune the defaults across releases.