API pour développeurs

Lumavo expose une API REST pour intégrer la génération directement dans vos applications, vos automatisations ou vos outils internes — sans passer par l'interface.

Disponibilité

L'API suit la mise à disposition progressive des capacités de Lumavo :

Base

https://api.getlumavo.com/v1

Toutes les requêtes sont en HTTPS et échangent du JSON (Content-Type: application/json).

Authentification

Chaque requête doit porter votre clé d'API dans l'en-tête X-LUMAVO-API-KEY :

X-LUMAVO-API-KEY: votre_clé

Créez et gérez vos clés depuis votre compte, section Clés API. Une clé porte des permissions (voix, vidéo, avatar, script) : elle n'a accès qu'aux capacités que vous lui accordez. Gardez vos clés secrètes ; en cas de fuite, révoquez-les et recréez-en une.

L'ancien en-tête X-LUMA-API-KEY reste accepté (rétro-compatibilité).

Démarrage rapide (client)

Clients sans dépendance, prêts à copier. Ils créent une voix et attendent le résultat.

Node.js (18+)

const KEY = process.env.LUMAVO_API_KEY, BASE = 'https://api.getlumavo.com';
const call = (p, o = {}) => fetch(BASE + p, { ...o,
  headers: { 'X-LUMAVO-API-KEY': KEY, 'Content-Type': 'application/json', ...(o.headers || {}) },
}).then((r) => r.ok ? r.json() : Promise.reject(r.status));

const job = await call('/v1/tts', { method: 'POST', body: JSON.stringify({ text: 'Bonjour !', voice: 'narrator' }) });
let res;
do { await new Promise((r) => setTimeout(r, 1500)); res = await call(`/v1/jobs/${job.id}`); }
while (res.status !== 'done' && res.status !== 'failed');
console.log(res.resultUrl);   // → WAV téléchargeable

Python (3.8+)

import os, time, json, urllib.request
KEY, BASE = os.environ["LUMAVO_API_KEY"], "https://api.getlumavo.com"
def call(path, method="GET", body=None):
    req = urllib.request.Request(BASE + path, data=json.dumps(body).encode() if body else None,
        method=method, headers={"X-LUMAVO-API-KEY": KEY, "Content-Type": "application/json"})
    return json.load(urllib.request.urlopen(req))

job = call("/v1/tts", "POST", {"text": "Bonjour !", "voice": "narrator"})
res = job
while res["status"] not in ("done", "failed"):
    time.sleep(1.5); res = call(f"/v1/jobs/{job['id']}")
print(res["resultUrl"])   # → WAV téléchargeable

Créer une synthèse vocale

curl -X POST https://api.getlumavo.com/v1/tts \
  -H "X-LUMAVO-API-KEY: votre_clé" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Bonjour, ceci est ma voix générée par Lumavo.",
    "voice": "calm",
    "speed": 1.0
  }'

La réponse (202 Accepted) contient l'identifiant de la tâche :

{ "id": "b1f2…", "status": "pending" }
Champ Type Obligatoire Détail
text string oui Le texte à lire (jusqu'à 8000 car.)
voice string non Voix/style — voir GET /v1/voices
emotion string non Ton (ex. calme, dynamique)
speed number non Vitesse, entre 0.5 et 2 (défaut 1)

Voix disponibles

curl https://api.getlumavo.com/v1/voices -H "X-LUMAVO-API-KEY: votre_clé"

Renvoie la liste des voix pour le champ voice : calm (calme), narrator (narrateur), cinematic (cinématique), ad (publicité), tiktok (réseaux sociaux).

Transcrire un média (speech-to-text)

Convertit un fichier audio ou vidéo (accessible par URL) en texte, avec le découpage horodaté par segment — parfait pour générer des sous-titres, indexer ou analyser du contenu. Réponse directe (synchrone), pas de tâche à sonder.

curl -X POST https://api.getlumavo.com/v1/transcribe \
  -H "X-LUMAVO-API-KEY: votre_clé" \
  -H "Content-Type: application/json" \
  -d '{
    "mediaUrl": "https://exemple.com/audio.mp3",
    "language": "fr"
  }'

Réponse (200 OK) :

{
  "text": "Bonjour et bienvenue sur Lumavo.",
  "language": "fr",
  "languageSource": "detected",
  "duration": 2.4,
  "segments": [
    { "start": 0.0, "end": 2.4, "text": "Bonjour et bienvenue sur Lumavo." }
  ]
}
Champ Type Obligatoire Détail
mediaUrl string oui URL http(s) du fichier audio/vidéo à transcrire
language string non Code langue ISO (ex. fr, en) — auto-détectée si omise

Langues : passe un code ISO 639-1 (fr, en, es, de, ar, zh…) pour forcer la langue, ou omets language pour la détection automatique (une centaine de langues supportées).

Le champ languageSource te dit d'où vient language, pour que tu saches si tu peux t'y fier comme détection :

