Transcripción en Make.com: el pipeline basado en webhooks

Un trabajo de transcripción tarda minutos en terminar. Un escenario de Make se factura por ejecución de módulo. Esos dos hechos tiran en direcciones opuestas, y resolver esa tensión es de lo que trata realmente este tutorial.


El diseño obvio —enviar un archivo y luego esperar, comprobar, esperar, comprobar hasta que esté listo— no cuesta nada en un servidor que alojas tú mismo. En Make no es gratis. Cada espera y cada comprobación de estado es una operación, y un trabajo de cuatro minutos puede consumir treinta sin hacer otra cosa que preguntar «¿ya está listo?».


Así que lo vamos a construir al revés. Inwista avisa a Make en el momento en que el trabajo termina, y el escenario que recibe ese aviso tiene solo tres módulos.


Al final tendrás:


  1. Un escenario receptor que se despierta cuando una transcripción está lista
  2. Un escenario emisor que entrega los archivos a Inwista y luego se detiene
  3. Verificación de firma, para que el receptor solo confíe en eventos auténticos
  4. Extras opcionales: traducciones en paralelo, deduplicación y purga de retención


Todo funciona sobre la API pública Inwista v1 a través del módulo HTTP estándar de Make. Sin aplicaciones a medida, sin código.

Antes de empezar

Necesitas tres cosas:


  • Una cuenta de Make: el plan gratuito sirve para construir esto, aunque su tope de operaciones se queda corto para producción
  • Una clave de API de Inwista: créala en Mi espacio de trabajo → Claves API. Empieza por inw_live_
  • Archivos accesibles para la API: la API espera una URL https pública, así que los archivos en Dropbox, Google Drive, S3 o tu CMS necesitan un enlace compartible o firmado


No necesitas alojar ningún endpoint de webhook. Make te da la URL.


Una nota sobre costes antes de construir algo que se ejecute sin supervisión: la transcripción se factura a 4 créditos por minuto iniciado de material, y se cobra cuando se acepta el trabajo. Los trabajos fallidos se reembolsan automáticamente. Apunta el escenario a un par de archivos de prueba cortos antes de apuntarlo a tu archivo histórico.

Por qué aquí importa la arquitectura

Los dos diseños funcionan. Simplemente cuestan cantidades muy distintas, y en Make esa diferencia se acumula cada mes.


Un escenario con polling para un trabajo que termina en unos cuatro minutos, comprobando cada veinte segundos, tiene más o menos esta pinta: un envío, luego doce rondas de espera más comprobación de estado más router, y por último descarga y entrega. Cuéntalo como 39 operaciones por archivo.


La versión con webhook se divide en dos escenarios. El emisor es un disparador y una llamada HTTP. El receptor es un webhook, un parseo, una descarga y una entrega. Cuéntalo como 6 operaciones por archivo.


Con 200 grabaciones al mes eso son 7.800 operaciones frente a 1.200: la diferencia entre subir de plan y un error de redondeo. También elimina los dos modos de fallo que castigan a los escenarios con polling en producción: una ejecución lo bastante larga como para chocar con el techo de 40 minutos de Make, y un trabajo atascado que da vueltas en silencio toda la noche.


Construye primero el receptor. Es la parte que tiene que estar bien.

Paso 1: crea el receptor

Escenario nuevo. Añade un módulo, elige Webhooks → Custom webhook, haz clic en Add, ponle un nombre como inwista-transcripts y copia la URL que te da Make.


Antes de salir de ese cuadro de diálogo, abre los ajustes avanzados del webhook y activa JSON pass-through.


Este es el ajuste que todo el mundo se salta, y merece la pena entenderlo en lugar de copiarlo sin más. Inwista firma cada envío con un HMAC calculado sobre los bytes en bruto del cuerpo de la petición. Si Make parsea el JSON por ti, esos bytes exactos desaparecen —incluidos el orden de las claves y los espacios— y ya no puedes reproducir la firma. El pass-through te entrega el cuerpo intacto como una única cadena: te cuesta un módulo extra más adelante para parsearlo, y te da la posibilidad de verificar algo.


Ahora pega esa URL en Inwista, en Mi espacio de trabajo → Integraciones, suscríbela a transcript.completed y copia la clave de firma que te muestra.

Paso 2: verifica la firma

Haz clic derecho en la conexión que sale del webhook y añade un filtro. La función sha256() de Make calcula un HMAC en cuanto le pasas una clave, así que toda la comprobación cabe en una sola expresión: sin módulo de criptografía, sin código.


