Sogni Voice API Documentation

REST API for audio transcription and text-to-speech synthesis

Base URL: https://voice.sogni.ai

Authentication (Optional)

API key authentication can be enabled for production deployments. When enabled, all API endpoints (including Kokoro, Pocket, Qwen3, MOSS-TTS-Nano, and Transcription) require authentication, except health check and auth status. Voice clone imports and reference-voice operations report their access modes separately.

Check Auth Status

GET /auth/status

Check if authentication is enabled on this server and whether voice clone imports are public, API-key-only, or blocked by configuration.

Response

{
  "authEnabled": true,
  "apiKeyConfigured": true,
  "dangerouslyAllowImports": false,
  "voiceCloneImports": {
    "enabled": true,
    "mode": "api_key"
  },
  "voiceCloning": {
    "enabled": true,
    "mode": "api_key"
  }
}

voiceCloneImports.mode and voiceCloning.mode are each one of public, api_key, or blocked.

Authenticating Requests

When authentication is enabled, include your API key using one of these methods:

Option 1: X-API-Key Header (recommended)

curl -X POST https://voice.sogni.ai/tts \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello world"}'

Option 2: Authorization Bearer Header

curl -X POST https://voice.sogni.ai/tts \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello world"}'

JavaScript Example

const API_KEY = 'your_api_key_here';

const response = await fetch('https://voice.sogni.ai/tts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': API_KEY
  },
  body: JSON.stringify({
    text: 'Hello, world!'
  })
});

Python Example

import requests

API_KEY = 'your_api_key_here'

response = requests.post(
    'https://voice.sogni.ai/tts',
    headers={'X-API-Key': API_KEY},
    json={'text': 'Hello, world!'}
)

with open('output.wav', 'wb') as f:
    f.write(response.content)

Public Endpoints

These endpoints are always accessible without authentication:

Voice clone import routes are not implicitly public when authEnabled is false; check voiceCloneImports.mode first.

Error Response

When authentication fails, the API returns a 401 Unauthorized response:

{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Missing API key. Provide X-API-Key header or Authorization: Bearer <key>"
}

1. STT - Parakeet, Qwen3-ASR, MOSS Transcribe-Diarize

Upload an audio file and receive a text transcript, or stream a microphone to Parakeet for interim and final events. Parakeet is the fast default; Qwen3-ASR adds multilingual recognition and alignment; experimental MOSS Transcribe-Diarize jointly emits English/Chinese text, speakers, and segment timestamps.

GET /transcription/models

Lists configured recognition providers, enabled state, language/timestamp capabilities, and whether diarization is built in.

Live Parakeet transcription

WS /v1/realtime/transcription

Streams mono 16 kHz little-endian float32 PCM to Parakeet's native transcribe_stream path. Use wss:// when the page is served over HTTPS. The playground's Start Live Transcription control is a complete browser client.

  1. Wait for the server's connected event.
  2. Send {"type":"start","encoding":"pcm_f32le","sampleRate":16000}. When global auth is enabled, browser clients also include apiKey in this message; non-browser clients may authenticate in the upgrade headers.
  3. After session.started, send binary PCM frames. The recommended cadence is 8,000 samples / 32,000 bytes (0.5 seconds).
  4. Read transcript.partial updates. Send {"type":"stop"} for transcript.final, or {"type":"abort"} to discard the stream.
{
  "type": "transcript.partial",
  "sessionId": "...",
  "sequence": 3,
  "text": "Realtime transcription is working",
  "finalizedText": "Realtime",
  "draftText": "transcription is working",
  "audioSeconds": 1.5,
  "processingSeconds": 0.09,
  "realTimeFactor": 0.21
}
{
  "type": "transcript.final",
  "sessionId": "...",
  "text": "Realtime transcription is working.",
  "timestamps": [
    { "text": "Realtime", "start": 0.0, "end": 0.4 }
  ],
  "audioSeconds": 1.7,
  "realTimeFactor": 0.18
}

Only one Parakeet live stream can own the resident model at a time; a second client receives retryable error code busy and WebSocket close code 1013. Parakeet batch requests pause for the same interval, while Qwen3-ASR and MOSS remain available. Cross-origin upgrades follow CORS_ORIGINS, and the default live limit is five minutes.

