AgileTTS — API

Il contratto delle API: due strati, un solo servizio. Il fronte è l'interfaccia OpenAI-compatibile con chiavi (per i prodotti, quando AgileTTS sarà maturo); il motore è il sintetizzatore residente, raggiungibile solo dalla rete interna WireGuard.

I due strati

stratoversionedovestato
motoreAGILETTS-v0.9 + cancello d'attaccorete interna WireGuardproduzione, residente (WARM), modello 0.6B
fronteAGILETTS-FRONTE-v0.6https://api.agiletts.agile.softwareconsegnato, non ancora in linea

Autenticazione (fronte)

Chiave nell'intestazione Authorization: Bearer ats_… oppure X-Api-Key. Ogni chiave è per un prodotto e un tenant, con uno scope (speech, stream, voices), un limite al minuto e una quota di caratteri al mese. I testi non vengono mai registrati: si contano solo richieste, caratteri e tempi.

Chiave passe-partout di prova: per chi integra per la prima volta, una chiave di ambito test con limiti bassi (20/min, 20000 caratteri/mese di default) e una scadenza dichiarata (di default 24 h), utile per uno smoke test prima di chiedere una chiave di prodotto. Scaduta risponde 403 KEY_EXPIRED. Il fronte accetta anche la chiave passe-partout condivisa fra tutti i servizi sovrani di Agile Software, la stessa per ognuno. Si richiede al centralino interno.

Fronte — rotte OpenAI-compatibili

