Skip to main content

Providers

Providers are the AI services that power each stage of the voice pipeline. VOCALS supports swappable providers for three types: STT (speech-to-text), LLM (language model), and TTS (text-to-speech).

Each provider configuration stores an encrypted API key, the selected model, and optional provider-specific settings. Agents reference provider IDs to define their pipeline.

Available Providers

TypeNameDescription
sttdeepgramDeepgram Nova real-time STT
sttopenaiOpenAI Whisper API STT
sttwhisperOpenAI Whisper (local) STT
sttelevenlabsElevenLabs STT
sttqwenAlibaba Qwen STT
sttfishFish Audio STT
llmopenaiOpenAI GPT models
llmclaudeAnthropic Claude models
llmgoogleGoogle Gemini models
llmkimiMoonshot Kimi models
ttsdeepgramDeepgram TTS
ttsopenaiOpenAI TTS
ttselevenlabsElevenLabs TTS
ttsqwenAlibaba Qwen TTS
ttsresembleResemble AI TTS
ttsfishFish Audio TTS
ttsgoogleGoogle Cloud TTS (Neural2/WaveNet, Chirp3-HD, Studio)
ttsgoogle_geminiGoogle Gemini TTS (Gemini API)
google and google_gemini are different providers

They target different Google APIs and are not interchangeable. google calls Cloud Text-to-Speech and takes a Cloud Text-to-Speech API key; google_gemini calls the Gemini API and takes a Gemini API key, which is billed separately. A key for one is rejected by the other. See Provider Integration for the full comparison.

Google Gemini TTS models

google_gemini accepts one of three model_id values:

model_idNotes
gemini-2.5-flash-preview-ttsDefault. Low latency, cost-efficient - the model to use for telephony.
gemini-2.5-pro-preview-ttsHigher quality, aimed at podcast/audiobook output.
gemini-3.1-flash-tts-previewTuned for expressive narration.

Reproduce these ids exactly. The 2.5 models end -preview-tts while the 3.1 model ends -tts-preview; the inconsistency is Google's, and a "corrected" id returns 404 at synthesis time. All three are Preview models and Google may change the ids when they reach stable.

The voice is set on the agent as a bare prebuilt name (for example Zephyr), not a fully-qualified locale-prefixed id, and google_gemini takes no language parameter - the spoken language follows the agent's text.

List Providers

GET /providers

Returns all provider configurations for the current tenant, ordered by type and name.

Response

[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "stt",
"name": "deepgram",
"model_id": "nova-2",
"extra_config": null,
"is_active": true,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-02-20T14:00:00Z"
}
]

Response Fields

FieldTypeDescription
iduuidProvider config ID
typestringstt, llm, or tts
namestringProvider name (e.g. deepgram, openai)
model_idstringSelected model identifier
extra_configobject | nullProvider-specific configuration
is_activebooleanWhether the provider is active
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

The API key is never returned in responses.

Get Provider

GET /providers/{provider_id}

Returns a single provider configuration.

Response

Same schema as the list item above.

Create Provider

POST /providers

Request Body

{
"type": "stt",
"name": "deepgram",
"api_key": "dg_live_abc123...",
"model_id": "nova-2",
"extra_config": { "language": "en" },
"is_active": true
}

Request Fields

FieldTypeRequiredDefaultDescription
typestringYes--Provider type: stt, llm, or tts
namestringYes--Provider name (must be a registered provider)
api_keystringYes--Provider API key (encrypted before storage)
model_idstringYes--Model identifier to use
extra_configobjectNonullProvider-specific settings
is_activebooleanNotrueWhether the provider is active

Response

201 Created -- Returns the ProviderResponse object.

If the type is not stt, llm, or tts, the API returns 400. If the name is not a registered provider for the given type, the API returns 400 with the list of available providers.

Google Gemini default: thinking_budget

Creating an LLM provider with name: "google" and no thinking_budget in extra_config stores "extra_config": { "thinking_budget": 0 }, which asks for the least extended reasoning the selected model allows: off entirely on older Gemini models, and the lowest reasoning level on Google's current generation, which cannot switch it off. Reasoning adds seconds before the first word is spoken, and a caller hears that as silence. Send your own thinking_budget in extra_config to keep reasoning on (for example { "thinking_budget": 1024 }); the value you send is always stored as-is.

This applies to newly created providers only. Existing providers are never modified, and PUT /providers/{provider_id} never adds the field.

Update Provider

