Skip to main content

Text-to-speech

Sellable product

This capability is granted by an API key scoped to the `` product (product face). See the product reference below.

Text-to-speech as a shared capability, TWO surfaces. HTTP (below): POST /tts/generate (synthesize), GET /tts/options, GET /providers/capabilities, plus the organization-scoped voice-management lifecycle under /voices/* (clone — consent/PII-guarded; erase — GDPR Art-17; list/get/update/delete; preview; analytics; provider sync). Multi-provider (ElevenLabs/OpenAI/Gemini/Cartesia) selection + failover + circuit-breaker. STREAMING (x-streaming): consumes session:*:llm_tokens (dialog egress, llm_tokens_v1) → emits audio:response:* (audio_response_v1, base64-PCM16k + monotonic sequence) for the voice pipeline — adaptive-chunk streaming. Provider keys resolved server-side (Infisical), NEVER in body/DB/logs.

  • Group: Voice & AI
  • Contract: contracts/tts/v1/openapi.yaml
  • Public base: https://api.vagarylabs.com (the consolidated API gateway — one host, per-brand sibling api.<zone>)
  • Auth: a product API key (vgk_…) issued from the console — Authorization: Bearer vgk_…
  • Product face (customer-keyed):
    • https://api.vagarylabs.com/product/v1/tts/generate

Endpoints

MethodPathSummary
POST/tts/generateSynthesize speech from text (multi-provider, streaming-capable)
GET/tts/optionsavailable voices/formats/providers
GET/providers/capabilitiesper-provider capability matrix
GET/tts/disclosureResolved AI-disclosure policy (the customer lever + its legal floor)
GET/voicesList the calling organization's voices (org-scoped)
POST/voices/cloneVoice clone (multipart audio upload; consent_ref + PII-guarded; never persists raw voice PII at rest)
POST/voices/eraseGDPR Art-17 erasure of ALL voices for the calling organization
GET/voices/providersProvider list + health for voice cloning (org-scoped)
GET/voices/analyticsUsage analytics across all of the organization's voices
POST/voices/sync/&#123;provider&#125;Sync voices from an upstream provider into the organization's registry
GET/voices/&#123;id&#125;Get one voice (org-scoped ownership enforced)
PUT/voices/&#123;id&#125;Replace a voice's mutable metadata
PATCH/voices/&#123;id&#125;Partially update a voice's mutable metadata
DELETE/voices/&#123;id&#125;Delete a voice (org-scoped ownership enforced)
POST/voices/&#123;id&#125;/previewSynthesize a short preview clip for a voice
GET/voices/&#123;id&#125;/analyticsUsage analytics for a single voice
GET/experimental/optimized-handler[EXPERIMENTAL — no compatibility promise] Whether the optimized synthesis handler is active
GET/healthliveness
GET/metricsPrometheus text exposition

Schemas

GenerateResult

FieldTypeDescription
audio_base64string
formatstring
provider_usedstring
charactersinteger

Voice

A registered (often cloned) voice, owned by an organization. Mirrors voice.Voice (services/tts/voice/storage.go).

FieldTypeDescription
idstring
namestring
providerstringelevenlabs / cartesia / …
provider_voice_idstring
organization_idstringproduct-tier tenant (I6)
created_atstring
updated_atstring
created_bystring
sample_audio_urlstring
descriptionstring
languagestring
genderstring
agestring
labelsobject
is_activeboolean
quality_scorenumber
usage_countinteger
statusstringprocessing / ready / failed

VoiceList

FieldTypeDescription
voicesarray

CloneResult

The 201 body of POST /voices/clone. GROUNDED in voice_handlers.go::CloneVoice's response writer — it is NOT the Voice schema: the identifier field is voice_id, not id, and the record's remaining metadata (organization_id, timestamps, labels, quality_score, usage_count) is not echoed back. Re-read the voice with GET /voices/{id} when the full record is needed.

FieldTypeDescription
successbooleanalways true on 201; failures use the 4xx/5xx Error shape
voice_idstringthe created voice's id — the value GET /voices/{id} takes as {id}
provider_voice_idstringthe upstream provider's own id for the clone
namestring
providerstringelevenlabs / cartesia / …
statusstringprocessing / ready / failed — a clone may still be processing upstream
messagestringhuman-readable confirmation

OptimizedHandlerStatus

[EXPERIMENTAL] Status of the optional optimized synthesis handler. GROUNDED in the /experimental/optimized-handler closure in main.go's buildRouter.

FieldTypeDescription
enabledbooleanthe optimized handler was constructed at boot
featurestringconstant "optimized_handler"
statusstringconstant "experimental"
featuresarrayqueue_management / audio_buffering / crossfade_processing / session_continuity

DisclosurePolicy

Resolved AI-disclosure policy for a session (or a preview). Mirrors disclosure.DecisionView (services/tts/disclosure/view.go), which owns this shape so the API and the audio path cannot drift apart. UI contract: bind the toggle's VALUE to mode ("on"/"forced" = on), bind its DISABLED state to locked, and when locked render the explanation from jurisdiction_name + legal_basis (e.g. "Required in European Union / EEA — EU AI Act Art. 50(1)"). When not locked, reason distinguishes "your choice" from "on by default" from "on because we could not determine the region".

FieldTypeDescription
modestringEffective state. forced = a jurisdiction requires it and the tenant cannot turn it off.
requiredbooleanA jurisdiction mandates the disclosure.
lockedbooleanMirrors required; the bit the UI binds the toggle's disabled state to.
announcebooleanA spoken disclosure is emitted for this session.
reasonstringWhy mode resolved as it did. tenant_disabled is the ONLY reason that can accompany mode=off. region_unknown_conservative_default means no region signal resolved and disclosure was enabled anyway.
jurisdictionstringBinding jurisdiction id — eu_eea or in. Present only when required.
jurisdiction_namestringHuman-readable jurisdiction, for the "because X" string.
legal_basisstringe.g. "EU AI Act Art. 50(1)" / "IT Rules r.3(3)".
in_forcestringISO date the obligation began applying.
regionstringNormalized region that decided the outcome (e.g. "DE", "IN", "EU").
region_sourcestringWhich signal decided the region, so the UI can say "because this call is to Germany" vs "because your account is in the EU".
prefixed_audiobooleanThe binding law specifically requires the disclosure to PREFIX the synthetic audio (India IT Rules r.3(3)).
localestringBCP-47 language the disclosure is spoken in.
script_availablebooleanFalse when no wording has been authored for locale and the default language was used instead — the coverage gap is surfaced, not hidden.
scopestringWhich obligation this engine covers. Constant art_50_1_and_it_rules_3_3_spoken_disclosure — explicitly NOT Art. 50(2) provenance marking.
announcedbooleanThe disclosure has already been spoken on this session. Meaningful only for a live session_id lookup.

Error

FieldTypeDescription
errorstring

Generated by scripts/gen-capability-docs.py from contracts/tts/v1/openapi.yaml — the contract IS the source of truth; edit the contract, not this page.