Trampas conocidas
Lo que la API acepta y no hace. Leerlo antes ahorra una tarde.
Todo lo de aquí está verificado leyendo el código, no deducido. Son cosas que la API acepta sin protestar y que no hacen lo que su nombre promete. Están ordenadas por lo que cuesta descubrirlas tarde.
1. No hay webhook por petición
{ "audio_url": "…", "webhook_url": "https://mi-app/callsist" } // ⛔ 400webhook_url, webhook_auth_header y webhook_include_result se rechazan con
400, con un mensaje que apunta a
POST /v1/webhooks.
Hasta hace poco se aceptaban, devolvían 202 y no los leía nadie: el aviso no salía
nunca y el cliente lo daba por configurado. Aceptar algo que no se hace es peor que no
aceptarlo.
Qué hacer: registrar un endpoint, o sondear. Para un lote propio, sondear es más simple.
2. Con redacción de PII, el texto de primer nivel sigue en claro
text y utterances nunca se redactan. La versión tapada vive en la clave pii.
Es deliberado —quien no pide redacción quiere su transcripción entera— pero si la pides
y sigues leyendo text, estás procesando datos personales en claro creyendo que no.
Qué hacer: en cuanto pii !== null, usa pii.text_redacted y
pii.utterances_redacted. Detalles en Redacción de PII.
3. redact_audio: true se cobra y no silencia nada
Factura y no existe ninguna etapa que silencie el audio. El fichero vuelve idéntico.
Qué hacer: déjalo en false, que es el valor por defecto. No lo mandes.
4. Cinco parámetros se aceptan y se ignoran en silencio
El pipeline solo recibe language, language_hints, custom_vocabulary, punctuate y
format_text. speaker_labels, speakers_expected, channels, word_timestamps y
language_confidence_threshold se quedan en el camino, sin warning.
Qué hacer: no construyas lógica que dependa de ellos. La tabla completa está en Subir audio.
5. Idempotency-Key en POST /v1/files lee el fichero entero en memoria
El middleware hashea el cuerpo para detectar reintentos, y para eso lo convierte en una cadena — incluido un multipart de hasta 2 GB.
Qué hacer: mándala en POST /v1/transcripts, que es el que cobra, y no en
POST /v1/files. Una subida duplicada solo deja un fichero huérfano.
6. Idempotency-Key con stream: true mata el streaming
El middleware consume la respuesta entera para memorizarla. Llega completa de golpe al final.
Qué hacer: no los combines.
7. El saldo puede quedarse en negativo por chat y embeddings
POST /v1/transcripts estima el coste y devuelve
402 antes de procesar. Los otros dos
cobran después de responder, así que no pueden reservar: se niegan a empezar con el
saldo a cero o menos, pero una llamada en curso puede dejarlo en negativo.
En la práctica el descubierto es de céntimos, no de euros.
Qué hacer: configura el aviso de saldo bajo en
app.callsist.com/billing, o consulta
GET /v1/balance cada cierto número de llamadas.
8. Una llamada de chat puede tardar ocho minutos antes de fallar
El cliente HTTP interno tiene 120 s de timeout y hasta 3 reintentos con espera creciente. En el peor caso una petición tarda varios minutos en devolver un error, y no hay forma de acotarlo desde la petición.
Qué hacer: pon tu propio timeout de cliente (60–120 s para análisis por lotes) y trátalo como reintentable.
9. estimated_completion es siempre «ahora + 6 minutos»
Es una constante. No mira la cola ni la duración del audio.
Qué hacer: no lo uses como timeout ni como predicción. Sondea.
10. El sondeo comparte el cubo de 300 peticiones por minuto
No hay límite aparte para consultar estado. Sondear 400 transcripciones cada 10 segundos
son 2 400 peticiones por minuto: 429 inmediato.
Qué hacer: sondea solo lo que está en vuelo. Con 8 en curso y un sondeo cada 10 s son 48 peticiones por minuto.
11. speakerRole está en camelCase
En un JSON donde todo lo demás es snake_case, dentro de utterances[] y de
pii.utterances_redacted[] el campo se llama speakerRole.
Qué hacer: escribirlo bien la primera vez.
12. metrics.telemetry no es contrato
Trae instrumentación interna —tiempos y memoria por etapa— y cambia sin aviso. De
metrics, lo estable es timestamps y speaker_role_confidence.
13. understanding es {}, no null, cuando no pediste nada
Qué hacer: comprueba las claves que esperas, no si el objeto existe.
14. GET /v1/transcripts pagina por cursor, y el cursor lleva microsegundos
Pasa next_before tal cual. Recortarlo a milisegundos —lo que hace
new Date(...).toISOString() en JavaScript— se salta las filas creadas dentro de ese
mismo milisegundo. No se repite ninguna: faltan.
15. Cosas que existen en la taxonomía y no ocurren nunca
- El estado
cancelledse puede filtrar y nadie lo produce: no hay endpoint de cancelación. - Trece códigos de error están publicados y no se emiten jamás. Están marcados uno a
uno en Errores. Maneja los errores por familia de
typey no des por hecho que verás cada código.