POST /transcribe

Request

Send audio as multipart/form-data.

Parameter Type Description
file * File Audio file to transcribe (supports common formats: mp3, wav, webm, m4a, etc.)
engine (optional) string parakeet (default), qwen3, or experimental moss-td
language (optional) string Qwen3-ASR language name/code or auto (default). Not accepted by moss-td, which handles English/Chinese automatically.
timestamps (optional) string Set to "true" to include sentence-level timings with start/end times for each segment
wordTimestamps (optional) string Set to "true" to include word-level timings (overrides timestamps). Not supported by moss-td.
diarize (optional) string Set to "true" to identify speakers with pyannote Community-1. MOSS always performs built-in diarization instead.
numSpeakers (optional) string Exact speaker count from 1-20. Overrides the minimum and maximum.
minSpeakers / maxSpeakers (optional) string Expected speaker-count range from 1-20.
hotwords / prompt (MOSS only) string Optional vocabulary hints or a custom transcription instruction for moss-td.
maxNewTokens (MOSS only) string Generation limit from 64-65536; raise it for long recordings.

Response (default)

{
  "success": true,
  "transcript": "The transcribed text appears here.",
  "filename": "recording.mp3"
}

Response (with sentence timestamps)

When timestamps=true, the response includes sentence-level timing data for subtitle generation:

{
  "success": true,
  "timestamps": [
    { "start": 0.00, "end": 2.34, "text": "Hello and welcome" },
    { "start": 2.34, "end": 5.67, "text": "to our presentation today" },
    { "start": 5.67, "end": 8.90, "text": "we will cover several topics" }
  ]
}

Response (with word timestamps)

When wordTimestamps=true, the response includes word-level timing data for precise subtitle synchronization:

{
  "success": true,
  "timestamps": [
    { "start": 0.00, "end": 0.48, "text": "Hello" },
    { "start": 0.48, "end": 0.72, "text": "and" },
    { "start": 0.72, "end": 1.20, "text": "welcome" },
    { "start": 1.20, "end": 1.44, "text": "to" },
    { "start": 1.44, "end": 1.68, "text": "our" },
    { "start": 1.68, "end": 2.34, "text": "presentation" }
  ]
}

Each timestamp object contains:

Response (with speaker identification)

{
  "success": true,
  "timestamps": [
    { "start": 0.00, "end": 1.40, "text": "Welcome.", "speaker": "SPEAKER_00" },
    { "start": 1.50, "end": 2.80, "text": "Thank you.", "speaker": "SPEAKER_01" }
  ],
  "diarization": { "available": true, "numSpeakers": 2 },
  "speakers": [
    { "speaker": "SPEAKER_00", "segmentCount": 1, "totalSeconds": 1.4 },
    { "speaker": "SPEAKER_01", "segmentCount": 1, "totalSeconds": 1.3 }
  ]
}

Qwen3-ASR with word alignment

When Qwen3-ASR receives timestamps=true or wordTimestamps=true, the Qwen3 ForcedAligner generates the requested spans. Alignment supports audio up to 5 minutes in 11 languages.

{
  "success": true,
  "engine": "qwen3",
  "language": "English",
  "model": "mlx-community/Qwen3-ASR-0.6B-8bit",
  "timestampLevel": "word",
  "timestamps": [
    { "text": "Hello", "start": 0.0, "end": 0.4 },
    { "text": "world", "start": 0.4, "end": 0.8 }
  ]
}

MOSS Transcribe-Diarize (experimental)

This opt-in 0.9B engine performs English/Chinese transcription, diarization, and segment timing in one model pass. It is pinned to reviewed package/model commits, runs in an isolated Python 3.12 environment, and uses local model classes with remote code disabled. Word timestamps, language selection, and speaker-count constraints are not supported.

{
  "success": true,
  "engine": "moss-td",
  "experimental": true,
  "transcript": "Good morning. Thanks for joining.",
  "segments": [
    { "start": 0.0, "end": 1.2, "speaker": "S01", "text": "Good morning." },
    { "start": 1.25, "end": 2.8, "speaker": "S02", "text": "Thanks for joining." }
  ],
  "diarization": { "available": true, "builtIn": true, "numSpeakers": 2 },
  "timestampLevel": "segment"
}

