Baue eine vollautomatische Transkriptions-Pipeline mit n8n
Die meisten Teams transkribieren immer noch von Hand: Jemand lädt die Aufnahme herunter, lädt sie irgendwo hoch, wartet, lädt eine SRT-Datei herunter, benennt sie um und legt sie im richtigen Ordner ab. Das funktioniert — bis es vierzig Mal pro Woche passiert.
Dieses Tutorial ersetzt diese Person durch einen Workflow. Sobald eine neue Aufnahme eintrifft, wird sie transkribiert, untertitelt und ausgeliefert — ohne dass jemand zusieht. Wir nutzen n8n, weil es überall läuft, in deiner eigenen Infrastruktur selbst gehostet werden kann und mit jeder HTTP-API spricht — auch mit unserer.
Am Ende hast du einen Workflow, der:
- startet, sobald eine neue Video- oder Audiodatei auftaucht
- sie zur Transkription an die Inwista-API schickt
- auf das Ergebnis wartet, ohne die Dauer vorher zu raten
- die fertige SRT-Datei herunterlädt
- sie dort ablegt, wo dein Team sie braucht
Alles Weitere läuft über die öffentliche Inwista API v1. Keine Plugins, keine eigenen Code-Nodes.
Bevor es losgeht
Du brauchst drei Dinge:
- Eine n8n-Instanz — Cloud oder selbst gehostet, Version 1.x
- Einen Inwista-API-Schlüssel — lege einen unter Mein Workspace → API-Schlüssel an. Er beginnt mit
inw_live_ - Medien, die die API erreichen kann — die API erwartet eine öffentliche
https-URL; Dateien in Dropbox, Google Drive, S3 oder deinem CMS brauchen also einen Freigabe- oder signierten Link
Ein Wort zu den Kosten, bevor du etwas baust, das unbeaufsichtigt läuft: Die Transkription kostet 4 Credits pro angefangener Medienminute, abgerechnet bei Annahme des Auftrags. Fehlgeschlagene Aufträge werden automatisch erstattet. Teste den Workflow mit ein paar kurzen Dateien, bevor du ihn auf dein Archiv loslässt.
Schritt 1: Wähle deinen Trigger
Die Pipeline startet mit dem Ereignis, das bedeutet: „Es gibt etwas Neues zu transkribieren." Die häufigsten Varianten:
- Google Drive Trigger / Dropbox Trigger — ein überwachter Ordner, in den dein Team Aufnahmen legt
- Webhook — dein eigenes CMS oder LMS ruft n8n auf, sobald ein Upload fertig ist
- Schedule Trigger — frage einen RSS-Feed, einen Podcast-Hoster oder eine Datenbank mit unverarbeiteten Zeilen ab
- Manual Trigger — zum Bauen und Testen, und genau damit fangen wir an
Füge fürs Erste einen Manual Trigger hinzu. Tausche ihn gegen den echten aus, sobald der Rest läuft.
Was auch immer du nimmst: Die Aufgabe des Triggers ist es, genau eine Sache zu liefern — eine öffentlich erreichbare URL zur Mediendatei. Speichere sie in einem Feld namens mediaUrl, damit der Rest des Tutorials zu deinem Workflow passt.
Schritt 2: Transkription abschicken
Füge einen HTTP-Request-Node namens Submit transcription hinzu.
- Methode —
POST - URL —
https://api.inwista.ai/v1/transcriptions - Authentifizierung — Generic Credential Type → Header Auth
- Header-Name —
Authorization - Header-Wert —
Bearer inw_live_dein_schluessel_hier - Send body — an, JSON
Body:
{
"source_url": "{{ $json.mediaUrl }}",
"language": "de",
"diarization": true,
"metadata": { "source": "n8n", "folder": "{{ $json.folderName }}" }
}
Drei Felder, die du verstehen solltest:
languageist die Sprache, die in der Datei gesprochen wird — nicht die Sprache, die du herausbekommen möchtest. In der Ausgangssprache zu transkribieren ist das, was präzise Zeitstempel und sauberen Text erzeugt; die Übersetzung kommt danach, als eigener Schritt auf dem fertigen Transkript. Wenn deine Pipeline mehrere Sprachen verarbeitet, leite dieses Feld aus dem Ordner oder den Metadaten des Triggers ab.
diarizationschaltet die Sprecherkennzeichnung ein. Lass sie bei Inhalten mit nur einer Stimme aus — sie kostet Verarbeitungszeit, die du nicht brauchst.
metadatagehört dir. Bis zu 1 KB beliebiger Inhalt, bei jedem Abruf unverändert zurückgegeben. Nutze es für die Kennungen, auf die es in deinen eigenen Systemen ankommt — eine Kurs-ID, ein Aktenzeichen, den Slug einer Episode —, damit spätere Schritte den Kontext nicht rekonstruieren müssen.
Füge gleich noch einen Header hinzu:
Idempotency-Key—{{ $execution.id }}
Wenn n8n den Node nach einem Netzwerkaussetzer erneut ausführt, erkennt die API den Schlüssel und gibt den ursprünglichen Auftrag zurück, statt einen zweiten zu starten — und zu berechnen.
Die Antwort kommt sofort mit status: "processing" und einer id. Die Transkription ist nicht fertig, sie ist angenommen.
Schritt 3: Richtig auf das Ergebnis warten
Hier gehen die meisten Pipelines schief. Ein festes „warte fünf Minuten" ist für eine Vorlesung zu kurz und für eine Sprachnotiz Verschwendung. Frage stattdessen den Status ab.
Füge einen Wait-Node (Wait 15s) mit 15 Sekunden hinzu.
Füge einen HTTP-Request-Node (Check status) hinzu:
- Methode —
GET - URL —
https://api.inwista.ai/v1/transcriptions/{{ $('Submit transcription').item.json.id }} - Authentifizierung — dieselbe wie zuvor
Füge einen IF-Node (Is it done?) mit dieser Bedingung hinzu:
{{ $json.status }} equals completed
Führe den false-Zweig zurück auf Wait 15s. Das ist deine Schleife: prüfen, warten, wieder prüfen, bis der Auftrag completed meldet. Den true-Zweig führst du zum nächsten Schritt.
Die Antwort enthält außerdem eine progress-Zahl, die den echten Stand der Pipeline widerspiegelt — wenn du also eine Fortschrittsanzeige in Slack oder deinem eigenen Dashboard möchtest, ist sie schon da.
Zwei Dinge, die du ergänzen solltest, bevor das in die Nähe von Produktion kommt:
- Fehler behandeln. Füge ein zweites IF hinzu, das
{{ $json.status }} equals failedprüft und an dein Alerting weiterleitet. Fehlgeschlagene Aufträge werden automatisch erstattet, aber du willst es trotzdem erfahren. - Die Schleife begrenzen. Der Schleifenschutz von n8n hilft, aber eine explizite Obergrenze — ein Zähler, der nach sagen wir 80 Durchläufen abbricht — macht aus einem hängenden Auftrag eine Warnung statt einer Ausführung, die die ganze Nacht läuft.
Schritt 4: Untertitel abholen
Füge einen HTTP-Request-Node namens Get SRT hinzu:
- Methode —
GET - URL —
https://api.inwista.ai/v1/transcriptions/{{ $('Submit transcription').item.json.id }}/captions?format=srt - Response format — File (oder Text, wenn du den Inhalt im Flow behalten willst)
Der Endpoint gibt die Untertiteldatei selbst zurück, keinen JSON-Wrapper — die Ausgabe des Nodes ist also bereit, auf die Festplatte geschrieben, an eine E-Mail gehängt oder hochgeladen zu werden.
Tausche format gegen das, was das Ziel braucht:
Format — wofür
srt— Videoplayer, Schnittprogramme, YouTube, die meisten CMSvtt— HTML5-<track>, Web-Playertxt— Suchindizes, LLM-Pipelines, Dokumentationjson— Zeitstempel auf Wortebene, Sprecherkennzeichnung, eigenes Rendering
Jedes Format ist nur eine Sicht auf denselben fertigen Auftrag. Vier davon abzurufen kostet nichts extra.
Schritt 5: Ausliefern
Der letzte Node ist das, was „fertig" für dein Team bedeutet:
- Google Drive / Dropbox / S3 — lege die Datei neben das Ausgangsvideo
- Slack — poste das Transkript in den Kanal, der danach gefragt hat
- HTTP Request — schiebe es in dein CMS, dein LMS oder dein Untertitelfeld
- Postgres / Airtable / Notion — speichere die
txt-Version als durchsuchbaren Text
Wenn dein Ziel zu denen gehört, die Inwista bereits nativ integriert — Google Drive, OneDrive, SharePoint, Dropbox, Box, YouTube, Vimeo, Wistia und weitere —, kannst du diesen Node auch komplett weglassen und stattdessen die Cloud-Auslieferung einrichten, unter Mein Workspace → Integrationen. Die Dateien kommen dann bei jedem fertigen Auftrag automatisch an, mit oder ohne Workflow.
Level up: Übersetzen vor dem Ausliefern
Setze zwei Nodes vor die Auslieferung, und dieselbe Pipeline liefert Untertitel in so vielen Sprachen, wie du brauchst.
POST /v1/transcriptions/{id}/translate mit:
{ "target_language": "no" }
Es antwortet mit 202 Accepted — die Übersetzung läuft auf dem fertigen Transkript, die Timings stimmen also bereits und nur der Text ändert sich. Frage GET /v1/transcriptions/{id}/translations/no genauso ab wie zuvor den Auftrag und hole dann:
GET /v1/transcriptions/{id}/captions?format=srt&language=no
Lass das über eine Liste von Zielsprachen laufen, und aus einer Aufnahme wird in einem einzigen Durchlauf ein vollständiger mehrsprachiger Untertitelsatz. Beachte: Die Sprachanfrage ist strikt — fragst du eine Sprache ohne fertige Übersetzung ab, bekommst du einen expliziten Fehler statt stillschweigend die Ausgangssprache. Genau das willst du in einer unbeaufsichtigten Pipeline.
Level up: Untertitel in Sendequalität
Eine reine Transkription ist wörtlich. Untertitel sind Handwerk: Zeilenlängen, Lesegeschwindigkeit, die Stelle, an der ein Satz auf zwei Karten umbricht.
POST /v1/transcriptions/{id}/enhance schickt das fertige Transkript durch genau diese Behandlung — Text verdichten, Zeilenumbrüche neu ausbalancieren, Dialoge formatieren, Mindest- und Höchstdauer je Block durchsetzen:
{
"settings": {
"maxLinesPerBlock": "2",
"maxCharactersPerLine": 42,
"textCondensation": "smart",
"speakerDialogueFormat": "hyphens",
"gapBetweenBlocks": "broadcasting"
}
}
Es antwortet mit 202; frage GET /v1/transcriptions/{id}/enhancements/{enhancementId} ab, bis der Vorgang fertig ist. Danach liefern die Untertitel-Endpoints automatisch die verbesserte Version — an deinem Auslieferungs-Node musst du nichts ändern.
Sensible Aufnahmen
Wenn deine Pipeline Material verarbeitet, das wir lieber nicht behalten sollen — Patientengespräche, juristische Aufnahmen, interne Vollversammlungen —, ergänze das Feld aus Schritt 2:
{
"source_url": "{{ $json.mediaUrl }}",
"language": "de",
"retention": "none"
}
Mit retention: "none" wird das Ausgangsmedium gelöscht, sobald die Transkription fertig ist. Transkript, Untertitel, Übersetzungen und spätere Verbesserungen funktionieren ganz normal weiter — nur Audio und Video sind weg. Daneben gibt es store_media: false: Die Datei bleibt für die Verarbeitung erhalten, es werden aber keine Wiedergabekopien erzeugt.
Und wenn die Aufbewahrungsfrist eines Auftrags auf deiner Seite endet, löscht DELETE /v1/transcriptions/{id} alles dazu — Medien, Transkript, Revisionen, Übersetzungen — mit einem einzigen Aufruf. Ein geplanter n8n-Workflow, der Aufträge älter als deine Richtlinie löscht, sind etwa vier Nodes — und macht aus deiner Aufbewahrungsrichtlinie etwas, das du vorführen kannst, statt es nur zu beschreiben.
Webhooks: das Gegenstück
Die Statusabfrage ist der richtige Ablauf innerhalb einer einzelnen n8n-Ausführung: Sie ist in sich geschlossen, braucht keine öffentliche URL und hält die gesamte Pipeline an einem Ort, den du debuggen kannst.
Webhooks lösen ein anderes Problem: fertige Arbeit an einen zentralen Empfänger zu bringen, egal woher der Auftrag kam. Aufnahmen, die deine Kolleginnen und Kollegen über das Dashboard hochladen, Aufträge aus einem anderen System, Exporte, die Stunden später fertig werden — all das kann auf einem einzigen Endpoint landen, statt dass jeder Workflow sich um seinen eigenen kümmert. Richte es unter Mein Workspace → Integrationen ein.
Zeige damit auf einen n8n-Webhook-Node, und du erhältst ein signiertes transcript.completed-Event:
{
"event": "transcript.completed",
"id": "evt_...",
"timestamp": 1786902819,
"workspaceId": "...",
"project": {
"id": "...",
"name": "vorstandssitzung-august.mp4",
"language": "de",
"durationSeconds": 3184
},
"files": [
{ "format": "srt", "name": "vorstandssitzung-august.srt", "url": "https://...", "expiresAt": 1786989219 }
]
}
Jede Anfrage ist mit HMAC-SHA256 über den rohen Body signiert, im Header X-Inwista-Signature als sha256=<hex>. Prüfe sie, bevor du dem Payload vertraust — in n8n reichen dafür ein Crypto-Node und ein IF-Vergleich. Die Datei-URLs sind signiert und 24 Stunden gültig: Hole dir, was du brauchst, statt die Links zu speichern.
Die Zustellung wird dreimal wiederholt, und ein Endpoint, 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 Events, die still verschwinden.
Was dir früher oder später auf die Füße fällt
Rate Limits. 300 Lese- und 60 Schreibanfragen pro Minute und Schlüssel. Großzügig im normalen Betrieb, schnell erreicht, wenn du ein großes Archiv ohne Warteschlange parallel loslässt. Wenn du Hunderte Dateien nachziehst, arbeite in Stapeln.
Quell-URLs müssen erreichbar sein. Die API prüft das Medium, bevor sie den Auftrag annimmt — daher sind Dauer und Kosten vorab bekannt. Ein Drive-Link, der einen Login verlangt, liefert unreadable_source, und es wird nichts berechnet. Nutze direkte oder signierte URLs.
Fehler sind strukturiert. Jeder Fehlschlag gibt dieselbe Hülle zurück:
{ "error": { "code": "insufficient_credits", "message": "..." } }
Verzweige nach code, nie nach dem Meldungstext. Codes kommen innerhalb von v1 nur hinzu und werden nie umbenannt; Meldungen können neu formuliert werden.
Idempotency-Keys gelten pro Anfrage, nicht pro Datei. Sie schützen dich vor doppelter Abrechnung, wenn dieselbe Node-Ausführung wiederholt wird. Dieselbe Aufnahme zu entdoppeln, wenn sie von zwei verschiedenen Durchläufen eingereicht wird, bleibt Aufgabe deines Workflows — eine Nachschlagetabelle über die Datei-ID ist die übliche Antwort.
Der fertige Workflow
Hier ist das Grundgerüst zum Importieren und Anpassen. Ersetze die Credential-Referenz durch deine eigene Header-Auth-Credential, statt einen Schlüssel in den Node zu kleben.
{
"name": "Inwista — automated transcription",
"nodes": [
{
"parameters": {},
"id": "trigger",
"name": "When clicking Test workflow",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0]
},
{
"parameters": {
"method": "POST",
"url": "https://api.inwista.ai/v1/transcriptions",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\"source_url\": \"{{ $json.mediaUrl }}\", \"language\": \"de\", \"diarization\": true}",
"options": {}
},
"id": "submit",
"name": "Submit transcription",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [220, 0]
},
{
"parameters": { "amount": 15 },
"id": "wait",
"name": "Wait 15s",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [440, 0]
},
{
"parameters": {
"url": "=https://api.inwista.ai/v1/transcriptions/{{ $('Submit transcription').item.json.id }}",
"options": {}
},
"id": "status",
"name": "Check status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [660, 0]
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "version": 2 },
"conditions": [
{
"leftValue": "={{ $json.status }}",
"rightValue": "completed",
"operator": { "type": "string", "operation": "equals" }
}
],
"combinator": "and"
},
"options": {}
},
"id": "isdone",
"name": "Is it done?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [880, 0]
},
{
"parameters": {
"url": "=https://api.inwista.ai/v1/transcriptions/{{ $('Submit transcription').item.json.id }}/captions?format=srt",
"options": { "response": { "response": { "responseFormat": "text" } } }
},
"id": "srt",
"name": "Get SRT",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1100, -100]
}
],
"connections": {
"When clicking Test workflow": { "main": [[{ "node": "Submit transcription", "type": "main", "index": 0 }]] },
"Submit transcription": { "main": [[{ "node": "Wait 15s", "type": "main", "index": 0 }]] },
"Wait 15s": { "main": [[{ "node": "Check status", "type": "main", "index": 0 }]] },
"Check status": { "main": [[{ "node": "Is it done?", "type": "main", "index": 0 }]] },
"Is it done?": {
"main": [
[{ "node": "Get SRT", "type": "main", "index": 0 }],
[{ "node": "Wait 15s", "type": "main", "index": 0 }]
]
}
}
}
Sechs Nodes. Alles Weitere — Übersetzung, Verbesserung, Auslieferung, Löschung — hängt sich an dasselbe Grundgerüst.
Was sich dadurch wirklich ändert
Das Interessante ist nicht, dass Transkription automatisiert wird. Es ist das, was aufhört, eine Entscheidung zu sein.
Wenn Untertitel den Nachmittag eines Menschen kosten, werden sie rationiert: Die wichtigen Videos bekommen welche, der Rest nicht. Wenn sie pro Datei nichts kosten und da sind, bevor jemand auf die Idee kommt, danach zu fragen, hören sie auf, ein Projekt zu sein, und werden zu einer Eigenschaft deiner Inhalte — jede Aufnahme durchsuchbar, jedes Video zugänglich, jeder Kurs konform, ohne dass es dafür ein Meeting braucht.
Das ist mehr wert als die Stunden, die es spart.
Bereit, es zu bauen? Erstelle einen API-Schlüssel — der kostenlose Tarif reicht, um das hier von Anfang bis Ende durchzuspielen. Die vollständige Endpoint-Referenz findest du in der API-Dokumentation.