API Documentation
Quick Start
Quick Start Guide
Get your API key
Your API key is available on the Settings page. Include it in every request as the X-API-Key header.
Submit a job
curl -X POST https://siren.example.com/api/jobs \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "full",
"voiceTemplateId": "vt_template1",
"input": { "script": "Hello world", "videoUrl": "https://..." },
"output": { "webhookUrl": "https://your-webhook.com/done" }
}'import requests
response = requests.post(
"https://siren.example.com/api/jobs",
headers={"X-API-Key": "YOUR_KEY"},
json={
"type": "full",
"voiceTemplateId": "vt_template1",
"input": {"script": "Hello world", "videoUrl": "https://..."},
"output": {"webhookUrl": "https://your-webhook.com/done"},
},
)
print(response.json()) # {"job_id": "...", "status": "queued"}const response = await fetch("https://siren.example.com/api/jobs", {
method: "POST",
headers: {
"X-API-Key": "YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "full",
voiceTemplateId: "vt_template1",
input: { script: "Hello world", videoUrl: "https://..." },
output: { webhookUrl: "https://your-webhook.com/done" },
}),
});
const data = await response.json();
console.log(data); // { job_id: "...", status: "queued" }Check results
Poll GET /api/jobs/{job_id} or receive a webhook callback when the job completes.
Voice Cloning & Intonation
Siren clones a voice from a short reference clip, then speaks any text in that voice. You control how it speaks with three stacking layers: expressive styles (the strongest — emotion comes from a recorded take), voice-design tags (pitch / whisper / accent), and prosody markup in the text itself.
Clone a voice
Upload a clean 5–30 second clip of one speaker. Add a reference transcript (the exact words in the clip) and a language for a higher-fidelity clone — both optional but recommended. Vietnamese (vi) routes to the dedicated Vietnamese model. You can also do this in the UI: Voices → Register Voice.
curl -X POST https://siren.nopslabs.com/api/voices/register \
-H "X-API-Key: YOUR_KEY" \
-F "name=Triet — Studio" \
-F "language=en" \
-F "ref_text=The exact words spoken in the clip." \
-F "file=@/path/to/reference.wav"
# → { "voice_id": "bacc...", "name": "Triet — Studio", "created_at": "..." }Add expressive styles (emotion)
A voice can store several style takes — e.g. one calm, one excited, one serious. Each is its own clip. At synthesis time you pick a styleId and the delivery inherits that take's emotion and phrasing. This is the most powerful lever — the quality of your reference takes is what gives genuine emotional range. Manage these in the UI under Voices → Manage styles.
curl -X POST https://siren.nopslabs.com/api/voices/VOICE_ID/styles \
-H "X-API-Key: YOUR_KEY" \
-F "label=Excited" \
-F "ref_text=Words spoken in the excited take." \
-F "file=@/path/to/excited_take.wav"
# → { "id": "style_2dcb...", "label": "Excited", ... }
# list / delete
curl https://siren.nopslabs.com/api/voices/VOICE_ID/styles -H "X-API-Key: YOUR_KEY"
curl -X DELETE https://siren.nopslabs.com/api/voices/VOICE_ID/styles/style_2dcb... -H "X-API-Key: YOUR_KEY"Synthesize with intonation
Combine everything in one call to /api/audio/synthesize. All intonation fields are optional and stack: pick a styleId for emotion, add voice-design tags, and use markup in the script. (For timed subtitles, send srt instead of script — each cue is duration-locked to its window.)
curl -X POST https://siren.nopslabs.com/api/audio/synthesize \
-H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{
"voiceId": "VOICE_ID",
"styleId": "style_2dcb...", // optional — clone from this expressive take
"tts_language": "en",
"tts_quality": "quality",
"pitch": "high pitch", // voice-design (validated, optional)
"accent": "british accent",
"whisper": false,
"speed": 1.0,
"script": "Wait [pause] this is the **only** way... really."
}'
# → { "audioUrl": "https://...", "srtUrl": "https://..." }Voice-design tags
Coarse, global shaping that layers on top of a clone. Values are validated — an unknown value returns 422 with the allowed list.
pitch
- very low pitch
- low pitch
- moderate pitch
- high pitch
- very high pitch
accent
- american accent
- british accent
- australian accent
- canadian accent
- indian accent
- chinese accent
- korean accent
- japanese accent
- russian accent
- portuguese accent
whisper
true for a whispered delivery.
speed
0.5–2.0 (plain text only; SRT auto-fits each cue).
Prosody markup
Write these directly in your script for sentence-level phrasing. They compile to punctuation the model turns into real pauses — and work together with a cloned voice and a style.
| Markup | Effect |
|---|---|
**word** | Emphasis — isolates the phrase so the voice lifts and breaks around it. |
[pause] | A short beat (≈ one ellipsis hold). |
[pause 400ms] | A timed hold — longer numbers give longer pauses. |
... | Trailing pause / hesitation (becomes a true ellipsis). |
-- | A brief mid-sentence break (becomes a comma). |
Example: "Wait [pause] this is the **only** way... really." → spoken with a beat after "Wait", emphasis on "only", and a trailing hesitation before "really".
Which lever should I use?
- Emotion / tone → register a style take (step 2). Strongest, most natural.
- Phrasing / pauses / emphasis → prosody markup in the script.
- Register / whisper / accent → voice-design tags. Coarse but free.
Pipeline Modes
Pipeline Modes
Choose the mode that matches your use case. Each mode accepts different inputs and produces different outputs.
Full Pipeline
fullGenerates speech from text and syncs it with a video. End-to-end pipeline.
Required Inputs
- script
- videoUrl
- voiceTemplateId
Example Request
{
"type": "full",
"voiceTemplateId": "vt_template1",
"input": {
"script": "Welcome to our platform",
"videoUrl": "https://storage.example.com/video.mp4"
},
"output": {
"webhookUrl": "https://your-app.com/webhook"
}
}Expected Outputs
- Final video with synced audio (.mp4)
- Generated audio (.wav)
- Subtitle file (.srt)
TTS Only
tts_onlyConverts text to speech using a voice template. No video processing.
Required Inputs
- script
- voiceTemplateId
Example Request
{
"type": "tts_only",
"voiceTemplateId": "vt_template1",
"input": {
"script": "Hello, this is a test."
},
"output": {
"webhookUrl": "https://your-app.com/webhook"
}
}Expected Outputs
- Generated audio file (.wav)
Lipsync Only
lipsync_onlySyncs existing audio with a video. Skips speech generation.
Required Inputs
- audioUrl
- videoUrl
Example Request
{
"type": "lipsync_only",
"input": {
"audioUrl": "https://storage.example.com/audio.wav",
"videoUrl": "https://storage.example.com/video.mp4"
},
"output": {
"webhookUrl": "https://your-app.com/webhook"
}
}Expected Outputs
- Final video with synced audio (.mp4)
Webhook Payloads
Webhook Payload Examples
Job Completed
job.completed{
"event": "job.completed",
"job_id": "job_abc123",
"type": "full",
"status": "completed",
"created_at": "2025-01-15T10:30:00Z",
"completed_at": "2025-01-15T10:32:45Z",
"output": {
"video_url": "https://storage.example.com/output/job_abc123/final.mp4",
"audio_url": "https://storage.example.com/output/job_abc123/audio.wav",
"subtitle_url": "https://storage.example.com/output/job_abc123/subtitles.srt",
"duration_seconds": 45.2
}
}Job Failed
job.failed{
"event": "job.failed",
"job_id": "job_def456",
"type": "tts_only",
"status": "failed",
"created_at": "2025-01-15T11:00:00Z",
"failed_at": "2025-01-15T11:01:12Z",
"error": {
"code": "TTS_GENERATION_FAILED",
"message": "Voice template not found or expired",
"retryable": true
}
}Signature Verification
All webhook payloads include an X-Siren-Signature header containing an HMAC-SHA256 signature. Verify this signature using your webhook secret to ensure the payload is authentic.
import hmac
import hashlib
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)Interactive API Explorer
Explore all available endpoints, try requests, and view response schemas.