Transcriptie in Make.com: de webhook-first pipeline

Een transcriptieopdracht heeft minuten nodig om klaar te zijn. Een Make-scenario wordt per module-uitvoering afgerekend. Die twee feiten trekken aan elkaar, en het oplossen van die spanning is waar deze handleiding eigenlijk over gaat.


De voor de hand liggende opzet — een bestand versturen en dan wachten, controleren, wachten, controleren tot het klaar is — kost niets op een runner die je zelf host. Op Make is die niet gratis. Elke wachtstap en elke statuscontrole is een operatie, en een opdracht van vier minuten kan er dertig verbranden zonder iets anders te doen dan vragen: “zijn we er al?”


We bouwen het daarom andersom. Inwista laat het aan Make weten zodra het werk klaar is, en het scenario dat dat opvangt is drie modules lang.


Aan het eind heb je:


  1. Een ontvangerscenario dat wakker wordt zodra een transcriptie klaar is
  2. Een verzendscenario dat bestanden aan Inwista overdraagt en daarna stopt
  3. Handtekeningverificatie, zodat de ontvanger alleen echte events vertrouwt
  4. Optionele uitbreidingen — vertalingen parallel uitzetten, deduplicatie en een opruimronde voor bewaartermijnen


Alles draait op de openbare Inwista API v1 via de standaard HTTP-module van Make. Geen eigen app, geen code.

Voordat je begint

Je hebt drie dingen nodig:


  • Een Make-account — het gratis pakket volstaat om dit te bouwen, al is het plafond aan operaties krap voor productie
  • Een Inwista-API-sleutel — maak er een aan onder Mijn werkruimte → API-sleutels. Hij begint met inw_live_
  • Media die de API kan bereiken — de API verwacht een openbare https-URL, dus bestanden in Dropbox, Google Drive, S3 of je CMS hebben een deelbare of ondertekende link nodig


Je hoeft nergens een webhook-endpoint te hosten. Make geeft je de URL.


Een opmerking over de kosten voordat je iets bouwt dat onbewaakt draait: transcriptie wordt afgerekend tegen 4 credits per begonnen minuut media, in rekening gebracht zodra de opdracht wordt aangenomen. Mislukte opdrachten worden automatisch vergoed. Richt het scenario eerst op een paar korte testbestanden voordat je het op je archief loslaat.

Waarom de opzet hier uitmaakt

Beide opzetten werken. Ze kosten alleen heel verschillende bedragen, en op Make stapelt dat verschil zich elke maand op.


Een polling-scenario voor een opdracht die in ongeveer vier minuten klaar is en elke twintig seconden controleert, ziet er ruwweg zo uit: één keer versturen, dan twaalf rondes van wachten plus statuscontrole plus router, daarna ophalen en afleveren. Reken op 39 operaties per bestand.


De webhook-versie splitst zich in twee scenario's. De verzender is een trigger en één HTTP-aanroep. De ontvanger is een webhook, een parse, een ophaalactie en een aflevering. Reken op 6 operaties per bestand.


Bij 200 opnames per maand is dat 7.800 operaties tegenover 1.200 — het verschil tussen een hoger pakket en een afrondingsfout. Het haalt ook de twee faalscenario's weg die polling in productie treffen: een uitvoering die lang genoeg loopt om tegen het plafond van 40 minuten van Make aan te lopen, en een vastgelopen opdracht die stilletjes de hele nacht doordraait.


Bouw eerst de ontvanger. Dat is het deel dat moet kloppen.

Stap 1: de ontvanger aanmaken

Nieuw scenario. Voeg een module toe, kies Webhooks → Custom webhook, klik op Add, geef hem een naam als inwista-transcripts en kopieer de URL die Make je geeft.


Open, voordat je dat venster sluit, de geavanceerde instellingen van de webhook en zet JSON pass-through aan.


