{"openapi":"3.1.0","info":{"title":"TTS Voice API","version":"3.0.0","description":"Synthèse vocale gratuite avec deux moteurs de voix (edge : voix neuronales hommes/femmes ; google). Textes illimités (découpage automatique), vitesse/hauteur/volume réels, pauses maîtrisées, multi-voix, sous-titres SRT/VTT, jobs asynchrones avec progression. Clé d'API facultative : \"Authorization: Bearer <clé>\" ou \"X-API-Key\". Sans clé, la limite de requêtes est plus basse."},"servers":[{"url":"/"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer"},"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"SpeechRequest":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":100000,"description":"Texte à lire. Balises possibles : [pause 800ms], [voice en-US] … [voice], [slow] … [/slow]. Une ligne vide = nouveau paragraphe."},"voice":{"type":"string","default":"fr-FR","examples":["fr-FR","fr-FR-HenriNeural","fr-CA-JeanNeural"],"description":"Identifiant de voix (GET /v1/voices). Moteur google : fr-FR, en-US… (alias fr, en, es…). Moteur edge : fr-FR-HenriNeural, fr-FR-DeniseNeural…"},"speed":{"type":"number","minimum":0.25,"maximum":4,"default":1,"description":"Vitesse réelle sans changer la hauteur (native sur le moteur edge entre 0.5 et 2, sinon appliquée localement)."},"pitch":{"type":"number","minimum":-12,"maximum":12,"default":0,"description":"Hauteur en demi-tons."},"volume":{"type":"number","minimum":-20,"maximum":20,"default":0,"description":"Gain en dB (limiteur anti-saturation)."},"normalize":{"type":"boolean","default":false,"description":"Normalisation de loudness EBU R128 (-16 LUFS)."},"slow":{"type":"boolean","default":false,"description":"Diction lente (mode lent natif sur le moteur google, 0.8× sur le moteur edge)."},"format":{"type":"string","enum":["mp3","ogg","aac","flac","wav"],"default":"mp3"},"bitrate":{"type":"integer","minimum":16,"maximum":320,"description":"kb/s (mp3, ogg, aac)."},"sampleRate":{"type":"integer","minimum":8000,"maximum":48000,"default":24000},"sentencePause":{"type":"integer","minimum":0,"maximum":5000,"default":350,"description":"Silence (ms) entre deux requêtes terminées par une fin de phrase."},"paragraphPause":{"type":"integer","minimum":0,"maximum":10000,"default":800,"description":"Silence (ms) entre paragraphes."},"cleanup":{"type":"boolean","default":false,"description":"Retire markdown, URLs et emojis (pratique pour lire une réponse de LLM)."},"markup":{"type":"boolean","default":true,"description":"Interpréter les balises [pause]/[voice]/[slow]."},"response":{"type":"string","enum":["audio","json","base64"],"default":"audio"},"filename":{"type":"string"},"download":{"type":"boolean"}},"example":{"text":"Bonjour ! [pause 500ms] Bienvenue.","voice":"fr-FR","speed":1.1}},"Segment":{"type":"object","properties":{"index":{"type":"integer"},"text":{"type":"string"},"voice":{"type":"string"},"start":{"type":"number"},"end":{"type":"number"}}},"SpeechResult":{"type":"object","properties":{"id":{"type":"string"},"format":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"integer"},"duration":{"type":"number","description":"secondes"},"requests":{"type":"integer"},"cached":{"type":"boolean"},"audioUrl":{"type":"string"},"subtitles":{"type":"object","properties":{"srt":{"type":"string"},"vtt":{"type":"string"}}},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"audioBase64":{"type":"string","description":"uniquement avec response=base64"}}},"Job":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["queued","running","done","error","cancelled"]},"progress":{"type":"object","properties":{"done":{"type":"integer"},"total":{"type":"integer"}}},"result":{"type":["object","null"]},"error":{"type":["object","null"]}}},"Voice":{"type":"object","properties":{"id":{"type":"string"},"engine":{"type":"string","enum":["google","edge"]},"language":{"type":"string"},"locale":{"type":"string"},"label":{"type":"string"},"name":{"type":"string"},"gender":{"type":["string","null"],"enum":["male","female",null]},"multilingual":{"type":"boolean"},"personalities":{"type":"array","items":{"type":"string"}}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"field":{"type":"string"}}}}}}},"security":[{},{"bearer":[]},{"apiKey":[]}],"paths":{"/v1/health":{"get":{"summary":"État du serveur et capacités","responses":{"200":{"description":"OK"}}}},"/v1/voices":{"get":{"summary":"Voix disponibles","parameters":[{"name":"language","in":"query","schema":{"type":"string"},"example":"fr"},{"name":"engine","in":"query","schema":{"type":"string","enum":["google","edge"]}},{"name":"gender","in":"query","schema":{"type":"string","enum":["male","female"]}},{"name":"q","in":"query","schema":{"type":"string"},"description":"Recherche dans l'id et le libellé"}],"responses":{"200":{"description":"Liste","content":{"application/json":{"schema":{"type":"object","properties":{"voices":{"type":"array","items":{"$ref":"#/components/schemas/Voice"}}}}}}}}}},"/v1/speech":{"post":{"summary":"Synthétiser un texte","description":"Renvoie l'audio directement (défaut) ou un JSON (response=json|base64). Accepte aussi un corps text/plain : le texte, avec les options en query string.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechRequest"}},"text/plain":{"schema":{"type":"string"}}}},"responses":{"200":{"description":"Audio, ou JSON selon \"response\". En-têtes : X-Audio-Id, X-Audio-Duration, X-Requests, X-Cache.","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"$ref":"#/components/schemas/SpeechResult"}}}},"400":{"description":"Paramètre invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Clé d'API invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Texte trop long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Limite de débit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Erreur du moteur de synthèse","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"Synthétiser (GET, pour <audio src=\"…\">)","parameters":[{"name":"text","in":"query","required":true,"schema":{"type":"string","maxLength":100000,"description":"Texte à lire. Balises possibles : [pause 800ms], [voice en-US] … [voice], [slow] … [/slow]. Une ligne vide = nouveau paragraphe."}},{"name":"voice","in":"query","required":false,"schema":{"type":"string","default":"fr-FR","examples":["fr-FR","fr-FR-HenriNeural","fr-CA-JeanNeural"],"description":"Identifiant de voix (GET /v1/voices). Moteur google : fr-FR, en-US… (alias fr, en, es…). Moteur edge : fr-FR-HenriNeural, fr-FR-DeniseNeural…"}},{"name":"speed","in":"query","required":false,"schema":{"type":"number","minimum":0.25,"maximum":4,"default":1,"description":"Vitesse réelle sans changer la hauteur (native sur le moteur edge entre 0.5 et 2, sinon appliquée localement)."}},{"name":"pitch","in":"query","required":false,"schema":{"type":"number","minimum":-12,"maximum":12,"default":0,"description":"Hauteur en demi-tons."}},{"name":"volume","in":"query","required":false,"schema":{"type":"number","minimum":-20,"maximum":20,"default":0,"description":"Gain en dB (limiteur anti-saturation)."}},{"name":"normalize","in":"query","required":false,"schema":{"type":"boolean","default":false,"description":"Normalisation de loudness EBU R128 (-16 LUFS)."}},{"name":"slow","in":"query","required":false,"schema":{"type":"boolean","default":false,"description":"Diction lente (mode lent natif sur le moteur google, 0.8× sur le moteur edge)."}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["mp3","ogg","aac","flac","wav"],"default":"mp3"}},{"name":"bitrate","in":"query","required":false,"schema":{"type":"integer","minimum":16,"maximum":320,"description":"kb/s (mp3, ogg, aac)."}},{"name":"sampleRate","in":"query","required":false,"schema":{"type":"integer","minimum":8000,"maximum":48000,"default":24000}},{"name":"sentencePause","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":5000,"default":350,"description":"Silence (ms) entre deux requêtes terminées par une fin de phrase."}},{"name":"paragraphPause","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":10000,"default":800,"description":"Silence (ms) entre paragraphes."}},{"name":"cleanup","in":"query","required":false,"schema":{"type":"boolean","default":false,"description":"Retire markdown, URLs et emojis (pratique pour lire une réponse de LLM)."}},{"name":"markup","in":"query","required":false,"schema":{"type":"boolean","default":true,"description":"Interpréter les balises [pause]/[voice]/[slow]."}}],"responses":{"200":{"description":"Audio","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Paramètre invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/plan":{"post":{"summary":"Aperçu du découpage (aucune synthèse)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechRequest"}}}},"responses":{"200":{"description":"Segments prévus, nombre de requêtes, durée estimée"}}}},"/v1/jobs":{"post":{"summary":"Créer une synthèse asynchrone (textes longs)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechRequest"}}}},"responses":{"202":{"description":"Job créé","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}}}}},"/v1/jobs/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"État d'un job","responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"404":{"description":"Introuvable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Annuler un job","responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}}}}},"/v1/jobs/{id}/events":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"Progression en direct (Server-Sent Events : progress, done, error, cancelled)","responses":{"200":{"description":"text/event-stream"}}}},"/v1/audio/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"download","in":"query","schema":{"type":"boolean"}}],"get":{"summary":"Télécharger un audio rendu (Range supporté)","responses":{"200":{"description":"Audio"},"404":{"description":"Introuvable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/audio/{id}/meta":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"Métadonnées et minutage des segments","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechResult"}}}}}}},"/v1/audio/{id}/subtitles.{kind}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"kind","in":"path","required":true,"schema":{"type":"string","enum":["srt","vtt"]}}],"get":{"summary":"Sous-titres synchronisés","responses":{"200":{"description":"SRT ou WebVTT"}}}}}}