Transkription in Make.com: die Webhook-first-Pipeline

Ein Transkriptionsauftrag braucht Minuten, bis er fertig ist. Ein Make-Szenario wird pro Modulausführung abgerechnet. Diese beiden Tatsachen ziehen in entgegengesetzte Richtungen, und dieses Spannungsverhältnis aufzulösen ist das eigentliche Thema dieses Tutorials.


Der naheliegende Aufbau – eine Datei absenden, dann warten, prüfen, warten, prüfen, bis sie fertig ist – kostet auf einem selbst gehosteten Runner nichts. Bei Make ist er nicht gratis. Jedes Warten und jede Statusabfrage ist eine Operation, und ein vierminütiger Auftrag kann dreißig davon verbrennen, nur um zu fragen: „Sind wir schon so weit?“


Wir bauen es deshalb andersherum. Inwista meldet sich bei Make, sobald die Arbeit fertig ist, und das Szenario, das diese Meldung entgegennimmt, ist drei Module lang.


Am Ende hast du:


  1. Ein Empfänger-Szenario, das aufwacht, sobald ein Transkript fertig ist
  2. Ein Absender-Szenario, das Dateien an Inwista übergibt und danach anhält
  3. Signaturprüfung, damit der Empfänger nur echten Ereignissen vertraut
  4. Optionale Erweiterungen – parallele Übersetzungen, Deduplizierung und ein Aufräumlauf für die Aufbewahrung


Alles läuft über die öffentliche Inwista API v1 und das Standard-HTTP-Modul von Make. Keine eigene App, kein Code.

Bevor du anfängst

Du brauchst drei Dinge:


  • Einen Make-Account – der kostenlose Tarif reicht zum Bauen, sein Operationslimit ist für den Produktivbetrieb allerdings knapp
  • Einen Inwista-API-Schlüssel – lege einen unter Mein Arbeitsbereich → API-Schlüssel an. Er beginnt mit inw_live_
  • Für die API erreichbare Medien – die API erwartet eine öffentliche https-URL, Dateien in Dropbox, Google Drive, S3 oder deinem CMS brauchen also einen freigegebenen oder signierten Link


Du brauchst keinen eigenen Host für einen Webhook-Endpunkt. Make gibt dir die URL.


Eine Anmerkung zu den Kosten, bevor du etwas baust, das unbeaufsichtigt läuft: Die Transkription wird mit 4 Credits pro angefangener Medienminute abgerechnet, fällig sobald der Auftrag angenommen wird. Fehlgeschlagene Aufträge werden automatisch erstattet. Richte das Szenario zuerst auf ein paar kurze Testdateien, bevor du es auf dein Archiv loslässt.

Warum die Architektur hier zählt

Beide Varianten funktionieren. Sie kosten nur sehr unterschiedlich viel, und bei Make summiert sich dieser Unterschied Monat für Monat.


Ein Polling-Szenario für einen Auftrag, der in etwa vier Minuten fertig ist und alle zwanzig Sekunden nachfragt, sieht ungefähr so aus: einmal absenden, dann zwölf Runden aus Warten plus Statusabfrage plus Router, danach abholen und ausliefern. Rechne mit 39 Operationen pro Datei.


Die Webhook-Variante teilt sich in zwei Szenarien. Der Absender besteht aus einem Trigger und einem HTTP-Aufruf. Der Empfänger aus einem Webhook, einem Parse, einem Abruf und einer Auslieferung. Rechne mit 6 Operationen pro Datei.


Bei 200 Aufnahmen im Monat sind das 7.800 Operationen gegenüber 1.200 – der Unterschied zwischen einer Tarifstufe und einem Rundungsfehler. Es beseitigt außerdem die beiden Fehlerbilder, die Polling-Szenarien im Produktivbetrieb treffen: eine Ausführung, die lange genug läuft, um an das 40-Minuten-Limit von Make zu stoßen, und ein hängender Auftrag, der klammheimlich die ganze Nacht durchdreht.


Baue zuerst den Empfänger. Das ist der Teil, der stimmen muss.

Schritt 1: Den Empfänger anlegen

Neues Szenario. Füge ein Modul hinzu, wähle Webhooks → Custom webhook, klicke auf Add, benenne es etwa inwista-transcripts und kopiere die URL, die Make dir gibt.


Bevor du diesen Dialog verlässt, öffne die erweiterten Webhook-Einstellungen und aktiviere JSON pass-through.


