
Internes Transkriptionstool: Bauen oder kaufen – und dann bauen
Summarize this article with:
Selber bauen oder kaufen – ganz ehrlich
Der Bau eines internen Transkriptionstools lohnt sich, wenn dein Team ein ausreichendes Volumen hat und die SaaS-Kosten pro Sitzplatz klar über dem liegen, was eine nutzungsbasierte API plus einige Wochen Entwicklungszeit kosten würde. Dieser Wendepunkt liegt meist bei etwa 10–15 aktiven Nutzern. Darunter ist die Zahlung für Otter oder Fireflies fast immer die richtige Entscheidung. Darüber gewinnst du echte Vorteile: Deine Daten bleiben in deinen eigenen Systemen, der Zugriff wird über dein bestehendes SSO gesteuert, und du kannst genau den Workflow bauen, den dein Team braucht, statt dich an das Produkt eines anderen anzupassen.
Dieser Leitfaden behandelt die Architektur, die Abwägung zwischen Bauen und Kaufen sowie den Kerncode für ein produktionsreifes internes Tool.
Wann Kaufen weiterhin gewinnt
Bevor du Code schreibst, sei ehrlich zu dir selbst darüber, was du eigentlich kaufst, wenn du pro Sitzplatz zahlst.
Die automatische Aufzeichnung durch Meeting-Bots ist am schwersten nachzubilden. Otter und Fireflies schicken automatisch einen Bot in deine Zoom- oder Meet-Calls, ohne dass jemand daran denken muss, auf Aufnehmen zu drücken. Das nachzubauen erfordert die Integration einer Third-Party-Recall-API, das Management des Bot-Lebenszyklus, die Berücksichtigung von DSGVO- und Hinweispflichten und das Überstehen von API-Änderungen der Meeting-Plattformen. Das sind Wochen Arbeit und laufende Wartung.
SOC 2 und Compliance-Ausrichtung sind ein weiterer realer Kostenfaktor. Wenn Enterprise-Kunden fragen, ob dein internes Tool konform ist, verschiebt das die Frage dahin, ob dein Team dieses Audit selbst tragen will. Fertige Tools liefern ihre eigenen Compliance-Nachweise mit; dein internes Tool nicht.
Die echten Fälle, in denen Bauen Kaufen schlägt:
- Du hast ein hohes Volumen an Audiodateien, die nicht aus Meetings stammen (Interviews, Feldaufnahmen, Kundengespräche, Podcasts, gerichtliche Aussagen).
- Du brauchst eine tiefe Integration mit deinen internen Systemen (Fallmanagement, CRM, Dokumentenspeicher), die keine native Integration abdeckt.
- Du hast strenge Anforderungen an die Datenresidenz, die verlangen, dass Audio auf deiner eigenen Infrastruktur bleibt.
- Dein Team ist groß genug, dass sich die Kosten pro Sitzplatz spürbar summieren.
Otter.ai Business kostet 19,99 $ pro Nutzer und Monat bei jährlicher Abrechnung (oder 30 $ monatlich). Fireflies Business kostet 19 $ pro Nutzer und Monat bei jährlicher Abrechnung (oder 29 $ monatlich). Bei 30 Nutzern sind das rund 570–600 $ pro Monat. Eine nutzungsbasierte API wie Deepgram Nova-3 kostet 0,0077 $ pro Minute für vorab aufgezeichnetes Audio. Ein Team von 30 Personen, das jeweils eine Stunde Audio pro Monat einreicht, zahlt etwa 14 $ an Transkriptions-API-Gebühren. Die Lücke ist real.
| Otter Business | Fireflies Business | Internes Tool (API) | |
|---|---|---|---|
| 10 Nutzer | 200 $/Monat | 190 $/Monat | API-Kosten + Engineering-Aufwand |
| 30 Nutzer | 600 $/Monat | 570 $/Monat | ca. 50–100 $/Monat bei typischem Volumen |
| 50 Nutzer | 1.000 $/Monat | 950 $/Monat | ca. 80–160 $/Monat bei typischem Volumen |
| SSO/SAML | Nur Enterprise | Nur Enterprise | Selbst bauen |
| Meeting-Bots | Ja, nativ | Ja, nativ | Erfordert Third-Party-API |
| Datenresidenz | Infrastruktur des Anbieters | Infrastruktur des Anbieters | Deine Infrastruktur |
Preise pro Sitzplatz verifiziert über otter.ai/pricing und fireflies.ai/pricing, Juli 2026. Die API-Kostenschätzungen verwenden Deepgram Nova-3 mit 0,0077 $/Minute für vorab aufgezeichnetes Audio (verifiziert über deepgram.com/pricing), ohne Hosting oder Speicher.