Dit is de instelling die iedereen over het hoofd ziet, en het loont om hem te begrijpen in plaats van hem klakkeloos over te nemen. Inwista ondertekent elke levering met een HMAC over de ruwe bytes van de request body. Als Make de JSON alvast voor je parseert, zijn precies die bytes verdwenen — inclusief sleutelvolgorde en witruimte — en kun je de handtekening niet meer reproduceren. Pass-through geeft je de body ongemoeid als één tekenreeks: dat kost je verderop één extra module om te parseren, en het geeft je überhaupt de mogelijkheid om iets te verifiëren.


Plak die URL nu in Inwista onder Mijn werkruimte → Integraties, abonneer hem op transcript.completed en kopieer het ondertekeningsgeheim dat je te zien krijgt.

Stap 2: de handtekening verifiëren

Klik met de rechtermuisknop op de verbinding die van de webhook wegloopt en voeg een filter toe. De functie sha256() van Make berekent een HMAC zodra je er een sleutel aan meegeeft, dus de hele controle past in één expressie — geen cryptomodule, geen code.


Voorwaarde, Text: Equal to:

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

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


Twee details die je anders een middag kosten. Make levert binnenkomende headernamen in kleine letters aan, dus het is x-inwista-signature, niet de schrijfwijze met hoofdletters die je in onze documentatie ziet. En de headerwaarde draagt het voorvoegsel sha256=, dus zet het er zoals hierboven vóór of haal het weg voordat je vergelijkt.


Alles wat het filter niet haalt, stopt gewoon. Een niet-ondertekend verzoek bereikt de modules die werk doen nooit.


Voeg na het filter een JSON → Parse JSON-module toe die naar 1.data wijst. Vanaf hier gedraagt de payload zich als elke andere Make-bundle.

Stap 3: ophalen en afleveren

Het event bevat al alles wat je nodig hebt:

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


Voeg HTTP → Get a file toe en map de URL naar {{2.files[1].url}}.


Die [1] is geen typefout. Arrays in Make beginnen bij 1, en wie uit gewoonte [0] pakt, krijgt een lege waarde in plaats van een fout — die reist vervolgens stilletjes verder en duikt drie dagen later op als een bestand van nul bytes in Drive.


Die URL's zijn ondertekend en 24 uur geldig. Haal het bestand op; bewaar de link niet.


Hang er daarna aan wat “klaar” voor jouw team betekent — Google Drive, Dropbox, S3, Slack, een HTTP-aanroep naar je CMS, een rij in Airtable of Notion.


Drie modules en een filter. Dat is de hele ontvanger, en hij verwerkt elke voltooide opdracht in de werkruimte — inclusief opnames die collega's met de hand via het dashboard uploaden, waar geen enkel polling-scenario ooit weet van zou hebben gehad.

Stap 4: de verzender

Tweede scenario. Begin met het event dat “er is iets nieuws” betekent: een bewakingsmodule voor Google Drive of Dropbox, een Custom webhook vanuit je eigen CMS, of een geplande query over een database. Om te testen start je hem gewoon met de hand.


Zijn enige taak is een openbaar bereikbare URL opleveren. Voeg HTTP → Make a request toe:


  • URL — https://api.inwista.ai/v1/transcriptions
  • Methode — POST
  • Headers — Authorization: Bearer inw_live_your_key_here
  • Headers — Idempotency-Key: {{md5(1.fileUrl)}}
  • Body-type — Raw, contenttype JSON


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


Drie velden die je moet begrijpen:


  • language is de taal die in het bestand wordt gesproken, niet de taal die je eruit wilt krijgen. Transcriberen in de brontaal levert nauwkeurige tijdcodes en schone tekst op; de vertaling komt daarna, op de voltooide transcriptie. Verwerkt je pipeline meerdere talen, map dan de map van de trigger naar dit veld.


  • diarization zet sprekerlabels aan. Laat het uit bij content met één spreker — het kost verwerkingstijd die je niet nodig hebt.


  • metadata is van jou. Tot 1 KB aan wat je maar wilt, bij elke uitlezing en in de webhook letterlijk teruggegeven — zo weet de ontvanger bij welk scenario, welke cursus of welk dossier een bestand hoort, zonder ergens te hoeven opzoeken.


