# Transcripción de audio

Convierte notas de voz en texto con el SDK, en una sola llamada para audios cortos o como tarea en segundo plano con progreso para los largos.

La transcripción convierte en texto una nota de voz: la persona explica su problema hablando y tu aplicación recibe el texto para rellenar un formulario o un mensaje. Hay tres formas de pedirla, según lo largo que sea el audio y si quieres enseñar el progreso. Funciona con la clave de tu aplicación externa, siempre desde tu servidor.

Todas exigen el scope `support:tickets:ai`, además del aplicativo Tickets de soporte y la capacidad «Tickets propios» en el plan. El audio va en base64, con su nombre y extensión (`webm`, `mp4`, `mp3` o `wav`).

**Tres formas de transcribir**

## En una llamada (transcribe)

Para notas de voz breves, de menos de 30 segundos. Espera y devuelve el texto. Hasta 10 MB.

## En tiempo real (streamTranscription)

Para audios largos con barra de progreso. Encola la tarea y recibe el progreso y el texto por eventos.

## Paso a paso (requestTranscription)

Encolas la tarea y consultas tú su estado con getTranscriptionStatus.

_En Python, los mismos métodos en snake_case: transcribe, stream_transcription, request_transcription y get_transcription_status._

Si además quieres que la IA proponga el ticket, no hace falta transcribir antes: manda el audio directamente a las [sugerencias con IA](https://os.utilia.ai/docs/developers/sdk/ai-suggestions) y el resultado traerá la transcripción y la propuesta.

## En una llamada

`transcribe` espera a tener el texto y lo devuelve: `{ transcription }` en JavaScript y el texto directamente en Python.

> **Atención: Máximo de 10 MB**
>
> La llamada directa rechaza los audios de más de 10 MB con un 400 y `errorCode` `AI_AUDIO_TOO_LARGE`. Para archivos mayores usa la tarea en segundo plano, cuyo límite es el de la petición: 50 MB de cuerpo, teniendo en cuenta que el audio en base64 ocupa un tercio más que el original.

### JavaScript

```typescript
import { readFile } from 'node:fs/promises';

const audioBase64 = (await readFile('./nota-de-voz.webm')).toString('base64');

const { transcription } = await sdk.ai.transcribe(audioBase64, 'nota-de-voz.webm');
console.log(transcription);
```

### Python

```python
import base64
from pathlib import Path

audio_b64 = base64.b64encode(Path("nota-de-voz.webm").read_bytes()).decode()

transcription = await sdk.ai.transcribe(audio_b64, "nota-de-voz.webm")
print(transcription)
```

## En tiempo real

`streamTranscription` encola la tarea, abre una conexión de eventos (Server-Sent Events) y devuelve el texto cuando termina: `{ transcription }` en JavaScript y el texto en Python. A diferencia de las sugerencias, no ofrece cancelación. Opciones: función de progreso (`onProgress`, `on_progress`), función para errores no fatales (`onError`, `on_error`) y tiempo máximo, 120 s por defecto (`timeout`, en milisegundos en JavaScript y en segundos en Python con `SseStreamOptions`).

En Node.js necesitas un polyfill de `EventSource` (por ejemplo, `eventsource` asignado a `globalThis.EventSource`). La clave de tu aplicación no viaja en la dirección (4.2.0): el SDK de JavaScript abre la conexión con un pase de un solo uso y el de Python manda la clave en la cabecera. Aun así, usa estos métodos solo desde tu servidor.

### JavaScript

```typescript
const { transcription } = await sdk.ai.streamTranscription(audioBase64, 'reunion.webm', {
  onProgress: (p) => console.log(`Transcribiendo: ${p}%`),
  timeout: 180000, // 3 minutos para audios largos
});
```

### Python

```python
from utilia_sdk import SseStreamOptions

transcription = await sdk.ai.stream_transcription(
    audio_b64,
    "reunion.webm",
    SseStreamOptions(on_progress=lambda p: print(f"Transcribiendo: {p}%"), timeout=180.0),
)
```

## Paso a paso

`requestTranscription` encola la tarea y devuelve su identificador (`{ jobId }` en JavaScript, el texto del identificador en Python). `getTranscriptionStatus` devuelve el estado (`pending`, `processing`, `completed` o `failed`), el progreso de 0 a 100 y, al terminar, `transcription` o `error`.

### JavaScript

```typescript
const { jobId } = await sdk.ai.requestTranscription(audioBase64, 'entrevista.mp3');

let status = await sdk.ai.getTranscriptionStatus(jobId);
while (status.status === 'pending' || status.status === 'processing') {
  await new Promise((r) => setTimeout(r, 1000));
  status = await sdk.ai.getTranscriptionStatus(jobId);
}

if (status.status === 'completed') console.log(status.transcription);
else console.error(status.error);
```

### Python

```python
import asyncio

job_id = await sdk.ai.request_transcription(audio_b64, "entrevista.mp3")

status = await sdk.ai.get_transcription_status(job_id)
while status.status in ("pending", "processing"):
    await asyncio.sleep(1)
    status = await sdk.ai.get_transcription_status(job_id)

if status.status == "completed":
    print(status.transcription)
else:
    print(status.error)
```

Si consumes los eventos sin el SDK, ten en cuenta que todos llegan con el tipo genérico `message` y el tipo real va en el campo `event` del JSON. El formato está en [Endpoints de transcripción](https://os.utilia.ai/docs/developers/api/ticket-transcription).

## Siguiente paso

- [Sugerencias con IA](https://os.utilia.ai/docs/developers/sdk/ai-suggestions): Del audio a un ticket propuesto en un solo paso.
- [Tickets de soporte](https://os.utilia.ai/docs/developers/sdk/tickets): Abre el ticket con el texto transcrito.