Die Architektur
Das minimal nützliche interne Tool besteht aus vier Teilen:
- Upload-Endpunkt. Dateien landen in deinem Objektspeicher (S3, R2 oder GCS).
- Integration der Transkriptions-API. Jobs einreichen, den Abschluss per Webhook oder Polling empfangen.
- Datenbank. Transkripte, Job-Metadaten und Nutzerzuordnung speichern.
- Suche und Dashboard. Transkripte nach Nutzer, Projekt, Inhalt oder Datum finden.
Stack-Entscheidungen, die es einfach halten:
- Frontend: Next.js oder dein vorhandenes Framework für interne Tools (Retool funktioniert auch für schnelles Prototyping).
- Backend: Node.js-, Python- oder Go-REST-Dienst.
- Datenbank: Postgres mit integrierter Volltextsuche.
- Speicher: S3 oder R2 für Audiodateien.
- Auth: zuerst Google OAuth, dann SAML/OIDC, wenn die IT es verlangt.
Das Datenmodell
Drei Kerntabellen decken den wesentlichen Anwendungsfall ab:
CREATE TABLE users (
id UUID PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
role TEXT NOT NULL DEFAULT 'member',
department TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE transcription_jobs (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
audio_filename TEXT NOT NULL,
audio_s3_key TEXT NOT NULL,
external_job_id TEXT UNIQUE,
status TEXT NOT NULL,
language TEXT,
duration_seconds INT,
project TEXT,
tags TEXT[],
created_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
CREATE TABLE transcripts (
job_id UUID PRIMARY KEY REFERENCES transcription_jobs(id),
full_text TEXT NOT NULL,
metadata JSONB,
summary TEXT,
search_vector tsvector GENERATED ALWAYS AS (
to_tsvector('english', full_text)
) STORED
);
CREATE INDEX idx_search ON transcripts USING gin(search_vector);
CREATE INDEX idx_jobs_user ON transcription_jobs(user_id);
CREATE INDEX idx_jobs_project ON transcription_jobs(project);
Die tsvector-Spalte gibt dir Postgres-Volltextsuche über den gesamten Transkript-Bestand, ohne einen weiteren Dienst hinzuzufügen. Bei sehr großen Beständen (Hunderttausende von Transkripten) lohnt sich ein Blick auf Meilisearch oder Elasticsearch.
Füge user_id zu den Jobs von Anfang an hinzu. Nach sechs Monaten wirst du wissen wollen, wer was hochgeladen hat. Das nachträglich zu ergänzen ist schmerzhaft.
Der Upload-Ablauf
Das Frontend sammelt die Datei plus Metadaten (Projekt, Sprache, Tags) und sendet einen multipart-POST. Das Backend lädt zu S3 hoch und reicht dann an deine Transkriptions-API weiter:
import express from 'express';
import multer from 'multer';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const s3 = new S3Client({ region: process.env.AWS_REGION });
app.post('/api/transcribe', requireAuth, upload.single('audio'), async (req, res) => {
const { project, tags, language } = req.body;
const user = req.user;
const s3Key = `audio/${user.id}/${Date.now()}-${req.file.originalname}`;
await s3.send(new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: s3Key,
Body: req.file.buffer,
ContentType: req.file.mimetype,
}));
const audioUrl = `https://${process.env.S3_BUCKET}.s3.amazonaws.com/${s3Key}`;
// Replace with whichever transcription API you choose.
// This example uses a URL-based submission (no re-upload needed).
const apiRes = await fetch('https://api.example-transcription.com/v1/transcribe', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.TRANSCRIPTION_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: 'url',
input_url: audioUrl,
language: language || 'auto',
}),
});
const { job_id: externalJobId } = await apiRes.json();
const result = await db.query(
`INSERT INTO transcription_jobs
(user_id, audio_filename, audio_s3_key, external_job_id, status, language, project, tags)
VALUES ($1, $2, $3, $4, 'queued', $5, $6, $7) RETURNING id`,
[user.id, req.file.originalname, s3Key, externalJobId,
language || 'auto', project, tags ? JSON.parse(tags) : []]
);
res.status(201).json({ job_id: result.rows[0].id });
});
Webhook-Handler
Webhooks schlagen Polling in Sachen Benutzererfahrung und Serverlast. Wenn die Transkriptions-API fertig ist, sendet sie einen POST an deinen Endpunkt – mit einer Signatur, die du prüfst, bevor du der Payload vertraust.
Der genaue Header-Name und das Signaturformat variieren je nach API. Prüfe beide in der Dokumentation deines gewählten Anbieters, bevor du den Handler schreibst. Ein generisches Muster, das bei den meisten funktioniert:
app.post('/api/webhooks/transcription',
express.raw({ type: 'application/json' }),
async (req, res) => {
// Verify HMAC signature, header name varies by vendor.
// Common patterns: X-Webhook-Signature, X-Signature-256, etc.
const sigHeader = req.header('X-Webhook-Signature') || '';
const sigValue = sigHeader.replace('sha256=', '');
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sigValue), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body.toString());
if (event.event === 'job.completed') {
const { job_id: externalJobId, result_url, duration } = event.data;
// Fetch the actual transcript from the result URL.
const transcriptRes = await fetch(result_url, {
headers: { 'Authorization': `Bearer ${process.env.TRANSCRIPTION_API_KEY}` },
});
const transcriptData = await transcriptRes.json();
await db.query(
`UPDATE transcription_jobs
SET status = 'completed', duration_seconds = $1, completed_at = NOW()
WHERE external_job_id = $2`,
[duration, externalJobId]
);
await db.query(
`INSERT INTO transcripts (job_id, full_text, metadata, summary)
SELECT id, $1, $2, $3 FROM transcription_jobs WHERE external_job_id = $4
ON CONFLICT (job_id) DO UPDATE
SET full_text = EXCLUDED.full_text,
metadata = EXCLUDED.metadata,
summary = EXCLUDED.summary`,
[
transcriptData.text,
JSON.stringify(transcriptData.metadata || {}),
transcriptData.summary || null,
externalJobId,
]
);
}
res.sendStatus(200);
}
);
Zwei Dinge musst du hier richtig machen: Bei fehlender oder ungültiger Signatur strikt ablehnen (die Payload nicht verarbeiten) und schnell mit 200 antworten. Jede langsame Verarbeitung sollte in eine Queue ausgelagert werden. Für einen tieferen Vergleich der Trade-offs zwischen Webhook und Polling siehe Webhook vs. Polling für Transkripte.
Volltextsuche
Postgres-Volltextsuche über deinen gesamten Bestand, mit Treffer-Snippets:
app.get('/api/search', requireAuth, async (req, res) => {
const { q, project, user_id, from, to } = req.query;
const result = await db.query(`
SELECT
j.id, j.audio_filename, j.created_at, j.project,
u.email AS user_email,
ts_headline(
'english', t.full_text,
plainto_tsquery('english', $1),
'StartSel=**, StopSel=**, MaxWords=30'
) AS snippet
FROM transcripts t
JOIN transcription_jobs j ON j.id = t.job_id
JOIN users u ON u.id = j.user_id
WHERE t.search_vector @@ plainto_tsquery('english', $1)
AND ($2::text IS NULL OR j.project = $2)
AND ($3::uuid IS NULL OR j.user_id = $3::uuid)
AND ($4::timestamptz IS NULL OR j.created_at >= $4)
AND ($5::timestamptz IS NULL OR j.created_at <= $5)
ORDER BY ts_rank(t.search_vector, plainto_tsquery('english', $1)) DESC
LIMIT 50
`, [q, project || null, user_id || null, from || null, to || null]);
res.json(result.rows);
});
Der GIN-Index auf search_vector hält das bei einem Bestand von Zehntausenden Transkripten schnell. Das Snippet zeigt den Treffer im Kontext – genau das macht die Suche wirklich brauchbar.
Zugriffskontrolle
Zwei Muster decken die meisten Teams ab.
Abteilungsbezogen: Jeder Nutzer sieht nur die Transkripte seiner Abteilung. Ergänze eine department-Spalte in der Jobs-Tabelle und filtere jede Abfrage nach der Abteilung des anfragenden Nutzers. Einfach und schwer falsch zu konfigurieren.
Projektbezogen: Nutzer gehören zu Projekten und sehen alle Transkripte ihrer Projekte. Flexibler, erfordert eine Join-Tabelle:
CREATE TABLE user_projects (
user_id UUID REFERENCES users(id),
project TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'member',
PRIMARY KEY (user_id, project)
);
Jede Abfrage joint dann gegen user_projects, um die Sichtbarkeit durchzusetzen. Setze das serverseitig durch, niemals im Client-Code. Ein Frontend-Flag ist ein Hinweis, kein Tor.
Für Teams ab mehr als 20 Personen solltest du frühzeitig SSO planen. Die meisten IT-Abteilungen werden irgendwann verlangen, dass jedes interne Tool, das sensibles Audio verarbeitet, in deinen Identitätsanbieter integriert ist (Okta, Azure AD, Google Workspace). Baue den Nutzerspeicher von Anfang an entkoppelt vom Authentifizierungsverfahren, damit du später einen SAML-Adapter hinzufügen kannst, ohne das Datenmodell umzubauen.
Das Dashboard-UI
Die 90-%-Lösung braucht fünf Ansichten:
- Upload-Formular mit Feldern für Projekt, Tags und Sprache.
- Jobliste mit aktuellen Transkriptionen, Status und Schnellaktionen (anzeigen, herunterladen, löschen).
- Transkriptansicht mit Volltext, Sprecher-Labels, Zeitstempeln und Schaltflächen zum Kopieren und Herunterladen.
- Suchseite mit Volltextabfrage und Filter-Dropdowns nach Projekt, Nutzer und Zeitraum.
- Admin-Ansicht mit Nutzung pro Nutzer und Abteilung, für Kostenzuordnung und Tarifplanung.
Mit Retool oder Internal.io steht das Dashboard an einem Nachmittag, wenn du die API schon hast. Eigenes Next.js dauert eine Woche, wenn du ein ausgereiftes UI willst, das nicht-technische Teamkollegen bequem bedienen können.
Datenaufbewahrung und Speicherkosten
Lege eine Aufbewahrungsrichtlinie fest, bevor du live gehst, nicht danach. Audiodateien sind groß; Transkripte sind winzig. Ein 60-minütiges Interview mit 128 kbps ist grob 60 MB groß. Ein Texttranskript desselben Interviews liegt unter 100 KB.
Ein praktischer Standard: Audio 30–90 Tage aufbewahren (lang genug, um zurückzugehen und nachzuprüfen, kurz genug, um die Speicherkosten im Zaum zu halten), Transkripte unbefristet. In rechtlichen, medizinischen oder finanziellen Compliance-Kontexten kann eine längere Audio-Aufbewahrung nötig sein. Baue den Aufbewahrungszeitraum als konfigurierbares Feld am Workspace- oder Abteilungsdatensatz ein, damit du für verschiedene Teams verschiedene Richtlinien setzen kannst.
Verlasse dich nie auf manuelles Löschen. Schreibe einen Aufräum-Job, der nach einem Zeitplan läuft und Audiodateien löscht, deren Aufbewahrungsfrist abgelaufen ist.
Damit das Kostenmodell des internen Tools aufgeht, muss der Speicher günstig bleiben. Cloudflare R2 und Backblaze B2 sind beide deutlich günstiger als S3 bei reinem Speicher und ausgehendem Datenverkehr (Egress). Überlegenswert, wenn das Audiovolumen hoch ist.
Dinge, die sich lohnen, später zu ergänzen
Slack- oder Notion-Benachrichtigungen. Poste automatisch eine Zusammenfassung in einen Projekt-Kanal, sobald ein Transkript fertig ist. Bei Slack genügt eine Webhook-URL pro Kanal – einfach und ohne App-Review. Siehe Transkription mit Slack integrieren und Transkription mit Notion integrieren.
Audit-Logs. Wer auf welches Transkript zugegriffen hat, wann. Wichtig für regulierte Branchen und in jeder Organisation nützlich. Ergänze eine access_log-Tabelle, die Lesezugriffe protokolliert, nicht nur Schreibzugriffe.
Eigene Ausgabevorlagen. Führe eigene Prompts gegen den Transkripttext aus, um unternehmensspezifische Ausgaben zu erzeugen (strukturierte Meeting-Notizen, Fallzusammenfassungen, Analysen von Verkaufsgesprächen), die über das hinausgehen, was die Transkriptions-API liefert. Siehe die Batch-Verarbeitungsmuster in Batch-Transkription für große Projekte.
Stimmregistrierung für Sprecher-Labels. Wenn deine API Speaker-Diarisierung unterstützt, kannst du die Label-Genauigkeit verbessern, indem du kurze Stimmproben für Teammitglieder registrierst. So werden aus „Sprecher 0 / Sprecher 1“ automatisch echte Namen.
TOTP- oder Hardware-Key-2FA. Sobald sensibles Audio auf deinem internen Tool liegt, zählt die Authentifizierungsschicht. Ergänze einen zweiten Faktor, bevor das Tool wirklich Vertrauliches verarbeitet.
Häufige Stolperfallen
Die Datenbank bei „einfachen“ Projekten überspringen. Eine JSON-Datei oder Tabelle funktioniert im Kleinen und wird bei 50 Transkripten unmöglich. Nimm Postgres vom ersten Tag an. Allein die Volltextsuche lohnt sich.
user_id bei Jobs nicht von Anfang an erfassen. Nach sechs Monaten wirst du wissen wollen, wer was transkribiert hat – für Kostenzuordnung, Compliance oder Debugging. Das nachträglich zu ergänzen bedeutet eine Migration und eine Lücke in den historischen Daten.
Fehlende Spracherfassung. Mehrsprachige Teams brauchen das. Erfasse die Sprache beim Einreichen, speichere sie am Job und nimm sie in die Suchfilter auf. Die Transkriptionsgenauigkeit variiert je nach Sprache erheblich zwischen verschiedenen APIs.
Meeting-Bot-Autoaufnahme selbst bauen. Das ist schwieriger, als es aussieht. Wenn dein Hauptanwendungsfall die Aufzeichnung von Zoom- oder Meet-Calls ist, prüfe zuerst Otter oder Fireflies – auch wenn du später einen Wrapper darum baust.
Davon ausgehen, dass das Transkript in der Webhook-Payload steckt. Die meisten Produktions-APIs (auch die hier als Vorbild dienenden) senden eine result_url im Webhook, nicht den vollen Transkripttext. Dein Handler muss das Ergebnis als zweiten Schritt abrufen, nachdem er die Signatur geprüft hat.
Der praktische nächste Schritt
Setz die Minimalversion (Upload + Webhook-Handler + Transkriptansicht) an einem Tag auf. Bring drei oder vier Teammitglieder dazu, es zu nutzen, bevor du Suche oder Dashboard-Feinschliff ergänzt. Die ersten echten Nutzer zeigen dir, was du als Nächstes bauen solltest.
Wenn du nur ein sauberes Transkript brauchst, ohne selbst etwas zu bauen, verarbeitet ConvertAudioToText Datei-Uploads direkt im Browser – ganz ohne Konto für einen schnellen Test.
Häufige Fragen
Was kostet der Betrieb eines internen Transkriptionstools im Vergleich zur Bezahlung pro Sitzplatz?
Bei 30 Nutzern kostet Otter.ai Business rund 600 $ pro Monat und Fireflies Business rund 570 $ pro Monat. Ein internes Tool auf Basis einer nutzungsbasierten API (wie Deepgram Nova-3 mit 0,0077 $/Minute) verlagert die Kosten auf die tatsächliche Nutzung. Ein Team mit im Schnitt einer Stunde Audio pro Person und Monat zahlt für 30 Nutzer etwa 14 $ an API-Gebühren, plus dein Hosting und Speicher. Der Wendepunkt, ab dem sich das Selbstbauen lohnt, liegt typischerweise bei etwa 10–15 aktiven Nutzern.
Welche Muster der Zugriffskontrolle eignen sich am besten für Team-Transkriptionstools?
Zwei Muster decken die meisten Fälle ab. Abteilungsbezogen: Jeder Nutzer sieht nur die Transkripte seiner Abteilung, erzwungen über eine Abteilungsspalte in der Jobs-Tabelle und gefilterte Abfragen. Projektbezogen: Nutzer können zu jedem Projekt transkribieren, dem sie angehören, erzwungen über eine Join-Tabelle. Das projektbezogene Modell ist flexibler und lässt sich später leichter erweitern. Beide erfordern serverseitige Durchsetzung, keine clientseitigen Flags.
Brauche ich SSO, um ein internes Transkriptionstool zu bauen?
Anfangs nicht, aber plane es ein. Die meisten IT-Teams blockieren ein neues internes Tool ab mehr als 20 Nutzern, wenn es ein separates Passwort erfordert. Baue deine Authentifizierungsschicht so, dass der Nutzerspeicher vom Authentifizierungsverfahren vom ersten Tag an entkoppelt ist. Google OAuth lässt sich schnell ausliefern und deckt die meisten Teams ab. SAML/OIDC (für Okta, Azure AD usw.) lässt sich später als Adapter ergänzen, ohne das Datenmodell anzufassen.
Wie sollte ich die Aufbewahrung von Audiodateien handhaben, um Speicherkosten zu kontrollieren?
Lege eine Richtlinie vom ersten Tag an fest, nicht später. Audiodateien sind groß; Transkripte sind winzig. Ein praktischer Standard: Audio 30–90 Tage aufbewahren, Transkripte unbefristet. In regulierten Branchen (Gesundheitswesen, Recht) kann eine längere Audio-Aufbewahrung erforderlich sein; mache daher den Aufbewahrungszeitraum pro Workspace oder Abteilung konfigurierbar. Verlasse dich nie auf manuelles Löschen.
Quellen
- Otter.ai Preise (verifiziert im Juli 2026)
- Fireflies.ai Preise (verifiziert im Juli 2026)
- Deepgram Preise (verifiziert im Juli 2026)
- ConvertAudioToText Preise (verifiziert im Juli 2026)
Try transcription free
Convert any audio or video to clean, unwatermarked text — speaker labels, timestamps, and AI summaries included. First 10 minutes free, no account.
Related Articles

Speechmatics Alternative for Non-Developers: Web Transcription Without Code
Speechmatics is genuinely excellent for developers: 50 hours free per month, 56 languages, on-prem deployment. If you need a drag-and-drop web app with flat $9.99/mo pricing instead of an API, here is an honest comparison of the two.

Best Transcription Tools with API Access (2026)
Which transcription SaaS tools actually give you API keys, and on which plan? Verified pricing and plan gates for Descript, Sonix, Fireflies, Happy Scribe, AssemblyAI, and more.