Das ist die eine Einstellung, die alle übersehen, und es lohnt sich, sie zu verstehen statt sie nur abzuschreiben. Inwista signiert jede Zustellung mit einem HMAC über die rohen Bytes des Anfragekörpers. Wenn Make das JSON für dich parst, sind genau diese Bytes weg – inklusive Schlüsselreihenfolge und Leerzeichen – und du kannst die Signatur nicht mehr nachbilden. Pass-through liefert dir den Körper unangetastet als einzelne Zeichenkette: Das kostet dich später ein zusätzliches Modul zum Parsen und verschafft dir überhaupt erst die Möglichkeit, irgendetwas zu prüfen.


Füge diese URL nun in Inwista unter Mein Arbeitsbereich → Integrationen ein, abonniere sie für transcript.completed und kopiere das angezeigte Signaturgeheimnis.

Schritt 2: Die Signatur prüfen

Klicke mit der rechten Maustaste auf die Verbindung, die vom Webhook wegführt, und füge einen Filter hinzu. Die Funktion sha256() von Make berechnet einen HMAC, sobald du ihr einen Schlüssel übergibst – die gesamte Prüfung passt damit in einen einzigen Ausdruck: kein Krypto-Modul, kein Code.


Bedingung, Text: Equal to:

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

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


Zwei Details, die dich sonst einen Nachmittag kosten. Make stellt eingehende Header-Namen in Kleinbuchstaben bereit, es heißt also x-inwista-signature und nicht die Schreibweise mit Großbuchstaben aus unserer Dokumentation. Und der Header-Wert trägt das Präfix sha256=, du musst es also entweder wie oben voranstellen oder vor dem Vergleich entfernen.


Alles, was den Filter nicht besteht, endet einfach. Eine unsignierte Anfrage erreicht die arbeitenden Module nie.


Füge nach dem Filter ein JSON → Parse JSON-Modul hinzu, das auf 1.data zeigt. Ab hier verhält sich die Nutzlast wie jedes andere Make-Bundle.

Schritt 3: Abholen und ausliefern

Das Ereignis enthält bereits alles, was du brauchst:

{
  "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 }
  ]
}


Füge HTTP → Get a file hinzu und mappe die URL auf {{2.files[1].url}}.


Diese [1] ist kein Tippfehler. Arrays beginnen bei Make bei 1, und wer aus Gewohnheit [0] schreibt, bekommt einen leeren Wert statt eines Fehlers – der wandert dann still weiter und taucht drei Tage später als Datei mit null Byte in Drive auf.


Diese URLs sind signiert und 24 Stunden gültig. Hole die Datei ab; speichere nicht den Link.


Hänge anschließend an, was „fertig“ für dein Team bedeutet – Google Drive, Dropbox, S3, Slack, einen HTTP-Aufruf in dein CMS, eine Zeile in Airtable oder Notion.


Drei Module und ein Filter. Das ist der komplette Empfänger, und er verarbeitet jeden abgeschlossenen Auftrag im Arbeitsbereich – auch die Aufnahmen, die deine Kolleginnen und Kollegen von Hand über das Dashboard hochladen und von denen kein Polling-Szenario je erfahren hätte.

Schritt 4: Der Absender

Zweites Szenario. Beginne mit dem Ereignis, das „es gibt etwas Neues“ bedeutet: ein Überwachungsmodul für Google Drive oder Dropbox, ein Custom webhook aus deinem eigenen CMS oder eine geplante Abfrage über eine Datenbank. Zum Testen startest du es einfach von Hand.


Seine einzige Aufgabe ist es, eine öffentlich erreichbare URL zu erzeugen. Füge HTTP → Make a request hinzu:


  • URL – https://api.inwista.ai/v1/transcriptions
  • Methode – POST
  • Header – Authorization: Bearer inw_live_your_key_here
  • Header – Idempotency-Key: {{md5(1.fileUrl)}}
  • Body-Typ – Raw, Inhaltstyp JSON


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


Drei Felder, die man verstanden haben sollte:


  • language ist die Sprache, die in der Datei gesprochen wird, nicht die Sprache, die du herausbekommen willst. In der Ausgangssprache zu transkribieren erzeugt genaue Zeitstempel und sauberen Text; die Übersetzung kommt danach, auf dem fertigen Transkript. Wenn deine Pipeline mehrere Sprachen abdeckt, mappe den Ordner des Triggers auf dieses Feld.


  • diarization schaltet Sprecherkennzeichnungen ein. Lass es bei Inhalten mit nur einer Stimme aus – es kostet Verarbeitungszeit, die du nicht brauchst.


  • metadata gehört dir. Bis zu 1 KB beliebiger Inhalt, bei jedem Lesezugriff und im Webhook wortgetreu zurückgegeben – so weiß der Empfänger, zu welchem Szenario, Kurs oder Vorgang eine Datei gehört, ohne irgendwo nachschlagen zu müssen.