cURL Examples

# Basic transcription
curl -X POST https://voice.sogni.ai/transcribe \
  -F "file=@/path/to/audio.mp3"

# With sentence-level timings
curl -X POST https://voice.sogni.ai/transcribe \
  -F "file=@/path/to/audio.mp3" \
  -F "timestamps=true"

# With word-level timings
curl -X POST https://voice.sogni.ai/transcribe \
  -F "file=@/path/to/audio.mp3" \
  -F "wordTimestamps=true"

# Experimental one-pass transcript + speakers
curl -X POST https://voice.sogni.ai/transcribe \
  -F "file=@/path/to/meeting.wav" \
  -F "engine=moss-td" \
  -F "hotwords=Sogni, MLX"

# With local speaker identification
curl -X POST https://voice.sogni.ai/transcribe \
  -F "file=@/path/to/conversation.mp3" \
  -F "timestamps=true" \
  -F "diarize=true" \
  -F "minSpeakers=2" \
  -F "maxSpeakers=4"

# Qwen3-ASR with auto language detection and word alignment
curl -X POST https://voice.sogni.ai/transcribe \
  -F "file=@/path/to/speech.m4a" \
  -F "engine=qwen3" \
  -F "language=auto" \
  -F "wordTimestamps=true" \
  -F "diarize=false"

Forced Alignment of Known Text

POST /qwen-asr/align

Align an exact transcript to an audio file. Send file, text, and one of the 11 supported language names as multipart fields.

curl -X POST https://voice.sogni.ai/qwen-asr/align \
  -F "file=@/path/to/speech.wav" \
  -F "text=The exact words spoken in the recording." \
  -F "language=English"
{
  "success": true,
  "language": "English",
  "model": "mlx-community/Qwen3-ForcedAligner-0.6B-8bit",
  "timestamps": [
    { "text": "The", "start": 0.0, "end": 0.18 },
    { "text": "exact", "start": 0.18, "end": 0.52 }
  ]
}

JavaScript Example

const formData = new FormData();
formData.append('file', audioFile);

const response = await fetch('https://voice.sogni.ai/transcribe', {
  method: 'POST',
  body: formData
});

const data = await response.json();
console.log(data.transcript);

Python Example

import requests

with open('audio.mp3', 'rb') as f:
    response = requests.post(
        'https://voice.sogni.ai/transcribe',
        files={'file': f}
    )

data = response.json()
print(data['transcript'])

2. TTS (Text-to-Speech) - Kokoro

Convert text to spoken audio using Kokoro TTS. Returns a WAV audio file by default.

POST /tts

Request

Send JSON with the text and optional parameters.

Parameter Type Default Description
text * string - Text to convert to speech (1-10,000 characters)
voice (optional) string af_heart Voice to use (see /tts/voices for available voices)
speed (optional) number 1.0 Speech speed multiplier (0.5 to 2.0)
format (optional) string wav Output format: "wav", "opus" (returns audio file) or "buffer" (returns base64 JSON). Also accepted as ?format= on this endpoint.
timestamps (optional) boolean false Include word-level timestamps for subtitle generation (forces JSON response)

Response (format: wav)

Returns binary WAV audio data with Content-Type: audio/wav

Response (format: buffer)

{
  "success": true,
  "audio": "UklGRiQAAABXQVZFZm10IBAA...",  // base64 encoded WAV
  "voice": "af_heart",
  "speed": 1.0,
  "format": "wav"
}

cURL Example

curl -X POST https://voice.sogni.ai/tts \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, world!", "voice": "af_heart", "speed": 1.0}' \
  --output output.wav

Opus via query-string format override

curl -X POST 'https://voice.sogni.ai/tts?format=opus' \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, world!", "voice": "af_heart", "speed": 1.0}' \
  --output output.opus

JavaScript Example

const response = await fetch('https://voice.sogni.ai/tts', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    text: 'Hello, world!',
    voice: 'af_heart',
    speed: 1.0
  })
});

const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
audio.play();

Python Example

import requests

response = requests.post(
    'https://voice.sogni.ai/tts',
    json={'text': 'Hello, world!', 'voice': 'af_heart', 'speed': 1.0}
)

