This workflow corresponds to n8n.io template #17723 — we link there as the canonical source.
This workflow follows the Execute Workflow Trigger → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "UfGSlAVdkgxtNcTm",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "STT & TTS in whatsapp",
"tags": [],
"nodes": [
{
"id": "34e28c9e-c3f5-4feb-b3f7-714d15c0f3d3",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
912,
1680
],
"parameters": {
"width": 480,
"height": 912,
"content": "## STT & TTS in whatsapp\n\n### How it works\n\n1. The workflow triggers upon receiving a message or media via WhatsApp and extracts relevant parameters.\n2. It routes the payload based on whether the input is a video or text message.\n3. Videos are downloaded, uploaded to FreeConvert, converted to MP3, and checked periodically for completion.\n4. The resulting audio is transcribed using Google Gemini and sent back to the user on WhatsApp.\n5. Alternatively, text messages trigger a sub-workflow to generate speech audio and send it back via WhatsApp.\n\n### Setup steps\n\n- Configure WhatsApp API credentials for triggers and messaging nodes\n- Set up FreeConvert API credentials for video-to-audio conversion\n- Configure Google Gemini API credentials for audio transcription\n- Set up Fish Audio credentials if utilizing text-to-speech sub-workflows\n\n### Customization\n\nEnsure all API keys (WhatsApp, FreeConvert, Google Gemini, and Fish Audio) are properly configured in n8n credentials.\n\n@[youtube](GFD8sBwHheg)"
},
"typeVersion": 1
},
{
"id": "263cb5c5-4ebf-402b-b3f0-b8f615596c09",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1472,
1760
],
"parameters": {
"color": 7,
"width": 416,
"height": 320,
"content": "## Receive and extract WhatsApp message\n\nReceives incoming messages from WhatsApp and extracts message parameters like phone, type, media ID, and body text."
},
"typeVersion": 1
},
{
"id": "2dfeaeec-ead5-4f80-b8ad-ef974095ea0d",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1920,
1760
],
"parameters": {
"color": 7,
"width": 416,
"height": 400,
"content": "## Determine media type route\n\nEvaluates whether the incoming WhatsApp media is a video file or text/audio message."
},
"typeVersion": 1
},
{
"id": "00e7ed87-7f43-433e-8118-563fcde63193",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2368,
1760
],
"parameters": {
"color": 7,
"width": 416,
"height": 304,
"content": "## Download video and create job\n\nDownloads the video file from WhatsApp and initializes a conversion job with FreeConvert."
},
"typeVersion": 1
},
{
"id": "8f214288-f882-4f16-aa64-b03b4bffd95d",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2816,
1760
],
"parameters": {
"color": 7,
"width": 416,
"height": 304,
"content": "## Prepare and upload video file\n\nPrepares binary data and uploads the video file to the conversion service."
},
"typeVersion": 1
},
{
"id": "a3549ed3-c3f1-4358-9ccf-16e664026d0b",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
3264,
1760
],
"parameters": {
"color": 7,
"width": 640,
"height": 432,
"content": "## Monitor conversion job status\n\nWaits and repeatedly checks the conversion job status until processing is complete."
},
"typeVersion": 1
},
{
"id": "45e25a0b-248b-4997-b2aa-bfb53c025a35",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
3936,
1760
],
"parameters": {
"color": 7,
"width": 416,
"height": 304,
"content": "## Download MP3 and process data\n\nDownloads the converted MP3 audio and prepares it for transcription."
},
"typeVersion": 1
},
{
"id": "edb361de-fab1-4810-b1b5-8529d773a01e",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
4384,
1760
],
"parameters": {
"color": 7,
"width": 416,
"height": 320,
"content": "## Transcribe audio and send response\n\nTranscribes the audio recording using Google Gemini and sends the result back to the user via WhatsApp."
},
"typeVersion": 1
},
{
"id": "ed26f8d7-9ebc-4e19-b4b3-a2ca386a0a60",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
2384,
2096
],
"parameters": {
"color": 7,
"height": 352,
"content": "## Generate voice note from text\n\nInvokes a sub-workflow to generate voice notes from text messages."
},
"typeVersion": 1
},
{
"id": "e464b359-3bde-43d3-a493-34c20a7c5f49",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
1472,
2288
],
"parameters": {
"color": 7,
"width": 864,
"height": 304,
"content": "## Generate speech and send media\n\nHandles sub-workflow execution triggered by the main flow to convert text into speech and upload media back to WhatsApp."
},
"typeVersion": 1
},
{
"id": "080c0baa-ecb1-479e-99b2-8cebbc13d05b",
"name": "When WhatsApp Message Received",
"type": "n8n-nodes-base.whatsAppTrigger",
"position": [
1520,
1920
],
"parameters": {
"options": {
"messageStatusUpdates": [
"sent"
]
},
"updates": [
"messages"
]
},
"credentials": {
"whatsAppTriggerApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "2afe6692-d0ca-499d-b815-0f99a675ca69",
"name": "Set Message Fields",
"type": "n8n-nodes-base.set",
"position": [
1744,
1920
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a1",
"name": "phone",
"type": "string",
"value": "={{ $json.messages[0].from }}"
},
{
"id": "a2",
"name": "type",
"type": "string",
"value": "={{ $json.messages[0].type }} {{ $json.messages[0].document.mime_type }}"
},
{
"id": "a3",
"name": "media_id",
"type": "string",
"value": "={{ $json.messages[0].document.url }}"
},
{
"id": "a4",
"name": "text_body",
"type": "string",
"value": "={{ $json.messages[0].text?.body || '' }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "fbe6d57b-82c2-4410-a026-5c68b038c302",
"name": "Execute Voice Note Workflow",
"type": "n8n-nodes-base.executeWorkflow",
"position": [
2432,
2288
],
"parameters": {
"mode": "each",
"options": {},
"workflowId": {
"__rl": true,
"mode": "list",
"value": "UfGSlAVdkgxtNcTm",
"cachedResultUrl": "/workflow/UfGSlAVdkgxtNcTm",
"cachedResultName": "STT & TTS in whatsapp"
},
"workflowInputs": {
"value": {
"phone": "={{ $json.phone }}",
"text_body": "={{ $json.text_body.replace('/tts ', '') }}"
},
"schema": [
{
"id": "phone",
"type": "string",
"display": true,
"required": false,
"displayName": "phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "text_body",
"type": "string",
"display": true,
"required": false,
"displayName": "text_body",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": true
}
},
"typeVersion": 1.2
},
{
"id": "80151d6e-b455-4aeb-b899-2929472abcc3",
"name": "When Called by Main Workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"position": [
1520,
2416
],
"parameters": {
"workflowInputs": {
"values": [
{
"name": "phone"
},
{
"name": "text_body"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "ae9ea0c7-ee48-47c3-a0e3-7565b868172d",
"name": "Post Conversion Job",
"type": "n8n-nodes-base.httpRequest",
"position": [
2640,
1888
],
"parameters": {
"url": "https://api.freeconvert.com/v1/process/jobs",
"method": "POST",
"options": {},
"jsonBody": "={\n \"tasks\": {\n \"import-1\": { \"operation\": \"import/upload\" },\n \"convert-1\": { \"operation\": \"convert\", \"input\": \"import-1\", \"output_format\": \"mp3\" },\n \"export-1\": { \"operation\": \"export/url\", \"input\": \"convert-1\" }\n }\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer YOUR_TOKEN_HERE"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "8e2c8f2c-c1ef-4469-9ad7-ee8601b235b8",
"name": "Upload File for Conversion",
"type": "n8n-nodes-base.httpRequest",
"position": [
3088,
1888
],
"parameters": {
"url": "={{ $json.tasks.find(t => t.name === 'import-1').result.form.url }}",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"name": "signature",
"value": "={{ $json.tasks.find(t => t.name === 'import-1').result.form.parameters.signature }}"
},
{
"name": "file",
"parameterType": "formBinaryData",
"inputDataFieldName": "data"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "143acf14-3f31-45c0-ae5f-e9432e89433e",
"name": "Wait for Conversion Status",
"type": "n8n-nodes-base.wait",
"position": [
3312,
1968
],
"parameters": {},
"typeVersion": 1.1
},
{
"id": "f43f7242-4210-4c1b-9d76-120cdd218522",
"name": "Fetch Conversion Job Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
3536,
1888
],
"parameters": {
"url": "=https://api.freeconvert.com/v1/process/jobs/{{ $('Post Conversion Job').item.json.id }}",
"options": {},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer YOUR_TOKEN_HERE"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "c3d72513-0fd3-440a-85bb-c183b28c00e4",
"name": "If Conversion Complete",
"type": "n8n-nodes-base.if",
"position": [
3760,
1968
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "completed"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "c5bc0bfa-07ac-441d-a4c6-2ad006ae603e",
"name": "Fetch Converted MP3",
"type": "n8n-nodes-base.httpRequest",
"position": [
3984,
1888
],
"parameters": {
"url": "={{ $json.tasks.find(t => t.name === 'export-1').result.url }}",
"options": {
"response": {
"response": {}
}
}
},
"typeVersion": 4.2
},
{
"id": "71413998-7483-4317-b5eb-b7cbde6285e8",
"name": "Generate Speech with FishAudio",
"type": "n8n-nodes-fishaudio.fishaudio",
"position": [
1744,
2416
],
"parameters": {
"text": "={{ $('When Called by Main Workflow').item.json.text_body }}",
"model": "=s2.1-pro-free",
"voiceId": {
"__rl": true,
"mode": "id",
"value": "92b9f644244f4571ba58d696a6ef5e67"
},
"additionalOptions": {
"speed": 1
}
},
"credentials": {
"fishaudioApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "bdb359ed-0b72-4798-a8ba-1955c84da0b8",
"name": "Upload media",
"type": "n8n-nodes-base.whatsApp",
"position": [
1968,
2416
],
"parameters": {
"resource": "media",
"phoneNumberId": "1083481144853090",
"additionalFields": {}
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "b23075e4-fdf5-4bc7-9454-b4938230c543",
"name": "Send Voice Note Confirmation",
"type": "n8n-nodes-base.whatsApp",
"position": [
2192,
2416
],
"parameters": {
"mediaId": "={{ $json.id }}",
"mediaPath": "useMediaId",
"operation": "send",
"messageType": "audio",
"phoneNumberId": "1083481144853090",
"additionalFields": {},
"recipientPhoneNumber": "={{ $('When Called by Main Workflow').item.json.phone }}"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "25401f60-e48a-4d24-a1bc-1011ecb42429",
"name": "If Text Conversion Requested",
"type": "n8n-nodes-base.if",
"position": [
2192,
1984
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "17ee0ff6-86ce-4843-b7e8-133f9756583b",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.text_body }}",
"rightValue": "/tts"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "a822c28f-83c9-44f1-a053-e3ed8c0e4cb5",
"name": "Prepare File Upload",
"type": "n8n-nodes-base.code",
"position": [
2864,
1888
],
"parameters": {
"jsCode": "// Code node: \"Prepare Upload\"\n// Re-attaches the original OGG binary (from Download Voice Sample)\n// to the current item (Create Job's JSON response), and validates the job response.\n\nconst jobResponse = $input.item.json;\n\nif (!jobResponse.tasks || !Array.isArray(jobResponse.tasks)) {\n throw new Error('Create Job response missing \"tasks\" array \u2014 check FreeConvert API response for errors: ' + JSON.stringify(jobResponse));\n}\n\nconst importTask = jobResponse.tasks.find(t => t.name === 'import-1');\n\nif (!importTask || !importTask.result || !importTask.result.form) {\n throw new Error('Could not find import-1 task with a valid upload form in the job response: ' + JSON.stringify(jobResponse));\n}\n\nconst originalBinary = $('Fetch Incoming Video').item.binary;\n\nif (!originalBinary || !originalBinary.data) {\n throw new Error('No binary data found on \"Download Voice Sample\" item \u2014 check the binary property name.');\n}\n\nreturn {\n json: jobResponse,\n binary: originalBinary\n};"
},
"typeVersion": 2
},
{
"id": "1a9cf410-1c94-42bf-84bb-f1d3555a2219",
"name": "Transcribe Audio with Gemini",
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"position": [
4432,
1920
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "models/gemini-3.1-flash-lite",
"cachedResultName": "models/gemini-3.1-flash-lite"
},
"options": {},
"resource": "audio",
"simplify": false,
"inputType": "binary"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "3256eb0c-1096-482c-b3e9-25829800042b",
"name": "Process Audio Metadata",
"type": "n8n-nodes-base.code",
"position": [
4208,
1888
],
"parameters": {
"jsCode": "for (const item of $input.all()) {\n // Check if binary data exists (adjust 'data' if your binary property has a different name)\n if (item.binary && item.binary.data) {\n item.binary.data.mimeType = \"audio/mpeg\";\n }\n}\n\nreturn $input.all();"
},
"typeVersion": 2
},
{
"id": "f5a08e99-7884-442d-b6b6-ad09503c59a6",
"name": "Send Transcription to WhatsApp",
"type": "n8n-nodes-base.whatsApp",
"position": [
4656,
1920
],
"parameters": {
"textBody": "={{ $json.candidates[0].content.parts[0].text }}",
"operation": "send",
"phoneNumberId": "1083481144853090",
"additionalFields": {},
"recipientPhoneNumber": "={{ $('When WhatsApp Message Received').item.json.contacts[0].wa_id }}"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "cc712203-846d-4d33-9c21-976d5c3aac5f",
"name": "If Message Type Video",
"type": "n8n-nodes-base.if",
"position": [
1968,
1888
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "7cb4a67d-77dc-49cc-82ba-b010bfd22516",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.type }}",
"rightValue": "video"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "c8ce1cdd-3387-402f-bae1-ba91f6a530f2",
"name": "Fetch Incoming Video",
"type": "n8n-nodes-base.httpRequest",
"position": [
2416,
1888
],
"parameters": {
"url": "={{ $json.media_id }}",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "whatsAppApi"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "3195080d-b041-46c5-975c-e71695ec372f",
"nodeGroups": [],
"connections": {
"Upload media": {
"main": [
[
{
"node": "Send Voice Note Confirmation",
"type": "main",
"index": 0
}
]
]
},
"Set Message Fields": {
"main": [
[
{
"node": "If Message Type Video",
"type": "main",
"index": 0
}
]
]
},
"Fetch Converted MP3": {
"main": [
[
{
"node": "Process Audio Metadata",
"type": "main",
"index": 0
}
]
]
},
"Post Conversion Job": {
"main": [
[
{
"node": "Prepare File Upload",
"type": "main",
"index": 0
}
]
]
},
"Prepare File Upload": {
"main": [
[
{
"node": "Upload File for Conversion",
"type": "main",
"index": 0
}
]
]
},
"Fetch Incoming Video": {
"main": [
[
{
"node": "Post Conversion Job",
"type": "main",
"index": 0
}
]
]
},
"If Message Type Video": {
"main": [
[
{
"node": "Fetch Incoming Video",
"type": "main",
"index": 0
}
],
[
{
"node": "If Text Conversion Requested",
"type": "main",
"index": 0
}
]
]
},
"If Conversion Complete": {
"main": [
[
{
"node": "Fetch Converted MP3",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait for Conversion Status",
"type": "main",
"index": 0
}
]
]
},
"Process Audio Metadata": {
"main": [
[
{
"node": "Transcribe Audio with Gemini",
"type": "main",
"index": 0
}
]
]
},
"Upload File for Conversion": {
"main": [
[
{
"node": "Wait for Conversion Status",
"type": "main",
"index": 0
}
]
]
},
"Wait for Conversion Status": {
"main": [
[
{
"node": "Fetch Conversion Job Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch Conversion Job Status": {
"main": [
[
{
"node": "If Conversion Complete",
"type": "main",
"index": 0
}
]
]
},
"If Text Conversion Requested": {
"main": [
[
{
"node": "Execute Voice Note Workflow",
"type": "main",
"index": 0
}
]
]
},
"Transcribe Audio with Gemini": {
"main": [
[
{
"node": "Send Transcription to WhatsApp",
"type": "main",
"index": 0
}
]
]
},
"When Called by Main Workflow": {
"main": [
[
{
"node": "Generate Speech with FishAudio",
"type": "main",
"index": 0
}
]
]
},
"Generate Speech with FishAudio": {
"main": [
[
{
"node": "Upload media",
"type": "main",
"index": 0
}
]
]
},
"When WhatsApp Message Received": {
"main": [
[
{
"node": "Set Message Fields",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
fishaudioApigooglePalmApiwhatsAppApiwhatsAppTriggerApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow listens for incoming WhatsApp messages and either transcribes video audio to text with Google Gemini (after converting it to MP3 via FreeConvert) or generates and sends a voice note from a /tts text command using Fish Audio. Triggers when a new WhatsApp message is…
Source: https://n8n.io/workflows/17723/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Powertech Whatsapp. Uses whatsAppTrigger, whatsApp, httpRequest, googleGemini. Event-driven trigger; 36 nodes.
This workflow replies to incoming WhatsApp Business messages (text, voice notes, images, and videos) by transcribing or describing media with OpenAI and Google Gemini, then generating a conversational
Main. Uses whatsApp, agent, httpRequest, memoryBufferWindow. Event-driven trigger; 25 nodes.
Overview 📌
Sora 2 + Nano Banana Shot Generator | 单镜头生成器(子工作流,异步轮询 + 失败重试). Uses googleDrive, executeWorkflowTrigger, httpRequest, agent. Event-driven trigger; 74 nodes.