Hi everyone,
I’m building a workflow in n8n for a course project and I’m stuck on the final step of audio transcription.
The goal is to:
-Receive a voice message from Telegram
-Download the audio using Telegram → Get a File (via file_id)
-Convert the audio into text (speech-to-text)
-Send the transcription to an AI Agent
Question
What is the recommended and reliable way in n8n to: take a Telegram voice message (.oga / opus) send it to a non-OpenAI speech-to-text API (e.g. Groq Whisper or similar) and get a stable transcription output
Especially interested in:
best practice for handling Telegram binary audio files
whether there is a known limitation with multipart/form-data in HTTP Request node
recommended workaround if direct binary upload is unreliable
Any working pattern or example workflow would be highly appreciated
What is the error message (if any)?
Where I’m stuck
At this stage:
Telegram → Get a File works correctly
I receive the binary audio file (.oga, audio/ogg)
The workflow breaks when trying to send this file to a Whisper/STT API
I tested multiple approaches:
HTTP Request node with Form-Data + binary file
Code node with fetch + FormData
different binary mappings in n8n
But I always get errors like:
file must be one of [flac mp3 mp4 mpeg mpga m4a ogg opus wav webm]
file not received
multipart/form-data issues
invalid request / bad request
At this point, I can successfully download the file, but I cannot reliably send it to a transcription API.
Please share your workflow
Expected workflow (from tutorial)
The tutorial I'm following suggests:
Telegram → Get a File
OpenAI → Whisper → Transcribe a Recording
Connect Whisper output to an AI Agent
Use this expression in the AI Agent:
{{$json.message.text ?? $node["OpenAI"].json.text}}
So the agent can handle both:
text messages
audio transcriptions
BUT I am not using OpenAI!!
I want to avoid using OpenAI (cost reasons), so I'm trying to use an alternative speech-to-text solution (e.g. Groq Whisper API or similar).
First, drop the Code node with fetch/FormData, it’s fragile for binary. Use the HTTP Request node: set Body to Form-Data, add a parameter named file with type n8n Binary File, and set Input Data Field Name to your binary property, usually data. Add a model field set to whisper-large-v3, and point it at Groq’s endpoint https://api.groq.com/openai/v1/audio/transcriptions with a Bearer token header.
Second, the “file must be one of” error is about the filename, not the audio. Whisper reads the format from the extension, and Telegram sends .oga, which isn’t on the list. Rename the binary’s file name to end in .ogg and that error clears.
Then in the HTTP Request node, set Body to Form-Data, add a file parameter with type “n8n Binary File”, Input Data Field Name = data, and add model as a string field set to whisper-large-v3. That combination consistently works with Groq’s endpoint.
Good news: your file is fine - .oga IS ogg/opus, which is already in that allowed list. The “file must be one of […ogg opus…]” error is misleading: Groq/OpenAI Whisper detect the format from the FILENAME EXTENSION in the multipart upload, and Telegram’s “Get a File” gives the binary a name with no usable extension. So you’re sending the bytes correctly, but the API rejects them because the part’s filename isn’t *.ogg. Fix the filename and it just works - no conversion needed.
Step 1 - fix the binary filename. Right after Telegram → Get a File, add a Code node (Run Once for Each Item)
item.binary.data.fileName = 'audio.ogg';
item.binary.data.mimeType = 'audio/ogg';
return item;
```
(use whatever your binary property is named - it's usually `data`).
Step 2 - send to Groq with the HTTP Request node (use the native node, not a Code node + fetch - the native one builds multipart correctly):
- Method: POST
- URL: https://api.groq.com/openai/v1/audio/transcriptions
- Authentication -> Generic -> Header Auth: name `Authorization`, value `Bearer YOUR_GROQ_KEY`
- Send Body: ON, Body Content Type: Form-Data (multipart/form-data)
- Parameters:
- `file` -> Parameter Type: n8n Binary File (older versions call it "Form Binary Data") -> Input Data Field Name: `data`
- `model` -> (form field) -> whisper-large-v3 (or whisper-large-v3-turbo)
- optional `response_format` -> json
That's the whole thing. Groq is OpenAI-compatible, fast, and has a generous free tier - perfect for avoiding OpenAI on cost.
Step 3 - the AI Agent expression, just swap the node name:
`{{ $json.message.text ?? $node["HTTP Request"].json.text }}`
(Groq returns the transcription in `.text`, same shape as OpenAI.)
The gotchas that cause your exact errors:
- "file not received" -> the Form-Data param must be type Binary File, not a text field, and Input Field Name must match the binary property (`data`).
- "file must be one of [...]" -> blank/missing filename extension -> the Code-node rename fixes it.
- Do NOT set a manual `Content-Type: multipart/...` header - let n8n set the boundary automatically; a manual one breaks the request.
Stable on n8n cloud 2.23.x.
To confirm what @work6 laid out, that’s the complete working pattern. One thing to add if you’re still getting “file not received” even after the rename:
Telegram’s Get a File node sometimes returns application/octet-stream as the MIME type instead of audio/ogg. Groq’s API can reject this. Force both fields in your Code node:
item.binary.data.fileName = 'audio.ogg';
@Work6 included this, just making sure it doesn’t get skipped since it’s easy to miss.
Also for the AI Agent expression, if your HTTP Request node has a custom name, update it accordingly.
Hey @SilvyElba — that .oga + manual multipart/form-data dance is genuinely the painful part of going the raw HTTP Request route. The .oga → .ogg rename + Form-Data fix above is solid if you want to stay on Groq.
If you’re open to a managed multi-provider option that sidesteps the binary plumbing entirely, here’s another angle.
Disclosure: I work at Eden AI, so take this as one option among others — but it solves your exact blocker, so I figured it was worth sharing.
Eden AI is an aggregator (one API/key in front of Deepgram, AssemblyAI, Gladia, Amazon, Google, Whisper…), and there’s a community node: n8n-nodes-edenai. The relevant bit for you: its Expert Models node takes a binary property directly and uploads the file for you — no manual Form-Data, no .oga renaming, no “file not received”.
Quick recipe:
Settings → Community Nodes → Installn8n-nodes-edenai (and add your Eden AI API key as a credential).
Telegram Trigger → Telegram (Get File) so you have the voice file as binary.
Eden AI – Expert Models node:
Feature:audio
Subfeature:speech to text (async) — it’s an async job, the node polls until it’s done automatically.
Input Type:File → File Source:Binary Property → point it at your Telegram binary field (e.g. data). That’s the whole “binary handling” part.
Provider: pick whichever you like — Deepgram and Gladia are fast/cost-effective, AssemblyAI is strong on accuracy + diarization. You can swap providers from a dropdown without re-plumbing anything.
(Optional) Set a couple of Fallback Models in Options, so if your primary provider errors out it retries with the next — handy for the kind of 4xx flakiness you’re hitting.
The transcript comes back in the node output (no need to handle the multipart response yourself).
On cost, since that’s your main reason for leaving OpenAI: Eden AI charges provider pass-through pricing — no markup on the per-request price. The only margin is ~5.5%, applied when you top up credits. So you won’t beat Groq-direct on raw price, but you do get to pick the cheapest provider per job (Deepgram/Gladia are very competitive) and switch freely from one node. The real win here is killing the binary/form-data wrangling + multi-provider flexibility & fallback. Happy to share an example workflow JSON if useful.