with open('output.wav', 'wb') as f:
    f.write(response.content)

List Available Voices

GET /tts/voices

Response

{
  "voices": ["af_heart", "af_bella", "am_adam", ...],
  "default": "af_heart"
}

3. TTS (Text-to-Speech) - Pocket

Kyutai Pocket TTS is a lightweight 100M-parameter, CPU-only, English-only TTS with ~200ms latency and voice cloning support. Requires POCKET_TTS_ENABLED=1.

Generate Speech

POST /pocket-tts
Parameter Type Default Description
text * string - Text to convert to speech (1-10,000 characters)
voice (optional) string alba Built-in voice: alba, marius, javert, jean, fantine, cosette, eponine, azelma
format (optional) string wav Output format: "wav", "opus" (returns audio file) or "buffer" (returns base64 JSON). Also accepted as ?format= on this endpoint.

cURL Example

curl -X POST https://voice.sogni.ai/pocket-tts \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, world!", "voice": "alba"}' \
  --output output.wav

Opus via query-string format override

curl -X POST 'https://voice.sogni.ai/pocket-tts?format=opus' \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, world!", "voice": "alba"}' \
  --output output.opus

List Voices

GET /pocket-tts/voices

Response

{
  "voices": ["alba", "marius", "javert", "jean", "fantine", "cosette", "eponine", "azelma"],
  "clones": ["my_clone"],
  "default": "alba"
}

Create Voice Clone

POST /pocket-tts/voices/clone

Upload a reference audio file to create a voice clone. No transcript needed.

Parameter Type Description
audio * File Reference audio file (WAV, MP3, OGG)
cloneId (optional) string Custom name for the clone (alphanumeric, underscore, hyphen)

cURL Example

curl -X POST https://voice.sogni.ai/pocket-tts/voices/clone \
  -F "audio=@/path/to/reference.wav" \
  -F "cloneId=my_voice"

Response

{
  "success": true,
  "cloneId": "my_voice",
  "message": "Voice clone created successfully"
}

Generate with Cloned Voice

POST /pocket-tts/voices/clone/{cloneId}/generate

cURL Example

curl -X POST https://voice.sogni.ai/pocket-tts/voices/clone/my_voice/generate \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello from my cloned voice!"}' \
  --output output.wav

Delete Voice Clone

DELETE /pocket-tts/voices/clone/{cloneId}

cURL Example

curl -X DELETE https://voice.sogni.ai/pocket-tts/voices/clone/my_voice

Download Voice Clone

GET /pocket-tts/voices/clone/{cloneId}/download

Download a voice clone as a ZIP file containing the reference audio and metadata. Useful for backup or transferring clones between servers.

Response

Returns a ZIP file (Content-Type: application/zip) containing:

cURL Example

curl https://voice.sogni.ai/pocket-tts/voices/clone/my_voice/download \
  --output my_voice.zip

JavaScript Example

const response = await fetch(
  'https://voice.sogni.ai/pocket-tts/voices/clone/my_voice/download'
);

const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'my_voice.zip';
a.click();

Python Example

import requests

response = requests.get(
    'https://voice.sogni.ai/pocket-tts/voices/clone/my_voice/download'
)

with open('my_voice.zip', 'wb') as f:
    f.write(response.content)

Import Voice Clone

POST /pocket-tts/voices/clone/import

Import a previously exported voice clone from a ZIP file. The ZIP must contain a reference.wav file.

Import access: If voiceCloneImports.mode is api_key, provide a valid API key via X-API-Key or Authorization: Bearer. If it is blocked, the server must set AUTH_API_KEY or DANGEROUSLY_ALLOW_IMPORTS=1 before imports will work.

Parameter Type Description
file * File ZIP file containing the voice clone
cloneId (optional) string Custom name for the imported clone. If omitted, uses the name from metadata.

cURL Example

curl -X POST https://voice.sogni.ai/pocket-tts/voices/clone/import \
  -H "X-API-Key: sk_your_secret_key_here" \
  -F "file=@my_voice.zip" \
  -F "cloneId=restored_voice"

Response

{
  "success": true,
  "cloneId": "restored_voice",
  "message": "Voice clone imported successfully"
}