PUT /providers/{provider_id}

Update a provider's API key, model, configuration, or active status. Only include the fields you want to change.

Request Body

{
"api_key": "new_key_here",
"model_id": "nova-2-general",
"is_active": true
}

Request Fields

FieldTypeRequiredDescription
api_keystringNoNew API key (re-encrypted)
model_idstringNoNew model identifier
extra_configobjectNoUpdated provider-specific settings
is_activebooleanNoEnable or disable the provider

Response

200 OK -- Returns the updated ProviderResponse.

Delete Provider

DELETE /providers/{provider_id}

Permanently deletes a provider configuration.

The delete is refused while any agent still references the provider, so no agent is ever detached as a side effect. Repoint or delete those agents first, then retry.

Response

204 No Content -- the provider was deleted.

409 Conflict -- one or more agents still use this provider. The body names them:

{
"detail": {
"error_code": "provider_in_use",
"message": "This provider is still used by Demo Restaurant, Reception Bot. Point those agents elsewhere or delete them, then try again.",
"agents": [
{ "id": "550e8400-e29b-41d4-a716-446655440000", "name": "Demo Restaurant" },
{ "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "name": "Reception Bot" }
]
}
}
FieldTypeDescription
error_codestringprovider_in_use
messagestringHuman-readable summary, naming up to five agents
agentsarray[object]Every agent blocking the delete, as id + name

403 Forbidden -- the provider is managed by VOCALS and cannot be deleted here.

List Available Models

GET /providers/{provider_id}/models

Fetches the list of available models for a saved provider using its stored API key. Queries the provider's API when possible, or returns a hardcoded list for providers that do not support model enumeration.

Response

{
"models": ["nova-2", "nova-2-general", "nova-2-meeting"],
"source": "api"
}
FieldTypeDescription
modelsarray[string]Available model identifiers
sourcestringapi (fetched from provider), hardcoded (static list), or error (fetch failed)

For google_gemini this returns the three Gemini TTS model ids with the default first and source: "hardcoded" - the list is static, so no call is made to Google.

List Google Voices

GET /providers/{provider_id}/google-voices

Returns the structured Google Cloud TTS voice catalogue for a saved Google TTS provider, used to populate the agent voice picker. The Neural2/WaveNet/Chirp3-HD voices are fetched live from the tenant's own Google Cloud project (scoped to the stored API key). The provider must be a tts provider named google owned by the current tenant.

Response

{
"voices": [
{
"id": "en-US-Chirp3-HD-Charon",
"label": "Charon (Female)",
"tier": "chirp",
"language": "en-US",
"gender": "Female"
}
],
"source": "live"
}

Response Fields

FieldTypeDescription
voicesarray[object]Voice entries for the picker
voices[].idstringFully-qualified voice identifier to store as the agent's voice_id
voices[].labelstringHuman-readable label for display
voices[].tierstringneural2, wavenet, or chirp
voices[].languagestringBCP-47 locale (one of the seven supported: en-US, en-GB, es-ES, fr-FR, de-DE, pt-BR, it-IT)
voices[].genderstringMale, Female, Neutral, or empty when unspecified
sourcestringlive (fetched from Google), cache (served from a recent fetch), or error (live fetch failed; voices is empty and the caller should fall back to its own static list)

A source of error is not an HTTP failure - the endpoint still returns 200 with an empty voices array so the picker can degrade gracefully to its own static list. Studio voices are never returned (they are no longer selectable for new voices).

Returns 404 if the provider is not found for the tenant or is not a Google Cloud TTS provider. This endpoint is Cloud-TTS-only: a google_gemini provider also returns 404, because its 30 prebuilt voices are a fixed, model-defined list with no per-tenant catalogue to fetch.

Fetch Models (Ad-hoc)

POST /providers/models

Fetch available models without saving a provider configuration. Useful for testing an API key before creating a provider.

Request Body

{
"type": "stt",
"name": "deepgram",
"api_key": "dg_live_abc123..."
}

Response

Same ProviderModelsResponse schema as above.

Test Provider

POST /providers/{provider_id}/test

Validates that a saved provider's API key and configuration are working correctly. Calls the provider's validate() method.

Response

{
"success": true,
"message": "Provider deepgram (stt) is working correctly."
}
FieldTypeDescription
successbooleanWhether validation passed
messagestringHuman-readable result message

Returns 400 if the API key is invalid, or a response with success: false if validation fails for other reasons.