Achte darauf, woraus der Idempotenzschlüssel abgeleitet wird. Aus der Ausführung abgeleitet schützt er dich vor einem erneuten Versuch genau dieses Moduls. Aus der Datei abgeleitet, wie oben, schützt er dich zusätzlich davor, dass dieselbe Aufnahme von zwei verschiedenen Läufen zweimal eingereicht wird – der weitaus häufigere Weg, versehentlich doppelt zu zahlen.


Die Antwort kommt sofort zurück, mit status: "processing" und einer id. Das Szenario endet hier. Das ist der Entwurf, der funktioniert, nicht der Entwurf, der scheitert.

Übersetzungen mit einem Iterator parallel anstoßen

Eine Aufnahme in sechs Sprachen – hier zahlt sich die Array-Verarbeitung von Make aus.


Füge im Empfänger, nachdem das Transkript eingetroffen ist, ein Tools → Set variable mit deiner Zielsprachenliste hinzu, dann einen Iterator darüber und schließlich ein einzelnes HTTP-Modul innerhalb der Schleife:

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

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


Jeder Aufruf liefert 202 Accepted. Die Übersetzung läuft auf dem fertigen Transkript, die Zeiten stimmen also bereits und nur der Text ändert sich. Sechs Sprachen kosten sechs Operationen, und die fertigen Dateien kommen über denselben Webhook an, den du bereits gebaut hast.


Beim Abholen fragst du ausdrücklich danach:

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


Der Sprachparameter ist streng. Wer eine Sprache ohne fertige Übersetzung anfordert, bekommt einen ausdrücklichen Fehler statt still die Ausgangssprache – genau das, was du in einer Pipeline willst, die niemand beobachtet.

Untertitel in Broadcast-Qualität

Rohe Transkription ist wortwörtlich. Untertitel sind ein Handwerk: Zeilenlängen, Lesegeschwindigkeit, die Stelle, an der ein Satz auf zwei Karten umbricht.


Ein weiteres HTTP-Modul, POST /v1/transcriptions/{id}/enhance, schickt das Transkript durch genau diese Behandlung – Text verdichten, Zeilenumbrüche neu ausbalancieren, Dialoge formatieren, Blockdauern einhalten:

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


Danach liefern die Untertitel-Endpunkte automatisch die verbesserte Fassung aus. Nachgelagert ändert sich nichts.

Wenn etwas schiefgeht: die Fehlerrouten von Make

Klicke mit der rechten Maustaste auf ein beliebiges Modul und wähle Add error handler. Make bietet dir Direktiven, die ein einfacher IF-Zweig nicht ausdrücken kann:


  • Break – parkt die Ausführung in Incomplete Executions, damit du die Ursache behebst und genau dieses Bundle erneut startest. Setze das auf das Absende-Modul.
  • Ignore – protokolliert den Fehler und macht weiter. Passend für einen Auslieferungsschritt, der verzichtbar ist.
  • Resume – setzt einen Ersatzwert ein und fährt fort.
  • Rollback – macht bestätigte Arbeit in transaktionalen Modulen rückgängig.


Jeder Fehler von Inwista liefert dieselbe Struktur zurück:

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


Verzweige über code, niemals über den Meldungstext. Codes kommen innerhalb von v1 nur hinzu und werden nie umbenannt; Meldungen dagegen können jederzeit neu formuliert werden.


Auf unserer Seite wird die Webhook-Zustellung dreimal wiederholt, und ein Endpunkt, der dauerhaft fehlschlägt, wird automatisch deaktiviert – mit dem Grund sichtbar in deinen Integrationseinstellungen. Ein kaputter Empfänger zeigt sich also als Status, den du sehen kannst, statt als Ereignisse, die stillschweigend verschwinden.

Dieselbe Datei nicht zweimal transkribieren

Trigger auf überwachten Ordnern lösen erneut aus. Dateien werden umbenannt. Jemand lädt etwas noch einmal hoch.


Make hat dafür eine eigene Antwort: den Data store. Lege einen an, der auf die ID der Quelldatei schlüsselt, und füge im Absender vor dem HTTP-Aufruf Data store → Get a record hinzu, filtere darauf, dass der Eintrag nicht existiert, und setze nach erfolgreichem Absenden Add a record dahinter.


Zwei Operationen, um keine doppelte Transkription zu bezahlen. Der Idempotenzschlüssel deckt Wiederholungen eines Moduls ab; der Data store deckt alles andere ab.

Sensible Aufnahmen

Wenn deine Pipeline Material verarbeitet, das wir lieber nicht aufbewahren sollen – Patientengespräche, juristische Aufnahmen, interne Mitarbeiterversammlungen –, ergänze ein Feld beim Absenden:

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


