Voice cloning
Create a short English speech sample from a voice you are permitted to use.
Base URL: https://happytails.ai · public API not connected
Overview
Create a short English speech sample from a voice you are permitted to use. Upload 3 to 15 seconds of clear speech and enter up to 160 characters. Local Chatterbox processing, built-in watermark, and no saved voice profile. Disclose synthetic audio. Review similarity and pronunciation; results may vary. Allow up to three minutes for this experimental local voice model.
All API requests process supplied data on the server. File handling and privacy.
Request parameters
Send a JSON object. Unknown properties are rejected. Omitted options use the defaults below; enter numbers and booleans as JSON values, not quoted text.
input is a string, empty by default, up to 1,000,000 characters. Up to 1,000,000 characters. Review the settings before running.
Files
Accepted formats: .wav,.mp3,.m4a,.flac,.mp4,.mov,.webm. Up to 1 file(s), totaling 20 MiB decoded.
Each files entry requires name and padded base64. Optional mime describes the content; optional bytes must equal its decoded length. Filenames cannot contain paths. File URLs are not accepted. See uploading and saving files.
Options
options.consentbooleanThis is my own voice, or I have permission to synthesize it
Success response
HTTP 200 returns tool and result. Text is in result.text; parsed JSON may also appear in result.data. Generated files are in result.files with name, mime, bytes and base64. Decode the bytes before saving. Empty file arrays and null text are valid for operations returning another result type.
Complete output schema
{
"type": "object",
"required": [
"tool",
"result"
],
"properties": {
"tool": {
"type": "string"
},
"result": {
"type": "object",
"required": [
"text",
"files"
],
"properties": {
"text": {
"type": [
"string",
"null"
]
},
"data": {
"description": "Parsed JSON when the textual result is JSON."
},
"name": {
"type": "string"
},
"mime": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"mime",
"bytes",
"base64"
],
"properties": {
"name": {
"type": "string"
},
"mime": {
"type": "string"
},
"bytes": {
"type": "integer"
},
"base64": {
"type": "string",
"contentEncoding": "base64"
}
}
}
}
}
}
}
}Limits and failures
Limits below are enforced by this endpoint. Additional format, page, duration and model limits in the behavior notes also apply. Browser UI limits can differ from API limits.
request_bytes- 31457280
input_characters- 1000000
files_bytes- 20971520
max_files- 1
timeout_seconds- 180
Invalid requests return an error object with code and message. Read HTTP statuses, retries, timeouts and concurrency. Execution is synchronous and results are returned in the response.
Complete request schema
Expand the JSON schema
{
"type": "object",
"additionalProperties": false,
"properties": {
"input": {
"type": "string",
"maxLength": 1000000,
"default": "",
"description": "Plain text input. File tools use files instead unless otherwise documented."
},
"options": {
"type": "object",
"additionalProperties": false,
"properties": {
"consent": {
"type": "boolean",
"description": "This is my own voice, or I have permission to synthesize it",
"default": false
}
}
},
"files": {
"type": "array",
"maxItems": 1,
"items": {
"type": "object",
"required": [
"name",
"base64"
],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"maxLength": 200,
"description": "Filename only, no path."
},
"mime": {
"type": "string",
"maxLength": 150
},
"bytes": {
"type": "integer",
"minimum": 0,
"description": "Optional decoded byte count; must match content if supplied."
},
"base64": {
"type": "string",
"contentEncoding": "base64",
"description": "File bytes as padded base64. Use the tool-specific limits.files_bytes value for the total decoded input size."
}
}
}
}
}
}For browser automation, use the central WebMCP guidance and discover the tools actually exposed by the browser.
import json
from urllib.request import Request, urlopen
import base64
from pathlib import Path
source = Path("input.wav")
payload = json.loads('{"options": {"consent": false}, "input": "Your input"}')
payload["files"] = [{
"name": source.name,
"base64": base64.b64encode(source.read_bytes()).decode("ascii")
}]
request = Request(
"https://happytails.ai/api/v1/tools/voice-cloning/run",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
)
with urlopen(request, timeout=200) as response:
result = json.load(response)
print(result)import { readFile } from "node:fs/promises";
const filename = "input.wav";
const payload = {
"options": {
"consent": false
},
"input": "Your input"
};
payload.files = [{
name: filename,
base64: (await readFile(filename)).toString("base64")
}];
const response = await fetch(
"https://happytails.ai/api/v1/tools/voice-cloning/run",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
}
);
const data = await response.json();
if (!response.ok) throw new Error(data.error.message);
console.log(data.result);# Save the JSON body as request.json first.
curl https://happytails.ai/api/v1/tools/voice-cloning/run \
-H "Content-Type: application/json" \
--data-binary @request.json{
"options": {
"consent": false
},
"input": "Your input",
"files": [
{
"name": "input.wav",
"base64": "BASE64_FILE_BYTES"
}
]
}Python and JavaScript read your local file and encode it as Base64. Replace the input filename and API origin.
{
"tool": "voice-cloning",
"result": {
"text": null,
"files": [
{
"name": "result",
"mime": "application/octet-stream",
"bytes": 0,
"base64": "BASE64_OUTPUT_BYTES"
}
]
}
}Illustrative envelope. Actual text, filename, MIME type and bytes depend on the result. See the complete output schema.
{
"error": {
"code": "INVALID_INPUT",
"message": "Unknown request property. See the tool input schema."
}
}