AutomationFlowsGeneral › Serve Text-to-speech Mp3 Over a Webhook with Smallest.ai

Serve Text-to-speech Mp3 Over a Webhook with Smallest.ai

ByZain Khan @zain on n8n.io

This workflow exposes a POST webhook that converts incoming text into an MP3 audio stream using Smallest.ai, optionally using a provided custom voice ID, and returns the audio inline in the same HTTP response. Receives a POST request on the webhook endpoint with a JSON body.…

Webhook trigger★★★★☆ complexity13 nodesN8N Nodes Smallestai
General Trigger: Webhook Nodes: 13 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #16819 — we link there as the canonical source.

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 →

Download .json
{
  "id": "Gr2nNlt21Q3aROhU",
  "name": "Instant Text-to-Speech Service",
  "tags": [],
  "nodes": [
    {
      "id": "d7aec264-1092-4333-b407-59ddda83bf0d",
      "name": "Listen to text",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1696,
        48
      ],
      "parameters": {
        "path": "text-to-speech",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "8eb606a1-29ca-423b-b1ba-cff3f4437f02",
      "name": "Check if request has voice id",
      "type": "n8n-nodes-base.if",
      "position": [
        -960,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ffadcb60-a752-4457-9885-84309e009d80",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.voice }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "d32cf118-9ac7-4691-890e-3c3badd168db",
      "name": "Generate audio with custom voice id",
      "type": "n8n-nodes-smallestai.smallestai",
      "onError": "continueErrorOutput",
      "position": [
        -720,
        -48
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "voiceId": "={{ $json.voice }}",
        "output_format": "={{ $json.format }}",
        "additionalOptions": {}
      },
      "credentials": {},
      "typeVersion": 1
    },
    {
      "id": "94e06d74-5456-495b-9042-27488c1cd9d9",
      "name": "Check if request has text",
      "type": "n8n-nodes-base.if",
      "position": [
        -1488,
        48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "23f0886c-d46c-4e24-b87c-5205d459ff12",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.body.text }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "dfde46d7-897d-45e6-8aff-1299e956dfa5",
      "name": "Return Error if text is empty",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -1200,
        224
      ],
      "parameters": {
        "options": {
          "responseCode": 400
        },
        "respondWith": "json",
        "responseBody": "{ \"error\": \"Missing required field: text\" }"
      },
      "typeVersion": 1.5
    },
    {
      "id": "8c74ea0a-2768-4568-aad3-7f81764511f8",
      "name": "Apply Defaults from Input",
      "type": "n8n-nodes-base.set",
      "position": [
        -1200,
        32
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b5c41923-34c2-439c-96ef-69ac4b5fae99",
              "name": "text",
              "type": "string",
              "value": "={{ $json.body.text.trim() }}"
            },
            {
              "id": "d5c4b293-3dab-47ee-a83b-1380fda29266",
              "name": "voice",
              "type": "string",
              "value": "={{ $json.body.voice || '' }}"
            },
            {
              "id": "53538ec4-91cb-43b3-ad7d-e578ce471aaf",
              "name": "=format",
              "type": "string",
              "value": "={{ $json.body.format || 'mp3' }}"
            },
            {
              "id": "94fe0465-b175-4390-a528-f396695dc17e",
              "name": "=speed ",
              "type": "number",
              "value": "={{ $json.body.speed || 1 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b26bbab4-2c16-4e7c-a250-ffacd899f94b",
      "name": "Generate audio with default voice id",
      "type": "n8n-nodes-smallestai.smallestai",
      "onError": "continueErrorOutput",
      "position": [
        -720,
        176
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "output_format": "={{ $json.format }}",
        "additionalOptions": {}
      },
      "credentials": {},
      "typeVersion": 1
    },
    {
      "id": "ebca43f9-5a78-483f-a8a7-ae169cc2d372",
      "name": "Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -304,
        -64
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "audio/mpeg"
              },
              {
                "name": "Content-Disposition",
                "value": "inline; filename=\"speech.mp3\""
              }
            ]
          }
        },
        "respondWith": "binary"
      },
      "typeVersion": 1.5
    },
    {
      "id": "d7cbfd63-8d80-411e-be41-8ce167c89ba7",
      "name": "Return Error if TTS is failed",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -288,
        192
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={ \"error\":  \"{{ $json.error }}\" }"
      },
      "typeVersion": 1.5
    },
    {
      "id": "aa0211c1-cc6e-49f0-808b-b0c343b59066",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2272,
        -176
      ],
      "parameters": {
        "width": 512,
        "height": 672,
        "content": "## Text-to-Speech API (Smallest.ai)\n\n### How it works\nThis workflow turns Telegram into an intelligent voice memo companion. When an inbound message hits the Telegram bot trigger, it validates if the payload contains an audio object. It then fetches the binary audio file from Telegram's servers and routes it directly to Smallest.ai for accurate speech-to-text processing. \n\n### Setup steps\n* Add your **Telegram Bot API Token** inside the Trigger, Audio Downloader, and Reply Tool nodes.\n* Connect your **Smallest.ai** API key credentials to the transcription node.\n* Link your **Google Gemini** API key account to fuel the underlying LangChain Chat Model.\n\n### Customization\nYou can completely rewrite the target system prompt parameters inside the **AI Agent** node if you want the bot to perform alternate tasks with your voice memos\u2014such as acting as a language translator, sorting actionable items into a task list, or saving formatting summaries out to an external notes database."
      },
      "typeVersion": 1
    },
    {
      "id": "af5ad6f0-cfda-449e-9978-9a1df5faaf86",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1712,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 336,
        "content": "## 1. Capture & Filter Inbound Audio\n"
      },
      "typeVersion": 1
    },
    {
      "id": "6c47f6d3-b326-403a-94b6-8f2a16379ecc",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1248,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 496,
        "content": "## 2. Download & Transcribe"
      },
      "typeVersion": 1
    },
    {
      "id": "8b5d3bd1-a93b-433d-a108-4194e101120d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -768,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 496,
        "content": "## 3. Intelligent Response Loop"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "8b619454-e6e7-45d4-804a-1edd1a294c40",
  "nodeGroups": [],
  "connections": {
    "Listen to text": {
      "main": [
        [
          {
            "node": "Check if request has text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apply Defaults from Input": {
      "main": [
        [
          {
            "node": "Check if request has voice id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if request has text": {
      "main": [
        [
          {
            "node": "Apply Defaults from Input",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Return Error if text is empty",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if request has voice id": {
      "main": [
        [
          {
            "node": "Generate audio with custom voice id",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate audio with default voice id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate audio with custom voice id": {
      "main": [
        [
          {
            "node": "Success",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Return Error if TTS is failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate audio with default voice id": {
      "main": [
        [
          {
            "node": "Success",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Return Error if TTS is failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow exposes a POST webhook that converts incoming text into an MP3 audio stream using Smallest.ai, optionally using a provided custom voice ID, and returns the audio inline in the same HTTP response. Receives a POST request on the webhook endpoint with a JSON body.…

Source: https://n8n.io/workflows/16819/ — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

A clean, extensible REST-style API routing template for n8n webhooks with up to 3 path levels. Serves API routes via Webhooks with path variables Normalizes incoming requests into "global" REQUEST and

General

PUQ Docker NextCloud deploy. Uses respondToWebhook, stickyNote, httpRequest, ssh. Webhook trigger; 44 nodes.

HTTP Request, Ssh
General

puq-docker-immich-deploy. Uses respondToWebhook, ssh, stickyNote. Webhook trigger; 35 nodes.

Ssh
General

Analyze_email_headers_for_IPs_and_spoofing__3. Uses stickyNote, respondToWebhook, itemLists, httpRequest. Webhook trigger; 35 nodes.

Item Lists, HTTP Request
General

puq-docker-n8n-deploy. Uses respondToWebhook, ssh, stickyNote. Webhook trigger; 34 nodes.

Ssh