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.
- 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.
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}
def request_with_backoff(method, url, **kwargs):
for attempt in range(5):
response = requests.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.