Metodo e percorsoCosa fa
POST /v1/audio/speechsintesi vocale: model = agiletts (alias tts-1, tts-1-hd, gpt-4o-mini-tts), input ≤ 4096 caratteri (oltre 2000 il fronte spezza a fine frase e riunisce), voice, response_format = wav | pcm | pcm16k | mp3 | opus | flac | aac, speed 0,5–2,0, stream: true per l'audio a pezzi (solo pcm/pcm16k)
GET /v1/models, GET /v1/models/{id}i modelli disponibili, con created, owned_by, aliases; /v1/models/tts-1 restituisce l'oggetto con alias_di: "agiletts"
GET /v1/voicesle voci che la chiave può usare: id, lingua, lessico, ritmo, politica di ripiego — mai il riferimento né id esterni
GET /v1/usagei consumi della propria chiave: richieste, caratteri, millisecondi, audio prodotto, errori, stato e rotazioni della chiave — mai i testi
GET /v1/admin/keys, POST /v1/admin/keys, GET /v1/admin/keys/{id}, POST /v1/admin/keys/{id}/rotate, POST /v1/admin/keys/{id}/revokela console per azienda: un cliente elenca, crea, ruota e revoca le proprie chiavi da sé, con una credenziale di console (mai una chiave di prodotto). La chiave in chiaro si vede solo alla creazione; le chiavi di un'altra azienda rispondono come una chiave che non esiste; ogni operazione, e ogni rifiuto, resta scritta Dal 30/9 l’azienda si nomina e non si indovina: chi chiede le chiavi o le persone di un’altra azienda riceve un rifiuto, dove prima riceveva le proprie senza che nessuno glielo dicesse.
GET /v1/admin/utenti, POST /v1/admin/utenti, GET /v1/admin/utenti/{id}, POST /v1/admin/utenti/{id}/sospendi, POST /v1/admin/utenti/{id}/riattiva, POST /v1/admin/utenti/{id}/ruolole persone di un'azienda, dalla console: l'amministratore elenca, invita, sospende, riattiva e cambia ruolo ai propri utenti da sé. Chi sospende sé stesso, e chiunque lasci l'azienda senza amministratori attivi, si sente dire di no; le persone di un'altra azienda rispondono come una persona che non esiste; ogni operazione, e ogni rifiuto, resta scritta Dal 30/9 l’azienda si nomina e non si indovina: chi chiede le chiavi o le persone di un’altra azienda riceve un rifiuto, dove prima riceveva le proprie senza che nessuno glielo dicesse.
GET /v1/admin/operazioniil registro di chi ha fatto che cosa dentro l’azienda, letto dall’azienda: chi ha emesso una chiave e quando, chi l’ha revocata, chi ha invitato chi, e anche i tentativi rifiutati. Si filtra per persona, per azione e per intervallo di tempo, e si legge a pagine. Un utente semplice non lo vede; le righe di un’altra azienda non ci sono mai, e chi le chiede riceve un rifiuto invece della propria azienda senza dirlo. Anche leggerlo lascia la sua riga: guardare i dati di un’azienda è un accesso ai suoi dati.
GET /v1/admin/tenants/{id}/piano, PUT /v1/admin/tenants/{id}/pianoil piano concordato con l'azienda — quante richieste al minuto, quanti caratteri al mese — scritto dove il servizio lo può far valere. Lo scrive solo chi amministra tutto il servizio: un amministratore d'azienda che se lo alzasse da sé avrebbe un tetto per modo di dire. Lui lo legge, insieme al consumo del mese, e quel numero è lo stesso su cui il servizio rifiuta. Un'azienda senza piano risponde «nessun piano», non «azienda sconosciuta»; l'azienda altrui e una che non esiste rispondono allo stesso modo
GET /v1/admin/licenze, POST /v1/admin/licenze, GET /v1/admin/licenze/{id}, POST /v1/admin/licenze/{id}/sospendi, POST /v1/admin/licenze/{id}/riattiva, POST /v1/admin/licenze/{id}/rinnova, POST /v1/admin/licenze/{id}/revoca, POST /v1/admin/licenze/{id}/chiavi, DELETE /v1/admin/licenze/{id}/chiavi, GET /v1/admin/licenze/{id}/uso
(stesse rotte anche senza il prefisso di versione, la forma comune a tutti i servizi Agile: POST /admin/licenze, GET /admin/licenze, GET /admin/licenze/{id}, POST /admin/licenze/{id}/sospendi, POST /admin/licenze/{id}/riattiva, POST /admin/licenze/{id}/rinnova, POST /admin/licenze/{id}/revoca, POST /admin/licenze/{id}/chiavi, DELETE /admin/licenze/{id}/chiavi, GET /admin/licenze/{id}/uso)
la licenza: il diritto di un’azienda (o di una persona) di usare AgileTTS, con piano, ambiti, quota del mese, limite al minuto, inizio e scadenza. Le chiavi stanno sotto una licenza: la licenza si sospende e tutte le sue chiavi tacciono insieme, si riattiva e riprendono — senza ridistribuire niente ai prodotti; ruotare una chiave non tocca la licenza. Lo stato che conta è quello vero: una licenza con la scadenza di ieri si legge scaduta anche se in tabella c’è scritto attiva, e le sue chiavi ricevono un rifiuto che dice quale licenza e perché. Una scaduta si rinnova (non si riattiva); una revocata non torna: se ne rilascia un’altra. L’uso si misura per licenza ed è la riga che va in fattura, comprese le chiavi revocate e ruotate. I numeri di partenza del piano prova vengono dalla capacità misurata del servizio: 50000 caratteri al mese, 10 richieste al minuto, 30 giorni (l’uno per cento della capacità di servizio dichiarata, 5,6 milioni di caratteri al mese per scheda). standard ed enterprise sono su misura e non hanno numeri di partenza: si scrivono all’emissione, e chiederne una senza numeri è un rifiuto che lo dice. Il ciclo di vita lo governa chi amministra tutto il servizio; l’amministratore d’azienda legge le sue licenze e ci emette le chiavi dentro i limiti. La licenza di un’altra azienda risponde come una che non esiste
POST /v1/richieste-licenza (pubblica, senza chiave), GET /v1/admin/richieste
(anche senza il prefisso di versione: POST /richieste-licenza, GET /admin/richieste)
il modulo «richiedi una licenza» della pagina pubblica: azienda, referente, email, piano e note. Non manda una email e non inghiotte: la richiesta si registra con un numero e la pagina lo dice («richiesta registrata n. 7»), un giro sul box la porta a chi la deve leggere e la segna avvisata solo se l’avviso è partito davvero. I prezzi non stanno in nessuna risposta: sono su richiesta. Due guardie, perché la rotta è pubblica: un limite di richieste per indirizzo e un campo nascosto che una persona non vede — se arriva pieno l’ha riempito un robot, e la richiesta viene rifiutata dicendolo, non accettata a vuoto
POST /tts, POST /tts-streampassaggio storico: stesso corpo del motore, con chiave → l'iniezione nei prodotti è cambiare indirizzo e aggiungere l'intestazione
GET /healthstato del fronte e del motore: model_loaded, caldo, coda, trim_attacco, normalizza, formati, testi_lunghi, alias_modelli, politiche

Motore — rotte interne

Solo rete interna WireGuard, nessuna autenticazione. JSON in ingresso, audio in uscita, una sintesi per volta. Il corpo delle richieste è lo stesso del passaggio storico del fronte.