Condición, Text: Equal to:

{{1.headers.x-inwista-signature}}

sha256={{sha256(1.data; "hex"; "your_signing_secret")}}


Dos detalles que, si los pasas por alto, te costarán una tarde. Make expone los nombres de las cabeceras entrantes en minúsculas, así que es x-inwista-signature, no la forma con mayúsculas que verás en nuestra documentación. Y el valor de la cabecera lleva el prefijo sha256=, así que o lo añades como arriba o lo quitas antes de comparar.


Todo lo que no pase el filtro simplemente se detiene. Una petición sin firmar nunca llega a los módulos que hacen trabajo.


Añade un módulo JSON → Parse JSON después del filtro, apuntando a 1.data. A partir de ahí la carga útil se comporta como cualquier otro bundle de Make.

Paso 3: descarga y entrega

El evento ya contiene todo lo que necesitas:

{
  "event": "transcript.completed",
  "id": "evt_...",
  "timestamp": 1786902819,
  "workspaceId": "...",
  "project": {
    "id": "...",
    "name": "board-meeting-august.mp4",
    "language": "en",
    "durationSeconds": 3184
  },
  "files": [
    { "format": "srt", "name": "board-meeting-august.srt", "url": "https://...", "expiresAt": 1786989219 }
  ]
}


Añade HTTP → Get a file y mapea la URL a {{2.files[1].url}}.


Ese [1] no es una errata. Los arrays de Make empiezan en 1, y escribir [0] por costumbre devuelve un valor vacío en lugar de un error, que luego viaja en silencio aguas abajo y aparece tres días después como un archivo de cero bytes en Drive.


Esas URL están firmadas y son válidas durante 24 horas. Descarga el archivo; no guardes el enlace.


Después conecta lo que signifique «terminado» para tu equipo: Google Drive, Dropbox, S3, Slack, una llamada HTTP a tu CMS, una fila en Airtable o Notion.


Tres módulos y un filtro. Ese es el receptor entero, y gestiona todos los trabajos completados del espacio de trabajo, incluidas las grabaciones que tus compañeros suben a mano desde el panel, de las que ningún escenario con polling se habría enterado jamás.

Paso 4: el emisor

Segundo escenario. Empieza por el evento que signifique «hay algo nuevo»: un módulo de vigilancia de Google Drive o Dropbox, un Custom webhook desde tu propio CMS, o una consulta programada sobre una base de datos. Para probar, ejecútalo a mano.


Su única función es producir una URL accesible públicamente. Añade HTTP → Make a request:


  • URL: https://api.inwista.ai/v1/transcriptions
  • Método: POST
  • Cabeceras: Authorization: Bearer inw_live_your_key_here
  • Cabeceras: Idempotency-Key: {{md5(1.fileUrl)}}
  • Tipo de cuerpo: Raw, tipo de contenido JSON


{
  "source_url": "{{1.fileUrl}}",
  "language": "en",
  "diarization": true,
  "metadata": { "source": "make", "scenario": "{{1.folderName}}" }
}


Tres campos que conviene entender:


  • language es el idioma hablado en el archivo, no el idioma que quieres obtener. Transcribir en el idioma original es lo que produce marcas de tiempo precisas y texto limpio; la traducción llega después, sobre la transcripción terminada. Si tu pipeline maneja varios idiomas, mapea la carpeta del disparador a este campo.


  • diarization activa las etiquetas de hablante. Déjalo desactivado para contenido con una sola voz: añade un tiempo de procesamiento que no necesitas.


  • metadata es tuyo. Hasta 1 KB de lo que quieras, devuelto literalmente en cada lectura y en el webhook, que es como el receptor sabe a qué escenario, curso o expediente pertenece un archivo sin tener que consultar nada.


Fíjate de dónde sale la clave de idempotencia. Derivarla de la ejecución te protege frente a un reintento de ese módulo concreto. Derivarla del archivo, como arriba, te protege además de que la misma grabación se envíe dos veces desde dos ejecuciones distintas: la forma mucho más habitual de pagar dos veces sin querer.


La respuesta llega de inmediato con status: "processing" y un id. El escenario termina ahí. Eso es el diseño funcionando, no el diseño fallando.

Lanzar traducciones en paralelo con un Iterator