JavaScript Example

const formData = new FormData();
formData.append('file', zipFile);
formData.append('cloneId', 'my_imported_voice');

const response = await fetch('https://voice.sogni.ai/pocket-tts/voices/clone/import', {
  method: 'POST',
  headers: { 'X-API-Key': 'sk_your_secret_key_here' },
  body: formData
});

const data = await response.json();
console.log('Imported clone:', data.cloneId);

Python Example

import requests

with open('my_voice.zip', 'rb') as f:
    response = requests.post(
        'https://voice.sogni.ai/pocket-tts/voices/clone/import',
        headers={'X-API-Key': 'sk_your_secret_key_here'},
        files={'file': f},
        data={'cloneId': 'restored_voice'}
    )

data = response.json()
print(f"Imported clone: {data['cloneId']}")

4. TTS (Text-to-Speech) - Qwen3

Qwen3-TTS runs through pinned 8-bit MLX-Audio models on Apple Silicon, with separate daemons for voice cloning, emotion/style control, and lazy VoiceDesign. Requires QWEN_TTS_ENABLED=1. It supports Chinese, English, French, German, Italian, Japanese, Korean, Portuguese, Russian, and Spanish, plus automatic language selection.

Generate Speech

POST /qwen-tts
Parameter Type Default Description
text * string - Text to convert to speech (1-10,000 characters)
voice (optional) string Ryan Voice to use (see /qwen-tts/voices for available voices)
language (optional) string English Language for synthesis
format (optional) string wav Output format: "wav", "opus", or "buffer" (base64 JSON). Also accepted as ?format= on this endpoint.

cURL Example

curl -X POST https://voice.sogni.ai/qwen-tts \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, world!", "voice": "Ryan"}' \
  --output output.wav

Opus via query-string format override

curl -X POST 'https://voice.sogni.ai/qwen-tts?format=opus' \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, world!", "voice": "Ryan"}' \
  --output output.opus

Custom Voice (Emotion/Style Control)

POST /qwen-tts/custom-voice

Generate speech with emotion and style instructions using the dedicated CustomVoice daemon.

Parameter Type Description
text * string Text to convert to speech
speaker (optional) string Speaker voice to use (default: Ryan)
instruct * string Emotion/style instruction (e.g., "Very happy and excited", "Speak slowly with a calm tone")
language (optional) string Language for synthesis (default: English)
format (optional) string Output format: "wav", "opus", or "buffer". Also accepted as ?format= on this endpoint.

cURL Example

curl -X POST https://voice.sogni.ai/qwen-tts/custom-voice \
  -H "Content-Type: application/json" \
  -d '{"text": "I am so excited!", "speaker": "Ryan", "instruct": "Very happy and enthusiastic", "language": "English"}' \
  --output excited.wav

Opus via query-string format override

curl -X POST 'https://voice.sogni.ai/qwen-tts/custom-voice?format=opus' \
  -H "Content-Type: application/json" \
  -d '{"text": "I am so excited!", "speaker": "Ryan", "instruct": "Very happy and enthusiastic", "language": "English"}' \
  --output excited.opus

Voice Design (Create Voice from Description)

POST /qwen-tts/voice-design

Generate speech using a voice created from a text description. The dedicated VoiceDesign daemon downloads and loads lazily on its first request.

Parameter Type Description
text * string Text to convert to speech
instruct * string Voice description (e.g., "A deep male voice with a warm, calm tone")
language (optional) string Language for synthesis (default: English)
format (optional) string Output format: "wav", "opus", or "buffer". Also accepted as ?format= on this endpoint.

cURL Example

curl -X POST https://voice.sogni.ai/qwen-tts/voice-design \
  -H "Content-Type: application/json" \
  -d '{"text": "Welcome to our service.", "instruct": "A deep male voice with calm, professional tone", "language": "English"}' \
  --output designed_voice.wav

Opus via query-string format override

curl -X POST 'https://voice.sogni.ai/qwen-tts/voice-design?format=opus' \
  -H "Content-Type: application/json" \
  -d '{"text": "Welcome to our service.", "instruct": "A deep male voice with calm, professional tone", "language": "English"}' \
  --output designed_voice.opus