Metodo e percorsoCosa fa
GET /healthstato del motore: versione, model_loaded, warm, VRAM libera, trim_attacco, voci caricate, contatori (requests, synth_total_s, identity_retries, …)
GET /voicesle voci con riferimento di testo, lessico, ritmo (atempo_target_wpm) e parametri per voce
POST /ttssintesi in blocco: text (obbligatorio, ≤ 2000 caratteri), voice (default patrizia), format = wav | wav16k | pcm16k | mp3 | mp3_16k, apply_lexicon, atempo, trim_attacco. Risposta: corpo audio + intestazione X-AgileTTS-Meta
POST /tts-streamstreaming a pezzi: stessi campi, format = pcm24k (default) | pcm16k; corpo PCM s16le mono a pezzi finché finisce
POST /warm, POST /admin/unloadcarica il modello (/warm) o lo scarica (/admin/unload, solo regia); in produzione WARM lo ricarica alla prossima richiesta

Formati audio

Errori

HTTPcodicesignificato
401KEY_MISSING, KEY_INVALIDchiave assente o sconosciuta
403KEY_REVOKED, KEY_ROTATED, KEY_EXPIRED, SCOPE_NOT_ALLOWED, PLAN_EXCEEDEDchiave revocata, ruotata (grazia finita), scaduta (chiave di prova), senza il permesso richiesto, oppure chiave che da sola prometterebbe più del piano dell'azienda
404UNKNOWN_VOICE, UNKNOWN_MODEL, TENANT_NOT_FOUNDvoce o modello non esistenti, oppure (solo per chi amministra tutto il servizio) un piano scritto su un'azienda che non esiste
400BAD_REQUEST, UNSUPPORTED_FORMATcorpo non valido o formato non supportato
409ALREADY_EXISTS, SELF_SUSPEND_FORBIDDEN, LAST_ADMINsolo sulla console per azienda: persona gia' invitata, amministratore che sospende se' stesso, oppure operazione che lascerebbe l'azienda senza amministratori attivi
413TEXT_TOO_LONGoltre 4096 caratteri sul fronte (oltre 2000 sul motore)
429RATE_LIMIT, QUOTA_EXCEEDED, TENANT_RATE_LIMIT, TENANT_QUOTA_EXCEEDEDtroppe richieste al minuto o quota mensile esaurita: della singola chiave, oppure dell'intera azienda sulla somma delle sue chiavi (con Retry-After)
503BUSY, ENGINE_DOWN, SHUTTING_DOWNmotore occupato (con Retry-After), guardia VRAM: non disponibile, oppure il fronte si sta fermando per un ridispiego (con Retry-After: chi sta già parlando finisce, chi arriva riprova fra pochi secondi)
502ENGINE_ERRORerrore interno del motore
500synth_failed, stream_failedsintesi fallita (blocco) o stream fallito (solo prima dei primi byte)
501pcm16k_unavailable, streaming_not_availableformato o streaming non disponibile sul motore

Gli errori del fronte sono in forma OpenAI {"error":{"code","message","type"}}. Sui 5xx il fronte non ripiega mai da sé: dice al cliente cosa può fare (X-AgileTTS-Ripiego e error.ripiego), la decisione resta a chi chiama.

Intestazioni di risposta

Esempio

curl https://api.agiletts.agile.software/v1/audio/speech \
  -H "Authorization: Bearer $AGILETTS_KEY" -H "Content-Type: application/json" \
  -d '{"model":"agiletts","input":"Buongiorno, come posso aiutarla?","voice":"serena","response_format":"wav"}' \
  -o saluto.wav

Con il client OpenAI ufficiale basta cambiare base_url e la chiave: model: "agiletts" (o "tts-1") e response_format: "opus" funzionano senza modifiche al codice.

Client per i prodotti

Oltre al client OpenAI ufficiale (che parla col fronte senza modifiche), il repo porta un client di esempio in pura libreria standard, client/agiletts_client.py. Per chi preferisce la riga di comando c'è client/agiletts-cli.py, il client a riga di comando in pura libreria standard che avvolge client/agiletts_client.py: sintesi a blocco o in streaming su file (-o), models/model/voices/usage, chiave da --api-key o AGILETTS_API_KEY, errori su stderr con esito non-zero. La guida passo passo per spostare un prodotto sul fronte — URL, chiave, formato, timeout, 503 BUSY e ripiego, il «vai» e il rollback uno per volta — è in docs/ADOZIONE.md.