Mit retention: "none" wird das Ausgangsmaterial gelöscht, sobald die Transkription abgeschlossen ist. Transkript, Untertitel, Übersetzungen und spätere Verbesserungen funktionieren weiterhin – nur Audio und Video sind weg. Daneben gibt es store_media: false, das die Datei zur Verarbeitung behält, aber keine Wiedergabekopien erzeugt.


Am anderen Ende des Lebenszyklus löscht DELETE /v1/transcriptions/{id} mit einem einzigen Aufruf alles zu einem Auftrag. Ein geplantes Szenario, das aus demselben Data store die Auftrags-IDs jenseits deiner Aufbewahrungsfrist liest und löscht, sind vier Module – und es macht aus deiner Aufbewahrungsrichtlinie etwas, das du vorführen kannst, statt es nur zu beschreiben.

Wann Polling weiterhin die richtige Antwort ist

Drei Fälle sprechen wirklich dafür: Du kannst keinen Webhook nach außen anbieten, du brauchst das Transkript innerhalb derselben Ausführung, um eine synchrone Anfrage zu beantworten, oder du fährst eine einmalige Nachverarbeitung, bei der die Zahl der Operationen egal ist.


Dann füge ein Tools → Sleep-Modul und eine Statusabfrage gegen GET /v1/transcriptions/{id} hinzu und beachte zwei Obergrenzen: Sleep ist auf 300 Sekunden pro Modul begrenzt, eine Szenarioausführung auf 40 Minuten. Frage alle 20 bis 30 Sekunden ab statt alle fünf – die Antwort enthält ein Feld progress, das die tatsächliche Position in der Pipeline widerspiegelt, eine langsamere Schleife gibt dir also trotzdem etwas Ehrliches zum Anzeigen.


Wenn du dieses Muster lieber ausgebaut sehen möchtest: Unsere n8n-Fassung dieses Tutorials setzt durchgehend auf Polling, weil die Schleife auf einem selbst gehosteten Runner nichts kostet.

Was dich irgendwann beißen wird

Arrays beginnen bei 1. files[1] ist die erste Datei. files[0] liefert leer zurück, statt fehlzuschlagen.


Pass-through und Parsen sind ein Tauschgeschäft. Du kannst keine Signatur gegen einen Körper prüfen, den Make bereits geparst hat. Pass-through plus ein Parse-JSON-Modul ist die einzig richtige Reihenfolge.


Ratenbegrenzungen. 300 Lese- und 60 Schreibzugriffe pro Minute und Schlüssel. Großzügig für den normalen Betrieb, schnell erreicht, wenn du ein ganzes Archiv über parallele Szenarioläufe verteilst. Verarbeite Nachläufe in Stapeln.


Quell-URLs müssen erreichbar sein. Die API prüft das Medium, bevor sie den Auftrag annimmt – so stehen Dauer und Kosten vorab fest. Ein Drive-Link, der eine Anmeldung verlangt, liefert unreadable_source, und es wird nichts berechnet. Nutze direkte oder signierte URLs.


Alles zählt. Sleep-Module, Router, Iterator-Durchläufe und Filter, die durchlassen, verbrauchen allesamt Operationen. Wenn ein Szenario teuer wirkt, zähle die Module, bevor du der API die Schuld gibst.

Der fertige Empfänger

Hier ist das Gerüst des Blueprints. Importiere es und hänge dann deinen eigenen Webhook und dein Auslieferungsmodul an – und nutze die Verbindungsverwaltung von Make, statt einen Schlüssel in ein Modul zu kleben.

{
  "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": [] }
  }
}


Drei Module. Die Polling-Variante hatte elf und kostete im Betrieb das Sechsfache.

Was sich damit wirklich ändert

Der Maßstab für eine Automatisierung ist nicht, was sie tut, während du zusiehst. Der Maßstab ist, was sie sonntags um zwei Uhr nachts tut, wenn eine neunzigminütige Aufnahme eintrifft und niemand wach ist.


Ein Szenario, das pro Datei vier Minuten läuft und Operationen verbrennt, um eine Frage zu stellen, deren Antwort es längst kennt, ist etwas, nach dem du irgendwann schaust. Ein Empfänger, der aufwacht, eine Signatur prüft, eine Datei schreibt und wieder einschläft, ist etwas, dessen Existenz du vergisst – und ihn zu vergessen ist genau der Punkt.


An diesem Punkt hören Untertitel auf, eine Aufgabe zu sein, für die jemand zuständig ist. Sie werden zu einer Eigenschaft jeder Aufnahme, die deine Organisation produziert: durchsuchbar, barrierefrei, regelkonform – und niemand musste dafür eine Besprechung ansetzen.


Bereit, es zu bauen? Erstelle einen API-Schlüssel – der kostenlose Tarif reicht, um das Ganze von Anfang bis Ende durchzuspielen. Die vollständige Endpunkt-Referenz steht in der API-Dokumentation.