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.
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)
| Setting | Default | What changes when you flip it |
|---|---|---|
enforceClinicalFoundation | on | Off → drops the clinical safety priors (vital sanity, negation, code-mix, narrative formatting). Requires an audit-logged reason. Almost no one should turn this off. |
retainPayloads | off | On → every invocation row stores retainedInput + retainedOutput. Default is privacy-first. Flip on for debugging or compliance audit retention. |
redactPHI | off | On → 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(). |
rateLimitPerMinute | 60 | Per-API key request ceiling. Pilot hospitals can leave at 60. Bump to 600 for production volume. |
temperature | 0.2 | LLM temperature for extraction. Higher = more creative, more variation, lower compliance with strict schemas. Don't touch unless you've measured. |
modelId | engine default | Per-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. |
maxOutputTokens | 32 768 | LLM 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):
| Option | Where | Effect |
|---|---|---|
signal | every method | AbortSignal — cancel mid-flight. Aborts surface as OHMAbortError. React hooks auto-attach on unmount. |
idempotencyKey | every extraction method | Sent as Idempotency-Key header. Same key in same org returns the cached response for 24h. UUID v4 recommended. |
patientHash | extraction methods | Opaque hash of the hospital's patient ID (ABHA / MRN / IPD). Stored on the invocation row for audit search. OHM never sees raw PHI. |
recordedById | extraction methods | Verified user id from your session token — stamps the audit trail. |
onProgress | audio.transcribe, audio.extract | Upload progress callback ({loaded, total, percent}) => void. Routes through XHR for real progress events. |
language | audio methods | Hint for STT. auto (default) lets the STT layer detect. Override only when you know the language reliably. |
speakerMode | audio methods | doctor (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
| Var | Default | Effect |
|---|---|---|
DATABASE_URL | required | Postgres connection string. Neon / RDS / self-host all work. |
STUDIO_MAX_AUDIO_BYTES | 104857600 (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:
| Var | Default | Effect |
|---|---|---|
OHM_ENGINE_URL | https://api.ohm-engine.in | Engine base URL. |
OHM_ENGINE_API_KEY | required | Per-hospital bearer issued by OHM Ops. |
LLM_HOURLY_QUOTA_PER_USER | 60 | Soft per-user token budget for the doctor app. Doesn't affect Studio API quotas. |
Async-job worker
| Var | Default | Effect |
|---|---|---|
STUDIO_JOBS_WORKER_DISABLED | unset | Set true on read-only API replicas so they don't compete with the primary worker. |
STUDIO_JOBS_IDLE_MS | 1000 | Polling interval when the queue is empty. |
STUDIO_JOBS_WEBHOOK_TICK_MS | 30000 | How often the webhook dispatcher wakes up. |
Rate limiting
| Var | Default | Effect |
|---|---|---|
STUDIO_DEFAULT_RATE_LIMIT_PER_MIN | 60 | Falls back to this when an API key has no rateLimitPerMinute override. |
Studio UI
| Var | Default | Effect |
|---|---|---|
STUDIO_FEATURE_DEFAULT_ENABLED | false | When true, every new org gets Studio access automatically. Otherwise platform admin flips per-org. |
Branding (optional)
| Var | Default | Effect |
|---|---|---|
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:
- Per-call SDK option — wins always (caller's intent is explicit).
- Per-API Studio setting — applied when the SDK didn't override.
- Server env var default — when neither of the above is set.
Example: rate limit
- API key has
rateLimitPerMinute = 200set in Studio STUDIO_DEFAULT_RATE_LIMIT_PER_MIN = 60env 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.