Una grabación convertida en seis idiomas es donde el manejo de arrays de Make se gana el sueldo.


En el receptor, una vez que llega la transcripción, añade un Tools → Set variable con tu lista de idiomas de destino, luego un Iterator que la recorra, y después un único módulo HTTP dentro del bucle:

POST https://api.inwista.ai/v1/transcriptions/{{2.project.id}}/translate

{ "target_language": "{{4.value}}" }


Cada llamada devuelve 202 Accepted. La traducción se ejecuta sobre la transcripción terminada, así que los tiempos ya son correctos y solo cambia el texto. Seis idiomas cuestan seis operaciones, y los archivos terminados llegan por el mismo webhook que ya has construido.


Cuando vayas a descargarlos, pídelos explícitamente:

GET /v1/transcriptions/{id}/captions?format=srt&language=no


El parámetro de idioma es estricto. Pedir un idioma sin traducción terminada devuelve un error explícito en lugar de entregarte discretamente el idioma original, que es exactamente lo que quieres en un pipeline que nadie vigila.

Subtítulos con calidad broadcast

La transcripción en bruto es literal. Los subtítulos son un oficio: longitud de las líneas, velocidad de lectura, dónde se parte una frase entre dos rótulos.


Un módulo HTTP más, POST /v1/transcriptions/{id}/enhance, pasa la transcripción por ese tratamiento: condensación del texto, reequilibrio de los saltos de línea, formato de los diálogos y control de la duración de los bloques:

{
  "settings": {
    "maxLinesPerBlock": "2",
    "maxCharactersPerLine": 42,
    "textCondensation": "smart",
    "speakerDialogueFormat": "hyphens",
    "gapBetweenBlocks": "broadcasting"
  }
}


Después, los endpoints de subtítulos sirven automáticamente la versión mejorada. Nada cambia aguas abajo.

Cuando algo falla: las rutas de error de Make

Haz clic derecho en cualquier módulo y elige Add error handler. Make ofrece directivas que una rama IF genérica no puede expresar:


  • Break: aparca la ejecución en Incomplete Executions para que corrijas la causa y reintentes ese bundle exacto. Ponlo en el módulo de envío.
  • Ignore: lo registra y sigue. Adecuado para un paso de entrega prescindible.
  • Resume: sustituye por un valor de respaldo y continúa.
  • Rollback: deshace el trabajo confirmado en módulos transaccionales.


Todos los fallos de Inwista devuelven la misma estructura:

{ "error": { "code": "insufficient_credits", "message": "..." } }


Ramifica por code, nunca por el texto del mensaje. Los códigos solo se añaden dentro de v1 y nunca se renombran; los mensajes, en cambio, pueden reescribirse en cualquier momento.


Por nuestra parte, la entrega del webhook se reintenta tres veces, y un endpoint que falla de forma repetida se desactiva automáticamente, con el motivo visible en tus ajustes de integración: un receptor roto aparece como un estado que puedes ver, en lugar de eventos que desaparecen en silencio.

No transcribir dos veces el mismo archivo

Los disparadores de carpeta vigilada se vuelven a disparar. Los archivos se renombran. Alguien vuelve a subir una grabación.


Make tiene una respuesta nativa: el Data store. Crea uno indexado por el ID del archivo de origen y, en el emisor, añade Data store → Get a record antes de la llamada HTTP, filtra por que el registro no exista, y Add a record tras un envío correcto.


Dos operaciones para no pagar una transcripción duplicada. La clave de idempotencia cubre los reintentos de un módulo; el data store cubre todo lo demás.

Grabaciones sensibles

Si tu pipeline procesa material que prefieres que no conservemos —entrevistas médicas, grabaciones jurídicas, reuniones internas—, añade un campo al envío:

{
  "source_url": "{{1.fileUrl}}",
  "language": "en",
  "retention": "none"
}


Con retention: "none", el material original se elimina en cuanto termina la transcripción. La transcripción, los subtítulos, las traducciones y las mejoras posteriores siguen funcionando: solo desaparecen el audio y el vídeo. También existe store_media: false, que conserva el archivo para procesarlo pero no genera copias de reproducción.


En el otro extremo del ciclo de vida, DELETE /v1/transcriptions/{id} borra todo lo relativo a un trabajo en una sola llamada. Un escenario programado que lea de ese mismo data store los identificadores más antiguos que tu ventana de conservación y los elimine son cuatro módulos, y convierte tu política de retención en algo que puedes demostrar en lugar de describir.

