# Omi Medical Speech-to-Text API

Omi provides one direct-upload transcription front door plus a presigned
large-upload path, invited realtime WebSocket transcription, encounter vocabulary,
structured-token Patterns, multilingual per-utterance routing, speaker
diarization, and acoustic word timestamps.

## Authentication

Base URL: `https://api.omi.health`

Send every HTTP request with:

```http
Authorization: Bearer <OMI_API_KEY>
```

Create and rotate keys at `https://console.omi.health/dashboard`. Do not place
long-lived API keys in public browser bundles.

## First transcription

```bash
curl https://api.omi.health/v1/audio/transcriptions \
  -H "Authorization: Bearer $OMI_API_KEY" \
  -F file=@consultation.wav \
  -F model=omi-medical-1 \
  -F language=en \
  -F response_format=verbose_json
```

The direct-upload front door accepts exactly up to 100,000,000 bytes. Audio
under 30.000 seconds without a webhook returns an OpenAI-compatible 200
response. Audio at or above 30.000 seconds, or any request with `webhook_url`,
returns a 202 transcription job. Accepted formats: WAV, MP3, M4A/MP4, WebM,
OGG, FLAC, and AAC.

Files of 30 seconds or longer use the asynchronous-optimized pipeline.

For the Python or JavaScript quickstarts in the documentation site, install the
client once with `python -m pip install openai` or `npm install openai`.

Response formats:

- `text`: final transcript as `text/plain`.
- `json`: minimal `{"text":"..."}`.
- `verbose_json`: text, language, segments, duration, model, request ID,
  metadata, and words when alignment is available.
- `diarized_json`: historical speaker-segment response shape.

## Models

- `omi-medical-1`: flagship medical speech-to-text model.
- `omi-medical-edge-1`: on-device model for short audio.

Published benchmark claims apply to English unless the claim states otherwise.

## Languages

Omi Medical STT is an English-first medical speech model. English is the
benchmarked and medically evaluated language. Spanish, Portuguese, French,
German, Dutch, Arabic, and Hindi were included in training and are available
for testing and evaluation; they have not been medically evaluated and should
not be relied on for medical accuracy.

| Language | Family | Accepted examples | Medical evaluation |
|---|---|---|---|
| English | `en` | `en-AU`, `en-GB`, `en-US` | Benchmarked |
| Spanish | `es` | `es-ES`, `es-MX`, `es-419` | Testing only |
| Portuguese | `pt` | `pt-BR`, `pt-PT` | Testing only |
| French | `fr` | `fr-FR`, `fr-CA` | Testing only |
| German | `de` | `de-DE`, `de-AT` | Testing only |
| Dutch | `nl` | `nl-NL`, `nl-BE` | Testing only |
| Arabic | `ar` | `ar-SA`, `ar-AE` | Testing only |
| Hindi | `hi` | `hi-IN` | Testing only |

Use an explicit BCP-47 tag when known. If `language` is omitted, Omi uses the
API key’s stored default and then English. Omission is deliberately not
automatic detection.

### Automatic language detection

Set `language=auto` on `omi-medical-1` requests. Omi detects each closed
utterance, collapses consecutive same-language utterances into a run, and
concatenates the result in time order.

`language_hints` is an optional JSON array string containing 1–8 candidate
BCP-47 tags:

```bash
--form-string 'language_hints=["en","es-MX","pt-BR"]'
```

Without hints, all eight families are candidates. An unsupported detected
language fails explicitly; Omi never silently falls back to English.
Token-level code switching is outside V1. The current detector does not expose
a calibrated confidence value.

Public automatic routing is synchronous only. Async `language: "auto"` is
available to named invited testers.

English is the benchmarked, medically evaluated language. The seven additional
languages carry the same serving safety rails and are available for testing and
evaluation; they are not medically evaluated.

## Encounter vocabulary

Pass exact expected terms as a JSON array:

```bash
--form-string 'vocabulary=["Tinel","Hepcludex","tirzepatide"]'
```

Use 5–30 terms likely to occur in the encounter. Lists of 30 or fewer use
direct mode with structural safety checks. Lists of 31–1,000 use retrieval
mode with an acoustic-support rail. Arabic, Hindi, and automatic-language
requests accept up to 30 effective terms per language.

Request terms are merged above the API key’s stored language-specific
dictionary. Set `dictionary=false` to suppress stored terms for one request.
Suspect insertions, number changes, runaway length, repeated context, or copied
prompt text cause fallback to the no-context transcript. Verbose responses
include mode, requested/applied term counts, suspect count, and fallback state.

## Patterns v0-preview

Patterns are request-only hints for expected structured tokens:

```bash
--form-string 'patterns=[0-9]{1,3}(mg|ml|mcg),[A-Z]{2}-[0-9]{4}'
```

Only set patterns you expect in the recording. A pattern can format a token
only when the writer independently supports it. Up to 32 validated expressions
are accepted. Patterns are never stored or applied globally. This feature is
currently enabled for invited preview keys.

## Speaker diarization and timestamps

Set `diarize=true` and use `response_format=verbose_json`. The response may
contain `segments[].speaker`, `words[].speaker`, `start`, `end`, and `speakers`.

Speaker labels are anonymous, session-local clusters, not names or roles.
Diarization and alignment decorate sealed text and cannot rewrite the
transcript. Very short interjections, overlap, reverberation, and background
speech can reduce attribution quality.

## Asynchronous transcription

For files up to 100,000,000 bytes, use the same direct-upload front door.
It dispatches recordings of 30.000 seconds through 30 minutes to a job and
returns `Location` plus `Retry-After: 5`.

For larger files up to 1 GiB, use the presigned large-upload path:

1. `POST /v1/jobs` with filename, content type, exact byte length, model, and
   options.
2. POST the file to every field and URL returned in `upload`.
3. `POST /v1/jobs/{job_id}/complete`.
4. `GET /v1/jobs/{job_id}` with bounded exponential backoff.
5. When status is `succeeded`, download `result.download_url` before it expires.

The complete Python example uses Requests; install it once with
`python -m pip install requests`.

Job states: `awaiting_upload → accepted → running → succeeded`, or `failed`.
Result URLs expire after 15 minutes. Job audio, canonical results, requested
result representations, and job metadata are retained for 72 hours. Polling a
succeeded job may mint a fresh 15-minute URL while the result is retained.
After retention, polling reports `result: {"expired": true}`. An already
expired presigned URL returns the storage provider's native expiry response.

Optional `Idempotency-Key` applies to asynchronous job creation and is retained
for 24 hours. Inline 200 requests accept and ignore it.

## Webhooks

Create a separate webhook signing secret in the developer console before
sending `webhook_url`. The secret is shown once and is not your API key.

Completion events are transcript-free. They include an event id, event type,
created time, job id, status, and authenticated poll URL. Verify the raw body
using:

```text
webhook-signature = v1,base64(HMAC-SHA256(
  base64decode(whsec_...), "webhook-id.webhook-timestamp.raw-body"
))
```

Reject timestamps outside ±5 minutes and deduplicate by `webhook-id`.
Delivery is at least once. Omi attempts immediately, then after 1 minute,
5 minutes, 30 minutes, 2 hours, and 8 hours. Each attempt has a fresh
timestamp/signature, a 10-second timeout, and never follows redirects.
Rotation keeps the previous version valid for a 24-hour overlap; in-flight
events remain pinned to their original version.

## Realtime v1-preview

Endpoint:

```text
wss://rt.omi.health/v1/realtime
Authorization: Bearer <OMI_API_KEY>
```

Realtime access is enabled by request. Send a start frame, then binary mono
PCM16 16 kHz frames, then stop:

```json
{"type":"start","format":"pcm16","sample_rate":16000,"language":"en"}
{"type":"stop"}
```

Events include `hello`, draft `partial`, `turn_end`, display correction
`partial`, `status`, authoritative `final`, `capacity`,
`session_limit_reached`, `error`, and `end`.

Drafts are revisable and non-authoritative. Only the final event with
`tier:"record"`, `authority:"authoritative"`, and `is_final:true` is the
official record. The session cap is 30 minutes. Capacity refusal includes
`retry_after`. On cap, Omi closes cleanly, produces the record, then allows an
immediate reconnect.

## Errors

| Status | Meaning | Action |
|---|---|---|
| 400 | Malformed request, unsupported model option, mismatched media type | Fix; do not retry unchanged |
| 401 | Missing or invalid key | Verify the bearer token and replace or rotate the credential if needed |
| 404 | Unknown or non-owned job | Check job ID and owner key |
| 413 | File or duration exceeds the route | Use async or split at silence |
| 422 | Unsupported language/detection, bad vocabulary/pattern/hints | Correct field or candidate set |
| 429 | Rate, queue, or capacity limit | Honor `Retry-After` with jitter |
| 503 | Temporary backend, gated preview, or `detector_busy` | Honor `Retry-After`; never silently change models/language |

## Data handling and support

Direct uploads that resolve inline are deleted immediately on completion or
failure. Job audio/results/metadata are owner-scoped and retained for 72 hours.
Operational telemetry stores non-content usage and safety counters.

Status: `https://d14wqvek28lca6.cloudfront.net/`

Support: `hello@omi.health`. Include the response `request_id`.