Let op waar de idempotentiesleutel van wordt afgeleid. Afleiden uit de uitvoering beschermt je tegen een nieuwe poging van die ene module. Afleiden uit het bestand, zoals hierboven, beschermt je bovendien tegen dezelfde opname die door twee verschillende runs twee keer wordt ingediend — verreweg de meest voorkomende manier waarop mensen per ongeluk dubbel betalen.


Het antwoord komt meteen terug met status: "processing" en een id. Het scenario eindigt daar. Dat is de opzet die werkt, niet de opzet die faalt.

Vertalingen parallel uitzetten met een Iterator

Eén opname omzetten naar zes talen: daar verdient de array-verwerking van Make zichzelf terug.


Voeg in de ontvanger, zodra de transcriptie binnen is, een Tools → Set variable toe met je lijst doeltalen, dan een Iterator die erover loopt, en daarna één enkele HTTP-module binnen de lus:

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

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


Elke aanroep levert 202 Accepted op. De vertaling draait op de voltooide transcriptie, dus de timing klopt al en alleen de tekst verandert. Zes talen kosten zes operaties, en de voltooide bestanden komen binnen via dezelfde webhook die je al hebt gebouwd.


Als je ze ophaalt, vraag er dan expliciet om:

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


De taalparameter is streng. Een taal opvragen zonder voltooide vertaling levert een expliciete fout op in plaats van stilzwijgend de brontaal — precies wat je wilt in een pipeline waar niemand naar kijkt.

Ondertitels van broadcastkwaliteit

Ruwe transcriptie is letterlijk. Ondertitelen is een vak: regellengtes, leessnelheid, de plek waar een zin over twee blokken breekt.


Nog één HTTP-module, POST /v1/transcriptions/{id}/enhance, haalt de transcriptie door die behandeling heen — tekst inkorten, regelafbrekingen opnieuw uitbalanceren, dialogen opmaken, blokduren afdwingen:

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


Daarna serveren de ondertitel-endpoints automatisch de verbeterde versie. Verderop verandert er niets.

Als er iets misgaat: de foutroutes van Make

Klik met de rechtermuisknop op een willekeurige module en kies Add error handler. Make geeft je directieven die een gewone IF-tak niet kan uitdrukken:


  • Break — parkeert de uitvoering in Incomplete Executions, zodat je de oorzaak verhelpt en precies die bundle opnieuw draait. Zet dit op de verzendmodule.
  • Ignore — logt het en gaat door. Passend voor een afleverstap die meegenomen is.
  • Resume — zet er een terugvalwaarde voor in de plaats en gaat verder.
  • Rollback — draait vastgelegd werk in transactionele modules terug.


Elke fout van Inwista komt terug in dezelfde structuur:

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


Vertak op code, nooit op de tekst van het bericht. Codes komen er binnen v1 alleen bij en worden nooit hernoemd; berichten kunnen op elk moment worden herschreven.


Aan onze kant wordt webhook-levering drie keer opnieuw geprobeerd, en een endpoint dat blijft falen wordt automatisch uitgeschakeld, met de reden zichtbaar in je integratie-instellingen — zo komt een kapotte ontvanger naar voren als een status die je kunt zien, in plaats van events die geruisloos verdwijnen.

Niet twee keer hetzelfde bestand transcriberen

Triggers op bewaakte mappen vuren opnieuw. Bestanden worden hernoemd. Iemand uploadt iets nog een keer.


Make heeft daar een eigen antwoord op: de Data store. Maak er een aan met de ID van het bronbestand als sleutel, en voeg in de verzender vóór de HTTP-aanroep Data store → Get a record toe, filter erop dat het record niet bestaat, en zet Add a record na een geslaagde inzending.


Twee operaties om geen dubbele transcriptie te betalen. De idempotentiesleutel dekt nieuwe pogingen van één module af; de data store dekt al het overige af.

Gevoelige opnames

Verwerkt je pipeline materiaal waarvan je liever niet hebt dat wij het bewaren — patiëntgesprekken, juridische opnames, interne personeelsbijeenkomsten — voeg dan één veld toe aan de inzending:

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


Met retention: "none" wordt het bronmateriaal verwijderd zodra de transcriptie klaar is. Transcriptie, ondertitels, vertalingen en latere verbeteringen blijven gewoon werken — alleen de audio en video zijn weg. Er is ook store_media: false, dat het bestand voor de verwerking bewaart maar geen afspeelkopieën aanmaakt.


