Transcription Make.com : le pipeline piloté par webhook
Un travail de transcription met plusieurs minutes à se terminer. Un scénario Make est facturé à l'exécution de module. Ces deux faits s'opposent, et résoudre cette tension est le véritable sujet de ce tutoriel.
La conception évidente — envoyer un fichier, puis attendre, vérifier, attendre, vérifier jusqu'à ce qu'il soit prêt — ne coûte rien sur un serveur que vous hébergez vous-même. Sur Make, elle n'est pas gratuite. Chaque attente et chaque vérification de statut est une opération, et un travail de quatre minutes peut en consommer trente rien qu'à demander « c'est bientôt fini ? ».
Nous allons donc procéder dans l'autre sens. Inwista prévient Make dès que le travail est terminé, et le scénario qui reçoit cette notification ne compte que trois modules.
À la fin, vous aurez :
- Un scénario récepteur qui se réveille dès qu'une transcription est prête
- Un scénario émetteur qui confie les fichiers à Inwista puis s'arrête
- La vérification de signature, pour que le récepteur ne fasse confiance qu'aux véritables événements
- Des ajouts facultatifs — traductions en parallèle, déduplication et purge de rétention
Tout repose sur l'API Inwista v1 publique, via le module HTTP standard de Make. Aucune application sur mesure, aucun code.
Avant de commencer
Il vous faut trois choses :
- Un compte Make — le forfait gratuit suffit pour construire ce pipeline, même si son plafond d'opérations reste faible pour la production
- Une clé API Inwista — créez-en une dans Mon espace de travail → Clés API. Elle commence par
inw_live_ - Des médias accessibles à l'API — l'API attend une URL
httpspublique ; les fichiers stockés sur Dropbox, Google Drive, S3 ou dans votre CMS ont donc besoin d'un lien partageable ou signé
Vous n'avez pas besoin d'héberger un point de terminaison webhook. Make vous fournit l'URL.
Une remarque sur les coûts avant de construire quelque chose qui tournera sans surveillance : la transcription est facturée 4 crédits par minute entamée de média, au moment où le travail est accepté. Les travaux en échec sont remboursés automatiquement. Faites pointer le scénario vers deux ou trois fichiers de test courts avant de le lancer sur vos archives.
Pourquoi l'architecture compte ici
Les deux conceptions fonctionnent. Elles coûtent simplement des sommes très différentes, et sur Make cet écart se cumule chaque mois.
Un scénario en polling pour un travail qui se termine en quatre minutes environ, avec une vérification toutes les vingt secondes, ressemble à peu près à ceci : un envoi, puis douze cycles d'attente plus vérification de statut plus routeur, puis récupération et livraison. Comptez 39 opérations par fichier.
La version webhook se divise en deux scénarios. L'émetteur, c'est un déclencheur et un appel HTTP. Le récepteur, c'est un webhook, un parsing, une récupération et une livraison. Comptez 6 opérations par fichier.
Sur 200 enregistrements par mois, cela fait 7 800 opérations contre 1 200 — la différence entre un changement de forfait et une erreur d'arrondi. Cela élimine aussi les deux modes de défaillance qui frappent les scénarios en polling en production : une exécution assez longue pour atteindre le plafond de 40 minutes de Make, et un travail bloqué qui boucle silencieusement toute la nuit.
Construisez d'abord le récepteur. C'est la partie qui doit être juste.
Étape 1 : créer le récepteur
Nouveau scénario. Ajoutez un module, choisissez Webhooks → Custom webhook, cliquez sur Add, nommez-le par exemple inwista-transcripts, et copiez l'URL que Make vous donne.
Avant de quitter cette fenêtre, ouvrez les paramètres avancés du webhook et activez JSON pass-through.
C'est le réglage que tout le monde oublie, et il vaut mieux le comprendre que le recopier. Inwista signe chaque envoi avec un HMAC calculé sur les octets bruts du corps de la requête. Si Make analyse le JSON à votre place, ces octets exacts disparaissent — ordre des clés et espaces compris — et vous ne pouvez plus reproduire la signature. Le pass-through vous livre le corps intact sous forme de chaîne unique : cela vous coûte un module supplémentaire plus loin pour l'analyser, et cela vous donne la possibilité de vérifier quoi que ce soit.
Collez maintenant cette URL dans Inwista sous Mon espace de travail → Intégrations, abonnez-la à transcript.completed, et copiez la clé de signature qui s'affiche.
Étape 2 : vérifier la signature
Faites un clic droit sur la connexion qui part du webhook et ajoutez un filtre. La fonction sha256() de Make calcule un HMAC dès que vous lui fournissez une clé : toute la vérification tient donc en une seule expression — pas de module de chiffrement, pas de code.
Condition, Text: Equal to :
{{1.headers.x-inwista-signature}}
sha256={{sha256(1.data; "hex"; "your_signing_secret")}}
Deux détails qui vous coûteront un après-midi si vous les ignorez. Make expose les noms d'en-têtes entrants en minuscules : c'est donc x-inwista-signature, et non la forme avec majuscules que vous verrez dans notre documentation. Et la valeur de l'en-tête porte le préfixe sha256= : il faut donc soit l'ajouter comme ci-dessus, soit le retirer avant de comparer.
Tout ce qui ne passe pas le filtre s'arrête simplement. Une requête non signée n'atteint jamais les modules qui font le travail.
Ajoutez un module JSON → Parse JSON après le filtre, pointé sur 1.data. À partir de là, la charge utile se comporte comme n'importe quel bundle Make.
Étape 3 : récupérer et livrer
L'événement contient déjà tout ce dont vous avez besoin :
{
"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 }
]
}
Ajoutez HTTP → Get a file et mappez l'URL sur {{2.files[1].url}}.
Ce [1] n'est pas une faute de frappe. Les tableaux de Make sont indexés à partir de 1, et écrire [0] par habitude renvoie une valeur vide plutôt qu'une erreur — qui poursuit ensuite discrètement son chemin et réapparaît trois jours plus tard sous la forme d'un fichier de zéro octet dans Drive.
Ces URL sont signées et valables 24 heures. Récupérez le fichier ; ne stockez pas le lien.
Branchez ensuite ce que « terminé » veut dire pour votre équipe — Google Drive, Dropbox, S3, Slack, un appel HTTP vers votre CMS, une ligne dans Airtable ou Notion.
Trois modules et un filtre. C'est tout le récepteur, et il gère chaque travail terminé de l'espace de travail — y compris les enregistrements que vos collègues déposent à la main dans le tableau de bord, dont aucun scénario en polling n'aurait jamais eu connaissance.
Étape 4 : l'émetteur
Deuxième scénario. Commencez par l'événement qui signifie « il y a du nouveau » : un module de surveillance Google Drive ou Dropbox, un Custom webhook depuis votre propre CMS, ou une interrogation planifiée d'une base de données. Pour les tests, lancez-le simplement à la main.
Son seul rôle est de produire une URL publiquement accessible. Ajoutez HTTP → Make a request :
- URL —
https://api.inwista.ai/v1/transcriptions - Méthode —
POST - En-têtes —
Authorization:Bearer inw_live_your_key_here - En-têtes —
Idempotency-Key:{{md5(1.fileUrl)}} - Type de corps — Raw, type de contenu JSON
{
"source_url": "{{1.fileUrl}}",
"language": "en",
"diarization": true,
"metadata": { "source": "make", "scenario": "{{1.folderName}}" }
}
Trois champs à bien comprendre :
languageest la langue parlée dans le fichier, et non celle que vous voulez obtenir. Transcrire dans la langue source est ce qui produit des horodatages précis et un texte propre ; la traduction intervient ensuite, sur la transcription terminée. Si votre pipeline gère plusieurs langues, mappez le dossier du déclencheur sur ce champ.
diarizationactive les étiquettes de locuteur. Laissez-le désactivé pour du contenu à une seule voix — il ajoute un temps de traitement dont vous n'avez pas besoin.
metadatavous appartient. Jusqu'à 1 Ko de ce que vous voulez, renvoyé tel quel à chaque lecture et dans le webhook — c'est ainsi que le récepteur sait à quel scénario, cours ou dossier appartient un fichier, sans avoir à consulter quoi que ce soit.
Notez d'où vient la clé d'idempotence. La dériver de l'exécution vous protège contre une nouvelle tentative de ce module précis. La dériver du fichier, comme ci-dessus, vous protège en plus contre le même enregistrement soumis deux fois par deux exécutions différentes — de loin la manière la plus courante de payer deux fois par accident.
La réponse arrive immédiatement avec status: "processing" et un id. Le scénario s'arrête là. C'est la conception qui fonctionne, pas la conception qui échoue.
Générer les traductions en parallèle avec un Iterator
Un enregistrement décliné en six langues : c'est là que la gestion des tableaux de Make prend tout son sens.
Dans le récepteur, une fois la transcription arrivée, ajoutez un Tools → Set variable contenant votre liste de langues cibles, puis un Iterator qui la parcourt, puis un seul module HTTP à l'intérieur de la boucle :
POST https://api.inwista.ai/v1/transcriptions/{{2.project.id}}/translate
{ "target_language": "{{4.value}}" }
Chaque appel renvoie 202 Accepted. La traduction s'exécute sur la transcription terminée : les synchronisations sont donc déjà correctes et seul le texte change. Six langues coûtent six opérations, et les fichiers terminés arrivent par le même webhook que vous avez déjà construit.
Au moment de les récupérer, demandez explicitement :
GET /v1/transcriptions/{id}/captions?format=srt&language=no
Le paramètre de langue est strict. Demander une langue dont aucune traduction n'est terminée renvoie une erreur explicite plutôt que de vous rendre discrètement la langue source — ce qui est exactement ce que vous voulez dans un pipeline que personne ne surveille.
Des sous-titres en qualité broadcast
La transcription brute est verbatim. Les sous-titres relèvent d'un savoir-faire : longueur des lignes, vitesse de lecture, endroit où une phrase se coupe entre deux cartons.
Un module HTTP de plus, POST /v1/transcriptions/{id}/enhance, fait passer la transcription par ce traitement — condensation du texte, rééquilibrage des retours à la ligne, mise en forme des dialogues, respect des durées de blocs :
{
"settings": {
"maxLinesPerBlock": "2",
"maxCharactersPerLine": 42,
"textCondensation": "smart",
"speakerDialogueFormat": "hyphens",
"gapBetweenBlocks": "broadcasting"
}
}
Ensuite, les points de terminaison de sous-titres servent automatiquement la version améliorée. Rien ne change en aval.
Quand quelque chose échoue : les routes d'erreur de Make
Faites un clic droit sur n'importe quel module et choisissez Add error handler. Make propose des directives qu'une simple branche IF ne peut pas exprimer :
- Break — met l'exécution en attente dans Incomplete Executions, pour corriger la cause puis relancer ce bundle précis. À placer sur le module d'envoi.
- Ignore — consigne l'erreur et poursuit. Adapté à une étape de livraison secondaire.
- Resume — substitue une valeur de repli et continue.
- Rollback — annule le travail validé dans les modules transactionnels.
Chaque échec Inwista renvoie la même enveloppe :
{ "error": { "code": "insufficient_credits", "message": "..." } }
Branchez sur code, jamais sur le texte du message. Les codes ne font que s'ajouter au sein de v1 et ne sont jamais renommés ; les messages, eux, peuvent être reformulés à tout moment.
De notre côté, la livraison du webhook fait l'objet de trois nouvelles tentatives, et un point de terminaison qui échoue de façon répétée est désactivé automatiquement, avec le motif indiqué dans vos paramètres d'intégration — un récepteur cassé apparaît donc comme un statut visible plutôt que comme des événements qui disparaissent en silence.
Ne pas transcrire deux fois le même fichier
Les déclencheurs de dossier surveillé se redéclenchent. Des fichiers sont renommés. Quelqu'un remet un enregistrement en ligne.
Make dispose d'une réponse native : le Data store. Créez-en un indexé sur l'identifiant du fichier source, puis, dans l'émetteur, ajoutez Data store → Get a record avant l'appel HTTP, filtrez sur l'absence de l'enregistrement, et Add a record après un envoi réussi.
Deux opérations pour éviter de payer une transcription en double. La clé d'idempotence couvre les nouvelles tentatives d'un module ; le data store couvre tout le reste.
Gérer les enregistrements sensibles
Si votre pipeline traite des contenus que vous préférez ne pas nous voir conserver — entretiens médicaux, enregistrements juridiques, réunions internes —, ajoutez un champ à l'envoi :
{
"source_url": "{{1.fileUrl}}",
"language": "en",
"retention": "none"
}
Avec retention: "none", le média source est supprimé dès la fin de la transcription. La transcription, les sous-titres, les traductions et les améliorations ultérieures continuent de fonctionner — seuls l'audio et la vidéo disparaissent. Il existe aussi store_media: false, qui conserve le fichier pour le traitement mais ne génère aucune copie de lecture.
À l'autre bout du cycle de vie, DELETE /v1/transcriptions/{id} efface tout ce qui concerne un travail en un seul appel. Un scénario planifié qui lit dans ce même data store les identifiants plus anciens que votre fenêtre de conservation et les supprime tient en quatre modules — et transforme votre politique de rétention en quelque chose que vous pouvez démontrer plutôt que décrire.
Quand le polling reste la bonne réponse
Trois cas le justifient vraiment : vous ne pouvez pas exposer de webhook, vous avez besoin de la transcription dans la même exécution pour répondre à une requête synchrone, ou vous menez une reprise ponctuelle d'archives où le nombre d'opérations n'a aucune importance.
Dans ce cas, ajoutez un module Tools → Sleep et une vérification de statut sur GET /v1/transcriptions/{id}, en respectant deux plafonds : Sleep est limité à 300 secondes par module, et l'exécution d'un scénario à 40 minutes. Interrogez toutes les 20 à 30 secondes plutôt que toutes les cinq — la réponse contient un champ progress qui reflète la position réelle dans le pipeline, une boucle plus lente vous donne donc quand même quelque chose d'honnête à afficher.
Si vous préférez voir ce schéma développé en détail, notre version n8n de ce tutoriel utilise le polling de bout en bout, parce que sur un serveur auto-hébergé la boucle est gratuite.
Ce qui finira par vous poser problème
Les tableaux sont indexés à partir de 1. files[1] est le premier fichier. files[0] renvoie une valeur vide au lieu d'échouer.
Pass-through et parsing sont un compromis. Vous ne pouvez pas vérifier une signature sur un corps que Make a déjà analysé. Pass-through puis un module Parse JSON : c'est le seul ordre correct.
Limites de débit. 300 lectures et 60 écritures par minute et par clé. Généreux pour un usage normal, vite atteint si vous déployez une archive entière sur des exécutions parallèles. Traitez les reprises par lots.
Les URL sources doivent être accessibles. L'API sonde le média avant d'accepter le travail — c'est ainsi que la durée et le coût sont connus d'avance. Un lien Drive qui exige une connexion renvoie unreadable_source, et rien n'est facturé. Utilisez des URL directes ou signées.
Tout compte. Les modules Sleep, les routeurs, les cycles d'iterator et les filtres qui laissent passer consomment tous des opérations. Quand un scénario vous paraît cher, comptez les modules avant d'accuser l'API.
Le récepteur terminé
Voici le squelette du blueprint. Importez-le, puis rattachez votre propre webhook et votre module de livraison — et utilisez le gestionnaire de connexions de Make plutôt que de coller une clé dans un module.
{
"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": [] }
}
}
Trois modules. L'équivalent en polling en comptait onze, et coûtait six fois plus cher à faire tourner.
Ce que cela change vraiment
La mesure d'une automatisation n'est pas ce qu'elle fait pendant que vous la regardez. C'est ce qu'elle fait à deux heures du matin un dimanche, quand un enregistrement de quatre-vingt-dix minutes arrive et que personne n'est réveillé.
Un scénario qui tourne quatre minutes par fichier, en brûlant des opérations pour poser une question dont il connaît déjà la réponse, est une chose que vous finissez par surveiller. Un récepteur qui se réveille, vérifie une signature, écrit un fichier et se rendort est une chose dont vous oubliez l'existence — et l'oublier est précisément le but.
À ce stade, les sous-titres cessent d'être une tâche dont quelqu'un a la charge. Ils deviennent une propriété de chaque enregistrement produit par votre organisation : consultable, accessible, conforme, et sans que personne n'ait eu besoin d'une réunion pour cela.
Prêt à le construire ? Créez une clé API — l'offre gratuite suffit pour exécuter tout cela de bout en bout. La référence complète des points de terminaison se trouve dans la documentation de l'API.