Cuándo el polling sigue siendo la respuesta correcta

Hay tres casos que lo justifican de verdad: no puedes exponer un webhook, necesitas la transcripción dentro de la misma ejecución para responder a una petición síncrona, o estás haciendo una carga masiva puntual en la que el número de operaciones da igual.


En ese caso, añade un módulo Tools → Sleep y una comprobación de estado contra GET /v1/transcriptions/{id}, y respeta dos techos: Sleep está limitado a 300 segundos por módulo, y la ejecución de un escenario a 40 minutos. Consulta cada 20 o 30 segundos en lugar de cada cinco: la respuesta incluye un campo progress que refleja la posición real dentro del pipeline, así que un bucle más lento te sigue dando algo honesto que mostrar.


Si prefieres ver ese patrón desarrollado a fondo, nuestra versión de este tutorial con n8n usa polling de principio a fin, porque en un servidor autoalojado el bucle es gratis.

Cosas que acabarán mordiéndote

Los arrays empiezan en 1. files[1] es el primer archivo. files[0] devuelve vacío en lugar de fallar.


Pass-through y parseo son un intercambio. No puedes verificar una firma contra un cuerpo que Make ya ha parseado. Pass-through más un módulo Parse JSON es el único orden correcto.


Límites de peticiones. 300 lecturas y 60 escrituras por minuto y por clave. Generoso para un uso normal, fácil de alcanzar si repartes un archivo histórico entero entre ejecuciones paralelas. Procesa las cargas masivas por lotes.


Las URL de origen deben ser accesibles. La API sondea el material antes de aceptar el trabajo: así se conocen la duración y el coste por adelantado. Un enlace de Drive que exige inicio de sesión devuelve unreadable_source, y no se cobra nada. Usa URL directas o firmadas.


Todo cuenta. Los módulos Sleep, los routers, los ciclos del iterator y los filtros que dejan pasar consumen operaciones. Cuando un escenario te parezca caro, cuenta los módulos antes de culpar a la API.

El receptor terminado

Aquí tienes el esqueleto del blueprint. Impórtalo y luego engancha tu propio webhook y tu módulo de entrega, y usa el gestor de conexiones de Make en lugar de pegar una clave dentro de un módulo.

{
  "name": "Inwista — transcript receiver",
  "flow": [
    {
      "id": 1,
      "module": "gateway:CustomWebHook",
      "version": 1,
      "parameters": { "hook": 0, "maxResults": 1 },
      "mapper": {},
      "metadata": { "designer": { "x": 0, "y": 0 } }
    },
    {
      "id": 2,
      "module": "json:ParseJSON",
      "version": 1,
      "parameters": { "type": 0 },
      "mapper": { "json": "{{1.data}}" },
      "metadata": { "designer": { "x": 300, "y": 0 } }
    },
    {
      "id": 3,
      "module": "http:ActionGetFile",
      "version": 3,
      "parameters": {},
      "mapper": { "url": "{{2.files[1].url}}", "serializeUrl": false },
      "metadata": { "designer": { "x": 600, "y": 0 } }
    }
  ],
  "metadata": {
    "instant": true,
    "version": 1,
    "scenario": { "roundtrips": 1, "maxErrors": 3, "autoCommit": true },
    "designer": { "orphans": [] }
  }
}


Tres módulos. El equivalente con polling tenía once, y costaba seis veces más ejecutarlo.

Lo que esto cambia de verdad

La medida de una automatización no es lo que hace mientras la estás mirando. Es lo que hace a las dos de la madrugada de un domingo, cuando llega una grabación de noventa minutos y no hay nadie despierto.


Un escenario que se ejecuta durante cuatro minutos por archivo, quemando operaciones para hacer una pregunta cuya respuesta ya conoce, acaba siendo algo que revisas. Un receptor que se despierta, verifica una firma, escribe un archivo y se vuelve a dormir es algo de cuya existencia te olvidas, y olvidarlo es precisamente el objetivo.


Llegados a ese punto, los subtítulos dejan de ser una tarea de la que alguien se encarga. Pasan a ser una propiedad de cada grabación que produce tu organización: buscable, accesible, conforme, y sin que nadie haya tenido que convocar una reunión por ello.


¿Listo para construirlo? Crea una clave de API: el plan gratuito basta para ejecutar todo esto de principio a fin. La referencia completa de endpoints está en la documentación de la API.