La réponse inclut aussi duration (durée de l'audio en secondes).

Scope requis : transcribe.

Transcrire un fichier local (upload)

L'API ne reçoit pas le fichier directement : tu téléverses d'abord vers une URL signée, puis tu passes l'URL obtenue à /v1/transcribe. Trois étapes :

1. Demande une URL d'upload (POST /v1/uploads) :

curl -X POST https://api.getlumavo.com/v1/uploads \
  -H "X-LUMAVO-API-KEY: votre_clé" -H "Content-Type: application/json" \
  -d '{ "filename": "reunion.mp3", "contentType": "audio/mpeg" }'
{
  "method": "PUT",
  "uploadUrl": "https://cdn.getlumavo.com/luma-uploads/…?X-Amz-…",
  "mediaUrl":  "https://cdn.getlumavo.com/luma-uploads/…?X-Amz-…",
  "objectKey": "…",
  "uploadExpiresIn": 3600,
  "mediaExpiresIn": 86400
}

2. Envoie ton fichier en PUT sur uploadUrl (aucune clé API ici — l'URL est déjà signée) :

curl -X PUT "<uploadUrl>" --data-binary @reunion.mp3 -H "Content-Type: audio/mpeg"

3. Transcris en passant mediaUrl :

curl -X POST https://api.getlumavo.com/v1/transcribe \
  -H "X-LUMAVO-API-KEY: votre_clé" -H "Content-Type: application/json" \
  -d '{ "mediaUrl": "<mediaUrl>", "language": "fr" }'

POST /v1/uploads est gratuit (il ne fait que générer des URLs signées) ; seule la transcription est débitée. Le fichier n'a jamais besoin d'être public : mediaUrl est une URL signée temporaire.

Rétention : les fichiers téléversés sont automatiquement supprimés après ~24 h (au-delà, l'URL signée a de toute façon expiré). Transcris juste après l'upload ; ne t'appuie pas sur un stockage durable côté Lumavo.

Éviter les doublons (idempotence)

Ajoutez un en-tête Idempotency-Key (un identifiant unique que vous générez) à POST /v1/tts · /render · /lipsync. Si vous rejouez la même requête (timeout réseau, retry), Lumavo renvoie la tâche déjà créée au lieu d'en créer — et ne débite pas deux fois.

curl -X POST https://api.getlumavo.com/v1/tts \
  -H "X-LUMAVO-API-KEY: votre_clé" \
  -H "Idempotency-Key: 3f8c1e2a-…" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Bonjour", "voice": "calm" }'

Récupérer le résultat

La génération est asynchrone : interrogez le statut de la tâche jusqu'à ce qu'elle soit terminée, puis récupérez l'URL du fichier.

curl https://api.getlumavo.com/v1/jobs/b1f2… \
  -H "X-LUMAVO-API-KEY: votre_clé"
{ "id": "b1f2…", "type": "tts_only", "status": "done", "progress": 100, "resultUrl": "https://…/audio.wav" }

Chaque tâche a une forme stable : { id, type, status, progress, resultUrl, error, duration, createdAt }. Interrogez ce point de terminaison toutes les 1–2 secondes jusqu'à status: "done" (ou "failed"). L'URL renvoyée est directement téléchargeable.

Lister ses tâches

curl "https://api.getlumavo.com/v1/jobs?limit=20" \
  -H "X-LUMAVO-API-KEY: votre_clé"

Renvoie vos tâches les plus récentes, de la plus récente à la plus ancienne (paramètre limit optionnel, max 100).

Gérer ses clés par l'API

Méthode & route Rôle
POST /v1/keys Créer une clé
GET /v1/keys Lister ses clés
POST /v1/keys/:id/revoke Révoquer une clé

Webhooks (recommandé)

Au lieu de sonder /v1/jobs/:id, enregistrez une URL de callback : Lumavo vous notifie dès qu'une tâche se termine (job.completed) ou échoue (job.failed).

curl -X POST https://api.getlumavo.com/v1/webhooks \
  -H "X-LUMAVO-API-KEY: votre_clé" -H "Content-Type: application/json" \
  -d '{"url":"https://votre-app.com/lumavo/callback"}'
{ "id": "…", "url": "https://votre-app.com/lumavo/callback", "secret": "whsec_…", "active": true }

Conservez le secret (affiché une seule fois). Chaque livraison est signée : l'en-tête X-LUMAVO-Signature contient HMAC-SHA256(corps, secret) — recalculez-le côté serveur pour vérifier l'authenticité. Corps reçu :

{ "event": "job.completed", "data": { "jobId": "…", "status": "done", "resultUrl": "https://…" }, "timestamp": 1720000000000 }

Vérifier la signature (indispensable) — recalculez le HMAC sur le corps brut (pas le JSON re-sérialisé) et comparez à temps constant :

// Express
import { createHmac, timingSafeEqual } from 'node:crypto';
app.post('/callback', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.header('X-LUMAVO-Signature') || '';
  const expected = createHmac('sha256', SECRET).update(req.body).digest('hex');
  const ok = sig.length === expected.length && timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
  if (!ok) return res.sendStatus(401);
  const { event, data } = JSON.parse(req.body);   // data = { jobId, status, resultUrl }
  res.sendStatus(200);
});
# Flask
import hmac, hashlib
@app.post("/callback")
def callback():
    sig = request.headers.get("X-LUMAVO-Signature", "")
    expected = hmac.new(SECRET.encode(), request.data, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        abort(401)
    event = request.get_json()   # { event, data: { jobId, status, resultUrl }, timestamp }
    return "", 200
Méthode & route Rôle
POST /v1/webhooks Enregistrer une URL
GET /v1/webhooks Lister ses webhooks
POST /v1/webhooks/:id/test Envoyer un événement de test
DELETE /v1/webhooks/:id Supprimer

Bientôt

Ces points de terminaison existent et seront activés progressivement :

Ils suivent le même principe : la requête renvoie une tâche, dont vous suivez le statut via /v1/jobs/:id.

Codes de réponse

Code Signification
202 Tâche acceptée et mise en file
200 Requête réussie (statut, listes)
401 Clé d'API absente ou invalide
403 La clé n'a pas la permission requise
402 Crédits insuffisants
429 Trop de requêtes — ralentissez (limite de débit par clé)

Limites & bon usage

curl https://api.getlumavo.com/v1/usage -H "X-LUMAVO-API-KEY: votre_clé"
{ "rateLimit": { "limit": 60, "remaining": 58, "windowSeconds": 60, "resetSeconds": 42 },
  "dailyQuota": { "limit": 2000, "used": 12, "remaining": 1988 } }