Callsistdocs
Producción

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" }   // ⛔ 400

webhook_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 cancelled se 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 type y no des por hecho que verás cada código.

On this page