Aan het andere eind van de levenscyclus wist DELETE /v1/transcriptions/{id} met één aanroep alles van een opdracht. Een gepland scenario dat uit diezelfde data store de opdracht-ID's ophaalt die ouder zijn dan je bewaartermijn en ze verwijdert, is vier modules — en het maakt van je bewaarbeleid iets dat je kunt laten zien in plaats van beschrijven.

Wanneer polling toch het juiste antwoord is

Drie gevallen pleiten er echt voor: je kunt geen webhook naar buiten aanbieden, je hebt de transcriptie binnen dezelfde uitvoering nodig om een synchroon verzoek te beantwoorden, of je draait een eenmalige inhaalslag waarbij het aantal operaties niet uitmaakt.


Voeg dan een Tools → Sleep-module toe en een statuscontrole tegen GET /v1/transcriptions/{id}, en houd twee plafonds aan: Sleep zit op maximaal 300 seconden per module, en een scenario-uitvoering op 40 minuten. Vraag elke 20 tot 30 seconden op in plaats van elke vijf — het antwoord bevat een veld progress dat de werkelijke positie in de pipeline weergeeft, dus ook een tragere lus geeft je iets eerlijks om te tonen.


Wil je dat patroon liever volledig uitgewerkt zien: onze n8n-versie van deze handleiding gebruikt polling van begin tot eind, omdat de lus op een zelfgehoste runner gratis is.

Waar je vroeg of laat tegenaan loopt

Arrays beginnen bij 1. files[1] is het eerste bestand. files[0] geeft leeg terug in plaats van te falen.


Pass-through en parseren zijn een afweging. Je kunt geen handtekening verifiëren tegen een body die Make al heeft geparseerd. Pass-through plus een Parse JSON-module is de enige juiste volgorde.


Rate limits. 300 leesacties en 60 schrijfacties per minuut per sleutel. Ruim voor normaal gebruik, snel bereikt als je een heel archief over parallelle scenarioruns uitwaaiert. Verwerk inhaalslagen in batches.


Bron-URL's moeten bereikbaar zijn. De API peilt het materiaal voordat hij de opdracht aanneemt — zo staan duur en kosten vooraf vast. Een Drive-link die om een login vraagt geeft unreadable_source, en er wordt niets in rekening gebracht. Gebruik directe of ondertekende URL's.


Alles telt mee. Sleep-modules, routers, iteratorrondes en filters die doorlaten verbruiken allemaal operaties. Voelt een scenario duur, tel dan de modules voordat je de API de schuld geeft.

De voltooide ontvanger

Hier is het skelet van de blueprint. Importeer het en hang er dan je eigen webhook en afleveringsmodule aan — en gebruik het verbindingsbeheer van Make in plaats van een sleutel in een module te plakken.

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


Drie modules. De polling-variant had er elf, en kostte zes keer zoveel om te draaien.

Wat dit echt verandert

De maat voor een automatisering is niet wat ze doet terwijl je toekijkt. De maat is wat ze op zondagnacht om twee uur doet, wanneer er een opname van negentig minuten binnenkomt en niemand wakker is.


Een scenario dat vier minuten per bestand draait en operaties verbrandt om een vraag te stellen waarvan het het antwoord al kent, is iets waar je uiteindelijk naar gaat kijken. Een ontvanger die wakker wordt, een handtekening verifieert, een bestand wegschrijft en weer gaat slapen, is iets waarvan je het bestaan vergeet — en dat vergeten is precies de bedoeling.


Vanaf dat punt houden ondertitels op een taak te zijn waar iemand verantwoordelijk voor is. Ze worden een eigenschap van elke opname die je organisatie maakt: doorzoekbaar, toegankelijk, compliant, en niemand heeft er een vergadering over hoeven beleggen.


Klaar om het te bouwen? Maak een API-sleutel aan — het gratis pakket is genoeg om dit van begin tot eind te draaien. De volledige endpointreferentie staat in de API-documentatie.