List Voices

GET /qwen-tts/voices

Response

{
  "voices": ["Ryan", "Aiden", "Serena", "Vivian", "Uncle_Fu", "Dylan", "Eric", "Ono_Anna", "Sohee"],
  "clones": ["my_clone"],
  "default": "Ryan",
  "defaultLanguage": "English",
  "modelVariants": {
    "base": "base-0.6b",
    "customVoice": "custom-voice",
    "voiceDesign": "voice-design"
  },
  "backend": "mlx",
  "features": ["tts", "voice_cloning", "custom_voice", "voice_design"],
  "status": "ready"
}

Create Voice Clone

POST /qwen-tts/voices/clone

Upload a reference audio file with its transcript to create a voice clone using the Base daemon.

Parameter Type Description
audio * File Reference audio file (1-30 seconds accepted; 3-10 clean seconds recommended, WAV/MP3/OGG)
transcript * string Exact text spoken in the reference audio
cloneId (optional) string Custom name for the clone (alphanumeric, underscore, hyphen)

cURL Example

curl -X POST https://voice.sogni.ai/qwen-tts/voices/clone \
  -F "audio=@/path/to/reference.wav" \
  -F "transcript=Hello, this is my voice sample." \
  -F "cloneId=my_voice"

Response

{
  "success": true,
  "cloneId": "my_voice",
  "message": "Voice clone created successfully"
}

Generate with Cloned Voice

POST /qwen-tts/voices/clone/{cloneId}/generate
Parameter Type Description
text * string Text to convert to speech
language (optional) string Language for synthesis (default: English)
format (optional) string Output format: "wav", "opus", or "buffer"

cURL Example

curl -X POST https://voice.sogni.ai/qwen-tts/voices/clone/my_voice/generate \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello from my cloned voice!"}' \
  --output cloned_output.wav

Rename Voice Clone

PATCH /qwen-tts/voices/clone/{cloneId}
Parameter Type Description
newCloneId * string New name for the voice clone

cURL Example

curl -X PATCH https://voice.sogni.ai/qwen-tts/voices/clone/my_voice \
  -H "Content-Type: application/json" \
  -d '{"newCloneId": "renamed_voice"}'

Delete Voice Clone

DELETE /qwen-tts/voices/clone/{cloneId}

cURL Example

curl -X DELETE https://voice.sogni.ai/qwen-tts/voices/clone/my_voice

Response

{
  "success": true,
  "cloneId": "my_voice",
  "message": "Voice clone deleted successfully"
}

Download Voice Clone

GET /qwen-tts/voices/clone/{cloneId}/download

Download a voice clone as a ZIP file containing the voice embedding and metadata. Useful for backup or transferring clones between servers.

Response

Returns a ZIP file (Content-Type: application/zip) containing:

cURL Example

curl https://voice.sogni.ai/qwen-tts/voices/clone/my_voice/download \
  --output my_voice.zip

JavaScript Example

const response = await fetch(
  'https://voice.sogni.ai/qwen-tts/voices/clone/my_voice/download'
);

const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'my_voice.zip';
a.click();

Python Example

import requests

response = requests.get(
    'https://voice.sogni.ai/qwen-tts/voices/clone/my_voice/download'
)

with open('my_voice.zip', 'wb') as f:
    f.write(response.content)

Import Voice Clone

POST /qwen-tts/voices/clone/import

Import a previously exported voice clone from a ZIP file. The ZIP must contain a .safetensors file.

Safe ICL .safetensors clones from the former PyTorch backend migrate lazily: codec tokens are decoded, the reconstructed speaker is checked against the stored embedding, and the reference is cached. Clones created with the 0.6B model require base-0.6b; 1.7B clones require base-1.7b. Pickle files remain rejected.

Import access: If voiceCloneImports.mode is api_key, provide a valid API key via X-API-Key or Authorization: Bearer. If it is blocked, the server must set AUTH_API_KEY or DANGEROUSLY_ALLOW_IMPORTS=1 before imports will work.

Parameter Type Description
file * File ZIP file containing the voice clone (.safetensors + optional metadata.json)
cloneId (optional) string Custom name for the imported clone. If omitted, uses the name from metadata or filename.

cURL Example

