Operate safely
Production checklist
The first request is small. A reliable integration also protects credentials, survives transient failures, preserves job identity, and records enough metadata to investigate a result without logging clinical content.
Pick your processing region
api.eu.omi.health (EU only), api.us.omi.health (US only) or api.omi.health (nearest healthy region, with Frankfurt fallback). One API key works on all three, and data stays in the region that processes it. Follow the returned poll_url for jobs rather than rebuilding job URLs on another host. Regions & endpoints- Keep API keys server-side and rotate them from the console.
- Send a stable idempotency key for every job-creating upload.
- Retry only transient statuses, honoring Retry-After.
- Store request_id, job id, and model_build with your own record.
- Choose the shortest result-retention window your workflow needs.
- Sign a BAA before sending PHI and monitor the public status page.
Choose result retention
Every plan supports zero-retention English Medical batch up to 15 minutes / 32 MiB. Send X-Omi-Retention: none with language=en, dictionary=false and no speakers or custom hints. Processing is RAM-only; content-free usage and audit metadata remain. See supported options.
Ordinary eligible requests already support the same inline duration. The privacy option prevents stored fallback: busy requests return 503 instead of 202, unsupported options return 422, and a disconnect leaves no saved result.
For reliable async pickup, keep the normal retention window (24 hours by default, 1–72 hours in settings). After durably saving the result, call DELETE /v1/jobs/{job_id}/result. A 204 confirms content removal; 202 means cleanup is pending. GET does not delete the result.
Upload and response timeouts
The 60-second audio boundary and 15-minute extended inline limit measure recording length, not HTTP request duration. A short recording can take longer to upload without changing its duration class. The service inspects duration after receiving the upload.
Direct uploads have a separate 300-second deadline and protection against stalled transfers. The current connection idle budget is 330 seconds with no data sent or received; active upload traffic resets that idle clock. It is not a 330-second total request limit. Your client or proxy can impose a shorter timeout, so allow time for both uploading and processing and configure the client accordingly.
Extended sync is bounded by RAM and available processing capacity, not by a conversion from audio minutes to HTTP seconds. For a stored result that survives disconnects, use async. For zero-retention delivery, a disconnect leaves no saved result to retrieve.
1. Protect and rotate credentials
A newly created API key is revealed once. Omi stores only its hash. Put the raw value in a server-side secret manager; never embed it in a browser, mobile binary, desktop bundle, URL, log line, or support ticket.
Use separate keys for separate deployed environments when practical. During rotation, deploy the replacement first and then remove the old value from your secret store. A revoked key returns 401 and cannot be recovered.
2. Make job creation idempotent
Send Idempotency-Key on direct uploads and presigned job creation. Use a stable identifier for the source recording, not a random value generated on every retry. Keys may contain 1–255 characters and are retained for 24 hours.
Do not reuse a key for different audio
idempotency_conflict. This protects against accidentally attaching the wrong transcript to an encounter.3. Retry narrowly
Retry 429 and transient 5xx responses with exponential backoff, jitter, and the server's Retry-After value. Do not retry 4xx requests unchanged. Poll accepted jobs at the interval returned by the API, or use signed webhooks.
import random, time
import requests
RETRYABLE = {429, 500, 502, 503, 504}
client = requests.Session() # Reuse TCP/TLS connections across requests.
def request_with_backoff(method, url, **kwargs):
for attempt in range(5):
response = client.request(method, url, timeout=60, **kwargs)
if response.status_code not in RETRYABLE:
response.raise_for_status()
return response
retry_after = response.headers.get("Retry-After")
delay = float(retry_after) if retry_after else min(30, 2 ** attempt)
time.sleep(delay + random.uniform(0, 0.5))
response.raise_for_status()| Outcome | Client action |
|---|---|
| 200 / 201 / 202 | Continue; a 202 is a job, not a partial transcript. |
| 400 / 403 / 422 | Correct the request or entitlement before trying again. |
| 401 | Replace or rotate the credential; do not loop. |
| 402 | Resolve billing in the console or wait for the monthly reset. |
| 429 / transient 5xx | Retry with bounded backoff and the same idempotency key. |
4. Keep useful, content-free evidence
Record the response request_id, async job id, effective language, language_source, andmetadata.model_build. Include the request id in support messages. Avoid logging audio, transcript text, filenames, vocabulary terms, or presigned result URLs unless your own security policy explicitly permits it.
5. Pin releases and watch availability
The model name is stable; the served artifact is identified bymetadata.model_build and theX-Omi-Model-Build response header. Watch the changelog, test a new build against representative audio, and keep the prior accepted build in your own integration record.
Subscribe at status.omi.health. For PHI, complete the self-serve BAA before the first upload. Security, subprocessors, retention, and compliance status live in theTrust Center.
6. Accept additive response fields
Parse the fields your integration uses and ignore unknown JSON fields. Omi may add optional metadata without changing an endpoint. A dated public-contract change appears in the changelog and the curated OpenAPI file. Do not generate database schemas that reject a response merely because it contains a new optional field.
7. Avoid avoidable network waits
Keep one HTTP client alive across uploads and job polling. For Python, use requests.Session() or a persistenthttpx.Client; reuse the SDK client rather than creating it for each request. Close the client when your application shuts down. This reuses TCP and TLS connections instead of opening them again for every call.
If you know the spoken language, send languageexplicitly. For live audio, keep the websocket open across turns.
An existing compressed recording can reduce upload bytes. Lossless FLAC preserves the audio samples. English Medical recordings up to 15 minutes can complete inline with an explicit English language, a text or JSON response, and no speakers, dictionary, vocabulary, context, webhook or idempotency key. Send FLAC as audio/flac. Capacity and file preparation may still select a job, so handle both 200 and 202 and measure time to the completed result. Do not convert to a lossy format merely to chase a latency number.