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
| Method | Path | Summary |
|---|
POST | /tts/generate | Synthesize speech from text (multi-provider, streaming-capable) |
GET | /tts/options | available voices/formats/providers |
GET | /providers/capabilities | per-provider capability matrix |
GET | /tts/disclosure | Resolved AI-disclosure policy (the customer lever + its legal floor) |
GET | /voices | List the calling organization's voices (org-scoped) |
POST | /voices/clone | Voice clone (multipart audio upload; consent_ref + PII-guarded; never persists raw voice PII at rest) |
POST | /voices/erase | GDPR Art-17 erasure of ALL voices for the calling organization |
GET | /voices/providers | Provider list + health for voice cloning (org-scoped) |
GET | /voices/analytics | Usage analytics across all of the organization's voices |
POST | /voices/sync/{provider} | Sync voices from an upstream provider into the organization's registry |
GET | /voices/{id} | Get one voice (org-scoped ownership enforced) |
PUT | /voices/{id} | Replace a voice's mutable metadata |
PATCH | /voices/{id} | Partially update a voice's mutable metadata |
DELETE | /voices/{id} | Delete a voice (org-scoped ownership enforced) |
POST | /voices/{id}/preview | Synthesize a short preview clip for a voice |
GET | /voices/{id}/analytics | Usage analytics for a single voice |
GET | /experimental/optimized-handler | [EXPERIMENTAL — no compatibility promise] Whether the optimized synthesis handler is active |
GET | /health | liveness |
GET | /metrics | Prometheus text exposition |
Schemas
GenerateResult
| Field | Type | Description |
|---|
audio_base64 | string | |
format | string | |
provider_used | string | |
characters | integer | |
Voice
A registered (often cloned) voice, owned by an organization. Mirrors voice.Voice (services/tts/voice/storage.go).
| Field | Type | Description |
|---|
id | string | |
name | string | |
provider | string | elevenlabs / cartesia / … |
provider_voice_id | string | |
organization_id | string | product-tier tenant (I6) |
created_at | string | |
updated_at | string | |
created_by | string | |
sample_audio_url | string | |
description | string | |
language | string | |
gender | string | |
age | string | |
labels | object | |
is_active | boolean | |
quality_score | number | |
usage_count | integer | |
status | string | processing / ready / failed |
VoiceList
| Field | Type | Description |
|---|
voices | array | |
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.
| Field | Type | Description |
|---|
success | boolean | always true on 201; failures use the 4xx/5xx Error shape |
voice_id | string | the created voice's id — the value GET /voices/{id} takes as {id} |
provider_voice_id | string | the upstream provider's own id for the clone |
name | string | |
provider | string | elevenlabs / cartesia / … |
status | string | processing / ready / failed — a clone may still be processing upstream |
message | string | human-readable confirmation |
OptimizedHandlerStatus
[EXPERIMENTAL] Status of the optional optimized synthesis handler. GROUNDED in the /experimental/optimized-handler closure in main.go's buildRouter.
| Field | Type | Description |
|---|
enabled | boolean | the optimized handler was constructed at boot |
feature | string | constant "optimized_handler" |
status | string | constant "experimental" |
features | array | queue_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".
| Field | Type | Description |
|---|
mode | string | Effective state. forced = a jurisdiction requires it and the tenant cannot turn it off. |
required | boolean | A jurisdiction mandates the disclosure. |
locked | boolean | Mirrors required; the bit the UI binds the toggle's disabled state to. |
announce | boolean | A spoken disclosure is emitted for this session. |
reason | string | Why 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. |
jurisdiction | string | Binding jurisdiction id — eu_eea or in. Present only when required. |
jurisdiction_name | string | Human-readable jurisdiction, for the "because X" string. |
legal_basis | string | e.g. "EU AI Act Art. 50(1)" / "IT Rules r.3(3)". |
in_force | string | ISO date the obligation began applying. |
region | string | Normalized region that decided the outcome (e.g. "DE", "IN", "EU"). |
region_source | string | Which signal decided the region, so the UI can say "because this call is to Germany" vs "because your account is in the EU". |
prefixed_audio | boolean | The binding law specifically requires the disclosure to PREFIX the synthetic audio (India IT Rules r.3(3)). |
locale | string | BCP-47 language the disclosure is spoken in. |
script_available | boolean | False when no wording has been authored for locale and the default language was used instead — the coverage gap is surfaced, not hidden. |
scope | string | Which obligation this engine covers. Constant art_50_1_and_it_rules_3_3_spoken_disclosure — explicitly NOT Art. 50(2) provenance marking. |
announced | boolean | The disclosure has already been spoken on this session. Meaningful only for a live session_id lookup. |
Error
| Field | Type | Description |
|---|
error | string | |
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.