curl -X POST https://voice.sogni.ai/qwen-tts/voices/clone/import \
  -H "X-API-Key: sk_your_secret_key_here" \
  -F "file=@my_voice.zip" \
  -F "cloneId=restored_voice"

Response

{
  "success": true,
  "cloneId": "restored_voice",
  "message": "Voice clone imported successfully"
}

JavaScript Example

const formData = new FormData();
formData.append('file', zipFile);
formData.append('cloneId', 'my_imported_voice');

const response = await fetch('https://voice.sogni.ai/qwen-tts/voices/clone/import', {
  method: 'POST',
  headers: { 'X-API-Key': 'sk_your_secret_key_here' },
  body: formData
});

const data = await response.json();
console.log('Imported clone:', data.cloneId);

Python Example

import requests

with open('my_voice.zip', 'rb') as f:
    response = requests.post(
        'https://voice.sogni.ai/qwen-tts/voices/clone/import',
        headers={'X-API-Key': 'sk_your_secret_key_here'},
        files={'file': f},
        data={'cloneId': 'restored_voice'}
    )

data = response.json()
print(f"Imported clone: {data['cloneId']}")

5. TTS (Text-to-Speech) - MOSS-TTS-Nano

MOSS-TTS-Nano is an Apache-2.0, 100M-parameter multilingual reference-voice model running through MLX-Audio. It produces 48 kHz stereo audio and requires a saved reference voice; it has no built-in speaker roster. Enable it with MOSS_TTS_ENABLED=1.

Current limitation: the MLX implementation does not implement streaming, so capability responses explicitly return "streaming": false.

List Capabilities and Reference Voices

GET /moss-tts/voices

This lightweight endpoint does not load the model. Saved voice IDs are hidden unless the caller has clone access.

{
  "voices": ["narrator"],
  "default": "narrator",
  "model": "mlx-community/MOSS-TTS-Nano-100M",
  "features": ["multilingual_tts", "voice_cloning"],
  "streaming": false,
  "sampleRate": 48000,
  "languages": [{"code": "en", "name": "English"}, ...],
  "referenceAudio": {"minSeconds": 1, "maxSeconds": 30, "recommendedSeconds": "5-10"}
}

Create a Reference Voice

POST /moss-tts/voices/clone

Upload 1-30 seconds of clean, single-speaker audio; 5-10 seconds is recommended. No transcript is required. MP3, WAV, M4A, MP4, WebM, OGG, and FLAC are accepted and normalized to a frozen 48 kHz PCM WAV.

Authentication required by default: send a valid API key, or explicitly set DANGEROUSLY_ALLOW_VOICE_CLONING=1 for public local use.

curl -X POST https://voice.sogni.ai/moss-tts/voices/clone \
  -H "X-API-Key: your_api_key_here" \
  -F "[email protected]" \
  -F "voiceId=narrator"

Generate Speech

POST /moss-tts
ParameterTypeDefaultDescription
text *string-Text to synthesize (1-10,000 characters)
voicestringMOSS_TTS_DEFAULT_VOICESaved reference-voice ID; required if no default is configured
formatstringwavwav, opus, or buffer; also accepted as ?format=
curl -X POST https://voice.sogni.ai/moss-tts \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"text":"Hola desde MOSS-TTS-Nano.","voice":"narrator"}' \
  --output output.wav

Rename a Reference Voice

PATCH /moss-tts/voices/clone/{voiceId}
curl -X PATCH https://voice.sogni.ai/moss-tts/voices/clone/narrator \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"newVoiceId":"studio_voice"}'

Delete a Reference Voice

DELETE /moss-tts/voices/clone/{voiceId}
curl -X DELETE https://voice.sogni.ai/moss-tts/voices/clone/studio_voice \
  -H "X-API-Key: your_api_key_here"

Supported Languages

Chinese, English, German, Spanish, French, Japanese, Italian, Hebrew, Korean, Russian, Persian, Arabic, Polish, Portuguese, Czech, Danish, Swedish, Hungarian, Greek, and Turkish.

6. Health Check

Check if the API server is running and healthy.

GET /health

Response

{
  "status": "healthy",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "uptime": 3600
}

cURL Example

curl https://voice.sogni.ai/health