This workflow follows the HTTP Request → Readwritefile 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 →
{
"name": "Text to speech Voice clone using Zonos via API(local storage)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "voice-clone",
"options": {}
},
"id": "76f2c278-c769-443f-8aa5-541713323b51",
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
-560,
60
]
},
{
"parameters": {
"keepOnlySet": true,
"values": {
"string": [
{
"name": "text",
"value": "={{ $json.body.text || 'Hello, this is a test of voice cloning!' }}"
},
{
"name": "speaking_rate",
"value": "={{ $json.body.speaking_rate || 15 }}"
},
{
"name": "language_iso_code",
"value": "={{ $json.body.language_iso_code || 'en-us' }}"
},
{
"name": "mime_type",
"value": "={{ $json.body.mime_type || 'audio/wav'}}"
},
{
"name": "sample_voice_path",
"value": "={{ $json.body.sample_voice_path }}"
},
{
"name": "model",
"value": "={{ $json.body.model || 'zonos-v0.1-transformer' }}"
},
{
"name": "output_path",
"value": "={{ $json.body.output_path }}"
}
],
"number": [
{
"name": "happiness",
"value": "={{ $json.body.emotion.happiness || 0.6}}"
},
{
"name": "neutral",
"value": "={{ $json.body.emotion.neutral || 0.6}}"
},
{
"name": "sadness",
"value": "={{ $json.body.emotion.sadness || 0.05}}"
},
{
"name": "disgust",
"value": "={{ $json.body.emotion.disgust || 0.05}}"
},
{
"name": "fear",
"value": "={{ $json.body.emotion.fear || 0.05}}"
},
{
"name": "surprise",
"value": "={{ $json.body.emotion.surprise || 0.05}}"
},
{
"name": "anger",
"value": "={{ $json.body.emotion.anger || 0.05}}"
},
{
"name": "other",
"value": "={{ $json.body.emotion.other || 0.5}}"
}
]
},
"options": {}
},
"id": "0772d98e-7cd8-487d-a3db-26c3d6d50304",
"name": "Set Clone Parameters",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [
-340,
60
]
},
{
"parameters": {
"fileSelector": "={{ $json.sample_voice_path }}",
"options": {
"mimeType": "={{ $json.mime_type }}",
"dataPropertyName": ""
}
},
"id": "b3dc1528-90d7-4397-83e6-18c5f2dbf495",
"name": "Read Sample Voice",
"type": "n8n-nodes-base.readWriteFile",
"typeVersion": 1,
"position": [
40,
60
],
"onError": "continueErrorOutput"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "file-exists-check",
"leftValue": "={{ $ifEmpty($binary.data, false) !== false }}\n",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "86e0f9bf-ccec-4bb1-b9ce-7d1b94fdfcca",
"name": "Check File Loaded",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
560,
-80
]
},
{
"parameters": {
"method": "POST",
"url": "http://api.zyphra.com/v1/audio/text-to-speech",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-API-Key",
"value": "put-your-API-key-here"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"text\": \"{{ $('Set Clone Parameters').item.json.text }}\",\n \"speaking_rate\": {{ parseFloat($('Set Clone Parameters').item.json.speaking_rate) }},\n \"language_iso_code\": \"{{ $('Set Clone Parameters').item.json.language_iso_code }}\",\n \"mime_type\": \"{{ $('Set Clone Parameters').item.json.mime_type }}\",\n \"model\": \"{{ $('Set Clone Parameters').item.json.model }}\",\n \"emotion\": {\n \"happiness\": {{ $('Set Clone Parameters').item.json.happiness }},\n \"neutral\": {{ $('Set Clone Parameters').item.json.neutral }},\n \"sadness\": {{ $('Set Clone Parameters').item.json.sadness }},\n \"disgust\": {{ $('Set Clone Parameters').item.json.disgust }},\n \"fear\": {{ $('Set Clone Parameters').item.json.fear }},\n \"surprise\": {{ $('Set Clone Parameters').item.json.surprise }},\n \"anger\": {{ $('Set Clone Parameters').item.json.anger }},\n \"other\": {{ $('Set Clone Parameters').item.json.other }}\n },\n \"speaker_audio\": \"{{ $json.base64_content }}\"\n}\n",
"options": {
"response": {
"response": {}
}
}
},
"id": "2980ae94-30e3-4065-8d2c-b607c73c3c5f",
"name": "Call Zyphra Clone API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
980,
-120
],
"onError": "continueErrorOutput"
},
{
"parameters": {
"operation": "write",
"fileName": "={{ $('Set Clone Parameters').item.json.output_path }}{{ 'cloned_voice_' + new Date().getTime() + '.webm' }}",
"options": {}
},
"id": "0f896518-98ce-4176-8977-1f0380b54469",
"name": "Save Cloned Audio",
"type": "n8n-nodes-base.readWriteFile",
"typeVersion": 1,
"position": [
1440,
0
]
},
{
"parameters": {
"values": {
"string": [
{
"name": "success",
"value": "true"
},
{
"name": "message",
"value": "Voice cloning completed successfully!"
},
{
"name": "filename",
"value": "={{ $json.fileName }}"
},
{
"name": "text_processed",
"value": "={{ $('Set Clone Parameters').item.json.text }}"
},
{
"name": "sample_voice_used",
"value": "={{ $('Set Clone Parameters').item.json.sample_voice_path }}"
}
]
},
"options": {}
},
"id": "02f1b766-953a-4834-8d93-4cc596ffe77c",
"name": "Success Response",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [
1660,
0
]
},
{
"parameters": {
"values": {
"string": [
{
"name": "success",
"value": "false"
},
{
"name": "error_type",
"value": "file_not_found"
},
{
"name": "message",
"value": "Sample voice file could not be read from: {{ $('Set Clone Parameters').item.json.sample_voice_path }}"
},
{
"name": "suggestion",
"value": "Please check the file path and ensure the file exists"
}
]
},
"options": {}
},
"id": "7da98d65-08e3-4d6e-9957-ead2c8f1133f",
"name": "File Error Response",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [
860,
480
]
},
{
"parameters": {
"values": {
"string": [
{
"name": "success",
"value": "false"
},
{
"name": "error_type",
"value": "api_error"
},
{
"name": "message",
"value": "Voice cloning API request failed"
},
{
"name": "error_details",
"value": "={{ $json.error ? $json.error.message : 'Unknown API error' }}"
},
{
"name": "suggestion",
"value": "Please check your API key and try again"
}
]
},
"options": {}
},
"id": "44006f98-4241-436d-b8cf-1c586271a35a",
"name": "API Error Response",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [
1100,
480
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
},
"id": "e2c14808-e209-4daf-93dd-9c7049261e60",
"name": "Webhook Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1880,
100
]
},
{
"parameters": {
"content": "\ud83c\udf99\ufe0f VOICE CLONING WORKFLOW - ZONOS API\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nThis workflow clones voices using Zyphra's Zonos API\n\ud83d\udccb Input: Text + sample voice file + parameters \n\ud83c\udfb5 Output: Generated audio file with cloned voice\n\u26a1 Trigger: Webhook POST to /voice-clone endpoint\n\nBasic setup and use:\nConfigure your Zyphra API key in the \"Call Zyphra Clone API\" node under Header parameters \u2192 X-API-Key header parameter\n\nEnsure your sample voice files are accessible at the paths you'll specify\n\nTest the webhook endpoint is accessible\n\nSend valid POST request to your webhook\n",
"height": 340,
"width": 780,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-960,
-1200
],
"id": "8268fd4a-fd84-4462-9ad1-9af8152d0cf3",
"name": "Sticky Note"
},
{
"parameters": {
"jsCode": "// Get binary data from previous node\nconst binaryData = $input.first().binary?.data;\n\nif (!binaryData) {\n throw new Error(\"No binary data found\");\n}\n\n// Binary data in n8n is already base64 encoded\nconst base64String = binaryData.data;\n\nreturn {\n json: {\n base64_content: base64String,\n // Add other fields as needed\n }\n};\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
-80
],
"id": "cdbb775f-8408-43a9-89b1-1039315c6678",
"name": "Base64 convertor"
},
{
"parameters": {
"content": "\ud83d\udce5 REQUIRED WEBHOOK PARAMETERS\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\u2705 text: Text to synthesize\n\u2705 sample_voice_path: Path to your voice sample file \n\u2705 output_path: Where to save generated audio\n\n\ud83d\udd27 OPTIONAL PARAMETERS (with defaults):\n\u2022 speaking_rate: 15 (speech speed)\n\u2022 language_iso_code: \"en-us\" \n\u2022 mime_type: \"audio/wav\"\n\u2022 model: \"zonos-v0.1-transformer\"\n\u2022 emotion: Various emotion levels (0-1 scale)\n",
"height": 1020,
"width": 780,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-960,
-740
],
"id": "88b0131c-f93b-4593-9c88-ed4f91b74c88",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "\ud83d\udd10 API KEY CONFIGURATION REQUIRED!\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n1\ufe0f\u20e3 Register account at: playground.zyphra.com \n2\ufe0f\u20e3 Get API key from: playground.zyphra.com/settings/api-keys\n3\ufe0f\u20e3 Add key to \"Call Zyphra Clone API\" node\n \u2192 Header parameters section\n \u2192 Header: X-API-Key\n \u2192 Value: [Your API Key]\n\n\u274c Workflow will fail without proper API key setup!\n",
"height": 460,
"width": 480,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
820,
-380
],
"id": "3be24130-054d-4435-9aac-d5ad9c7b92d0",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "\ud83d\udcc1 FILE HANDLING PROCESS\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n1. Read sample voice from specified path\n2. Convert audio file to base64 format\n3. Validate file loaded successfully \n\n\u26a0\ufe0f IMPORTANT: Ensure sample voice file exists \n at the path specified in sample_voice_path!\n",
"height": 560,
"width": 740,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-60,
-280
],
"id": "08780e5a-4663-4a0d-aeff-69f4059495ce",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "\ud83d\udea8 ERROR HANDLING & RESPONSES\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\u2705 SUCCESS: Returns success message + filename\n\u274c FILE_NOT_FOUND: Sample voice file missing/unreadable\n\u274c API_ERROR: Zyphra API request failed\n\u274c AUTH_ERROR: Invalid or missing API key\n\nAll errors return JSON with error details and suggestions\n",
"height": 480,
"width": 480,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
820,
180
],
"id": "223743b2-aeb2-45cc-8094-9bef48fc815e",
"name": "Sticky Note4"
},
{
"parameters": {
"content": "\ud83c\udfb5 OUTPUT DETAILS\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\ud83d\udccd Location: [output_path]/cloned_voice_[timestamp].webm\n\ud83d\udcca Format: Specified by mime_type parameter\n\ud83d\udd50 Filename: Auto-generated with timestamp\n\ud83d\udcdd Response: JSON with success status and file details\n\nGenerated audio ready for download!\n",
"height": 540,
"width": 660,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1400,
-220
],
"id": "939714c2-9feb-4ccb-b3b6-fd133c65c56a",
"name": "Sticky Note5"
},
{
"parameters": {
"content": "POST http://localhost:5678/webhook-test/voice-clone\nContent-Type: application/json\n\n{\n \"text\": \"Hello there, kupo! This voice sounds just like the sample!\",\n \"speaking_rate\": 18,\n \"sample_voice_path\": \"/data/output/sampleVoice.wav\",\n \"output_path\": \"/data/output/\",\n \"language_iso_code\": \"en-us\",\n \"mime_type\": \"audio/wav\",\n \"model\": \"zonos-v0.1-transformer\",\n \"emotion\": {\n \"happiness\": 0.8,\n \"neutral\": 0.3,\n \"sadness\": 0.05,\n \"disgust\": 0.05,\n \"fear\": 0.05,\n \"surprise\": 0.05,\n \"anger\": 0.05,\n \"other\": 0.5\n }\n}\n",
"height": 440,
"width": 700,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-940,
-440
],
"id": "0a90485e-417a-415e-a9a4-f128aea6b9f1",
"name": "Sticky Note6"
}
],
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "Set Clone Parameters",
"type": "main",
"index": 0
}
]
]
},
"Set Clone Parameters": {
"main": [
[
{
"node": "Read Sample Voice",
"type": "main",
"index": 0
}
]
]
},
"Read Sample Voice": {
"main": [
[
{
"node": "Base64 convertor",
"type": "main",
"index": 0
}
],
[
{
"node": "File Error Response",
"type": "main",
"index": 0
}
]
]
},
"Check File Loaded": {
"main": [
[
{
"node": "Call Zyphra Clone API",
"type": "main",
"index": 0
}
],
[
{
"node": "File Error Response",
"type": "main",
"index": 0
}
]
]
},
"Call Zyphra Clone API": {
"main": [
[
{
"node": "Save Cloned Audio",
"type": "main",
"index": 0
}
],
[
{
"node": "API Error Response",
"type": "main",
"index": 0
}
]
]
},
"Save Cloned Audio": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
]
]
},
"Success Response": {
"main": [
[
{
"node": "Webhook Response",
"type": "main",
"index": 0
}
]
]
},
"File Error Response": {
"main": [
[
{
"node": "Webhook Response",
"type": "main",
"index": 0
}
]
]
},
"API Error Response": {
"main": [
[
{
"node": "Webhook Response",
"type": "main",
"index": 0
}
]
]
},
"Base64 convertor": {
"main": [
[
{
"node": "Check File Loaded",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "c4a644b3-3029-47ed-9dad-6396d22a9841",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "hqMRsze38RaTmgQh",
"tags": []
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
How this works
Create lifelike audio from your text using a custom voice clone, saving time on voiceovers or personalised messages without needing expensive recording sessions. This workflow suits content creators, educators, or developers who want quick, high-quality speech synthesis tailored to a specific voice sample. It starts by receiving your text via a webhook, then clones the voice using Zonos's API and stores the resulting audio file locally for easy access, integrating seamlessly with file handling and HTTP requests.
Use this when you need on-demand voice cloning for apps or automated content generation, especially with local storage to keep sensitive audio files secure. Avoid it for real-time applications due to API processing delays, or if you require cloud-based storage instead of local files. Common variations include adding email notifications for completion or integrating with a database for managing multiple voice clones.
About this workflow
Text to speech Voice clone using Zonos via API(local storage). Uses readWriteFile, httpRequest, respondToWebhook, stickyNote. Webhook trigger; 18 nodes.
Source: https://gitlab.com/Tiartyos/n8n-workflows/-/blob/master/workflows/zyphry-zonos-api/workflow.json — 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.
Portfolio Orchestrator. Uses httpRequest. Webhook trigger; 59 nodes.
jump-section: Comment Fix Pipeline. Uses httpRequest. Webhook trigger; 24 nodes.
GitHub Issues Router (Linear / Jira / ClickUp). Uses stickyNote, httpRequest, respondToWebhook. Webhook trigger; 23 nodes.
Form to CRM Lead Router (Pipedrive / HubSpot / Salesforce). Uses stickyNote, httpRequest, respondToWebhook. Webhook trigger; 22 nodes.
Calendly to CRM Sync (Pipedrive / HubSpot / Salesforce). Uses stickyNote, httpRequest, respondToWebhook. Webhook trigger; 22 nodes.