[![Omnimed AI](/assets/omnimed-logo-DVQKYy1K.webp)](/)

ESCopiar

En esta página

[Descripción general](#descripcion-general) [Autenticación](#autenticacion) [Widget](#widget) [Embeber](#embeber) [Autoinsertar el informe](#autoinsertar-el-informe) [Ciclo de vida del widget](#ciclo-de-vida-del-widget) [Requisitos del navegador](#requisitos-del-navegador) [API directa](#api-directa) [Generar un informe](#generar-un-informe) [Refinar un informe](#refinar-un-informe) [Respuesta de error](#respuesta-de-error) [Resaltar los cambios de la IA (diff en el cliente)](#resaltar-los-cambios-de-la-ia-diff-en-el-cliente) [Health check](#health-check) [Soporte](#soporte)

# Guía de integración de Omni-AI

Integrá la transcripción médica de Omni-AI en tu producto en minutos.

## 1. Widget

Agregá un tag `<script>` a tu página. Se inyecta un botón flotante de grabación; el informe generado puede autoinsertarse en el editor activo.

**Usá esta opción si**: querés la integración más rápida y te sirve la interfaz de grabación por defecto.

## 2. API directa

Enviá un archivo de audio por POST a `/api/omni-ai` y manejá la respuesta vos mismo.

**Usá esta opción si**: necesitás control total de la interfaz de grabación o integrás desde un cliente nativo o de escritorio.

**URL base**: `https://api.omniai.com.ar`

## Descripción general

Omni-AI convierte un dictado médico (audio) en un informe estructurado basado en plantilla, en una sola llamada. Hay dos formas de integrarse:

1. Widget embebible — agregá un tag `<script>` a tu página. El médico graba desde un botón flotante, el informe se devuelve y puede autoinsertarse en tu editor.

2. API directa — llamá a `POST https://api.omniai.com.ar/api/omni-ai` con el archivo de audio y manejá la respuesta vos mismo.

Ambas vías usan el mismo endpoint omni-ai por detrás. Empezá por el Widget salvo que necesites control total de la interfaz de grabación.

## Autenticación

Cada partner de integración recibe un `medical_center_id` (provisto por Omni). El ID del centro identifica las plantillas, el idioma y el estilo de informe usados para generar el resultado. Hoy no se requiere bearer token.

Escribí a [omni@omniai.com.ar](mailto:omni@omniai.com.ar) para solicitar un ID de centro.

## Widget

### Embeber

```
<script src="https://api.omniai.com.ar/static/widget/v1/omni-widget.min.js"
        data-center-id="YOUR_CENTER_ID"
        async></script>
```

Se inyecta un botón de grabación flotante abajo a la derecha. El médico lo toca, dicta, frena, y el widget muestra el informe generado.

### Autoinsertar el informe

El widget emite un evento `omniai:report` cuando el médico hace clic en "Insertar". Escuchalo y pegá el HTML (o el texto plano) en tu editor:

```
window.addEventListener('omniai:report', (event) => {
  const { html, text } = event.detail;
  // p. ej. pegar en el campo contenteditable / rich-text con foco
  document.execCommand('insertHTML', false, html);
});
```

O inicializalo manualmente con un callback:

```
OmniWidget.init({
  centerId: 'YOUR_CENTER_ID',
  onReport: ({ html, text }) => {
    // Insertar en tu editor de informes
  }
});
```

### Ciclo de vida del widget

Estado

Significado

idle

Listo para grabar

recording

Micrófono activo

paused

Grabación en pausa

processing

Audio subido, generando informe

result

Informe visible en el panel

### Requisitos del navegador

- HTTPS (requerido para acceder al micrófono)

- Permiso otorgado para `navigator.mediaDevices.getUserMedia`

- Duración máxima de grabación: 10 minutos (configurable)

## API directa

### Generar un informe

```
POST https://api.omniai.com.ar/api/omni-ai
Content-Type: multipart/form-data
```

#### Parámetros

Parámetro

Tipo

Requerido

Descripción

file

File

Condicional

Archivo de audio (wav, mp3, m4a, ogg, flac, webm). Requerido salvo que se envíe `transcription_text`

transcription_text

String

Condicional

Texto del dictado usado en lugar del audio (ver abajo). Requerido salvo que se envíe `file`

medical_center_id

String

Sí

Identificador del centro provisto por Omni

user_id

String

No

Identificador del médico/usuario para analítica

language

String

No

Código de idioma (default spa)

metadata

String (JSON)

No

Metadata JSON libre, máx 10KB

dicom_metadata

String (JSON)

No

Metadata DICOM como fallback del estudio

template_html

String (HTML)

No

Plantilla preseleccionada: generar el informe contra exactamente esta plantilla (ver abajo)

template_name

String

No

Etiqueta de la plantilla preseleccionada; se devuelve como `study_name` / `template_used`

include_template

Boolean

No

Devolver el HTML de la plantilla que usó el servidor, para resaltar diferencias

include_rtf

Boolean

No

Devolver versión RTF de cada informe

**Nota**: Sin `template_html`, la detección del estudio es automática a partir de la transcripción y la plantilla se matchea contra las plantillas guardadas del centro — no necesitás enviar el nombre del estudio.

#### Enviar texto en lugar de audio (`transcription_text`)

Si tu sistema ya tiene el dictado como texto (tipeado, o transcripto de tu lado), envialo como `transcription_text` y omití el audio — se saltea el paso de transcripción y el texto entra al pipeline exactamente donde entraría la transcripción: la detección del estudio, el matching de plantillas y la generación se comportan igual. Se combina naturalmente con `template_html` (sin transcripción *y* sin matching — el camino más rápido y totalmente determinístico).

- **El audio siempre gana.** Si enviás ambos, se transcribe el audio y `transcription_text` se ignora por completo. No hay fallback: si el audio falla al transcribirse (p. ej. micrófono muteado) la llamada da error aunque hayas enviado texto.

- Un campo `file` en blanco o un `transcription_text` en blanco ("" o espacios) se trata como ausente. No enviar ninguno devuelve un 400.

- Texto plano, máx **50KB**.

- En la respuesta: `duration_seconds` es `0.0`, `speaker_count` es `1`, `language_detected` refleja tu parámetro `language`, y `metadata.input_source` es `"text"` (vs `"audio"`).

- La facturación no cambia — un informe generado desde texto consume crédito exactamente igual que uno generado desde audio.

#### Enviar tu propia plantilla (`template_html`)

Cuando tu sistema sabe qué plantilla debe seguir el informe (por clínica, por centro derivador, por modalidad), enviala en la llamada:

- La detección del estudio y el matching de plantillas se **saltean por completo** — el informe sigue tu plantilla de forma determinística.

- El resultado es siempre un **único informe**, aunque el dictado mencione varios estudios.

- El HTML se sanitiza del lado del servidor antes de usarse; el tamaño máximo es **100KB**. HTML demasiado grande, o markup que queda vacío tras la sanitización, devuelve un 400. Un campo en blanco ("" o espacios) se trata como ausente: la llamada cae al camino normal de matching.

- HTML es el formato recomendado: preserva negritas, títulos y estructura de bloques.

- En la respuesta: `template_match_confidence` es `1.0` y `metadata.detection_source` es `"preselected_template"`.

- Con `include_template=true`, el `template_html` de la respuesta es la plantilla sanitizada que el servidor usó realmente — hacé el diff contra esa, no contra tu original.

#### Respuesta exitosa

```
{
  "success": true,
  "message": "Successfully processed audio and generated 1 report(s)",
  "data": {
    "transcription": "...",
    "language_detected": "spa",
    "duration_seconds": 42.1,
    "speaker_count": 1,
    "reports": [
      {
        "study_name": "IRM de Rodilla",
        "template_used": "IRM de Rodilla",
        "template_match_confidence": 1.0,
        "report_html": "<p>...</p>",
        "report_rtf": null,
        "transcription_segment": "...",
        "template_html": null
      }
    ],
    "processing_time_ms": 7441,
    "metadata": {
      "study_id": 12345,
      "detection_source": "preselected_template",
      "...": "..."
    }
  }
}
```

**Tip**: Guardá `data.metadata.study_id` si planeás refinar este informe más adelante.

#### Ejemplos

**cURL**

```
curl -X POST https://api.omniai.com.ar/api/omni-ai \
  -F "file=@recording.m4a" \
  -F "medical_center_id=YOUR_CENTER_ID" \
  -F "user_id=dr_martinez" \
  -F "language=spa"
```

**cURL (plantilla preseleccionada)**

```
curl -X POST https://api.omniai.com.ar/api/omni-ai \
  -F "file=@recording.m4a" \
  -F "medical_center_id=YOUR_CENTER_ID" \
  -F "template_html=<h2>IRM de Rodilla</h2><p>...</p>" \
  -F "template_name=IRM de Rodilla" \
  -F "include_template=true"
```

**Python**

```
import requests

url = "https://api.omniai.com.ar/api/omni-ai"

with open("audio.m4a", "rb") as f:
    response = requests.post(
        url,
        files={"file": f},
        data={
            "medical_center_id": "YOUR_CENTER_ID",
            "user_id": "dr_lopez",
            "language": "spa",
        },
    )

response.raise_for_status()
result = response.json()

for report in result["data"]["reports"]:
    print(report["study_name"], "->", report["report_html"])
```

**JavaScript (fetch)**

```
async function processAudio(audioFile, centerId, userId) {
  const form = new FormData();
  form.append('file', audioFile);
  form.append('medical_center_id', centerId);
  form.append('user_id', userId);
  form.append('language', 'spa');

  const res = await fetch('https://api.omniai.com.ar/api/omni-ai', {
    method: 'POST',
    body: form,
  });

  if (!res.ok) {
    const err = await res.json().catch(() => ({}));
    throw new Error(err.error || `HTTP ${res.status}`);
  }

  const { data } = await res.json();
  return data.reports[0].report_html;
}
```

### Refinar un informe

```
POST https://api.omniai.com.ar/api/omni-ai/refine
Content-Type: multipart/form-data
```

El médico dicta una corrección sobre un informe ya generado ("cambiar menisco interno por menisco externo"). El servidor transcribe el audio, cambia **solo** el elemento apuntado — más la Conclusión del informe cuando el cambio la afecta — y devuelve el informe actualizado. El refinamiento nunca regenera el informe completo, y no se factura.

#### Parámetros

Parámetro

Tipo

Requerido

Descripción

file

File

Condicional

Audio con la corrección dictada. Requerido salvo que se envíe `transcription_text`

transcription_text

String

Condicional

La corrección como texto en lugar de audio (máx 50KB, mismas reglas que en generación: el audio gana, blanco = ausente)

medical_center_id

String

Sí

Identificador del centro provisto por Omni

report_html

String (HTML)

Sí

El último informe generado, exactamente como lo devolvió la API (o como quedó tras el último refinamiento)

template_html

String (HTML)

No

La plantilla original del informe; se usa como referencia de estilo/formato de solo lectura (máx 100KB)

template_name

String

No

Nombre de la plantilla/tipo de estudio, habilita reglas por tipo de estudio

selected_html

String (HTML)

No

Elemento HTML exacto a refinar; omitilo para que el modelo ubique el objetivo desde el dictado

study_id

Integer

No

El `metadata.study_id` de la llamada de generación, para trazar el refinamiento

user_id

String

No

Identificador del médico/usuario para analítica

language

String

No

Código de idioma (default spa)

#### Respuesta exitosa

```
{
  "success": true,
  "message": "Successfully refined report",
  "data": {
    "transcription": "cambiar menisco interno por menisco externo",
    "report_html": "<p>...informe completo con la corrección aplicada...</p>",
    "patch": {
      "refined_text": "<p>Menisco externo sin alteraciones.</p>",
      "target_html": "<p>Menisco interno sin alteraciones.</p>",
      "conclusion_text": null,
      "conclusion_original_text": null
    },
    "processing_time_ms": 3120,
    "metadata": {
      "medical_center_id": "your-center",
      "study_id": 12345,
      "event_id": "678",
      "model_used": "gemini-2.5-flash",
      "...": "..."
    }
  }
}
```

- `data.report_html` es el informe completo con la corrección ya aplicada — usalo directamente.

- `data.patch` describe las regiones exactas que cambiaron, si preferís empalmarlas en tu propio editor: reemplazá la primera ocurrencia de `target_html` por `refined_text`, y la de `conclusion_original_text` por `conclusion_text` (cuando no sea null).

- Para encadenar refinamientos, enviá el `report_html` devuelto como entrada de la siguiente llamada de refine.

#### Ejemplo

```
curl -X POST https://api.omniai.com.ar/api/omni-ai/refine \
  -F "file=@correction.wav" \
  -F "medical_center_id=YOUR_CENTER_ID" \
  -F "report_html=<p>...último informe generado...</p>" \
  -F "template_html=<p>...plantilla original...</p>" \
  -F "study_id=12345"
```

### Respuesta de error

Los errores devuelven un status no-2xx con:

```
{
  "success": false,
  "error": "Human-readable error message",
  "error_code": 400
}
```

Código

Causa

Solución

400

Falta la entrada (ni file ni transcription_text), formato de audio no soportado, metadata JSON malformada, o template_html demasiado grande

Enviá wav/mp3/m4a/ogg/flac/webm o transcription_text (máx 50KB); validá el JSON bajo 10KB; mantené template_html bajo 100KB

403

Centro deshabilitado / no provisionado

Contactá a Omni para habilitar tu `medical_center_id`

500

Falló la transcripción o la generación del informe

Reintentá con backoff; contactá a soporte si persiste

## Resaltar los cambios de la IA (diff en el cliente)

El Widget resalta lo que cambió la IA haciendo un diff entre la plantilla y el informe generado. Podés hacer lo mismo en tu interfaz con [jsdiff](https://github.com/kpdecker/jsdiff) (`diff` en npm) — sin soporte del servidor:

```
import { diffWords } from 'diff';

function highlightChanges(templateText, reportText) {
  return diffWords(templateText, reportText)
    .map((part) =>
      part.added
        ? `<mark class="ai-change">${part.value}</mark>`
        : part.removed
          ? ''
          : part.value
    )
    .join('');
}
```

- Línea base: pedí `include_template=true` en la generación y hacé el diff contra el `template_html` devuelto.

- Después de un refine, hacé el diff entre el `report_html` anterior y el nuevo para resaltar solo la corrección.

- Hacé el diff sobre el texto visible (p. ej. vía `DOMParser`) en lugar de strings HTML crudos para resaltados más limpios.

## Health check

Usá este endpoint para verificar conectividad desde tu backend:

```
curl https://api.omniai.com.ar/api/health
```

Respuesta:

```
{
  "status": "healthy",
  "services": {
    "api": {
      "service": "api",
      "status": "healthy",
      "available": true
    }
  }
}
```

## Soporte

Consultas y aprovisionamiento: [omni@omniai.com.ar](mailto:omni@omniai.com.ar)

---

Source: https://www.omniai.com.ar/api-documentation
Site index for agents: https://www.omniai.com.ar/llms.txt
Sitemap: https://www.omniai.com.ar/sitemap.xml
OpenAPI: https://www.omniai.com.ar/openapi.json
