VEXYL AI

Per-call settings reference

This chapter lists every value you can set for one call: speech provider, voice, model, recogniser, language, bot and realtime mode. It then shows how to set them on inbound calls, either with SIP headers or from an Asterisk dialplan. Outbound calls take the same keys in the request body; see Outbound SIP calls and per-call settings.

Anything you do not set for a call comes from .env.

The keys

Setting Metadata key (POST, outbound) Inbound SIP header Honoured by
Speech provider tts_provider X-TTS-Provider Every call
Voice tts_voice X-TTS-Voice sarvam, google, gemini, smallest, openrouter
Speech model tts_model X-TTS-Model gemini, openrouter
Recogniser stt_provider X-STT-Provider Every call
Language language_code X-Language Every call
Bot botId X-Bot-Id LiteBot brain, LiteBot Live, Gemini Live
Realtime mode realtimeProvider X-Realtime-Provider Every call

Header names are not case-sensitive. A header value that fails the check is dropped with a warning (for example Invalid x-tts-provider "polly" in SIP call <id>, ignoring) and the .env default applies. The same checks run on the metadata POST and the outbound request, so a value accepted in one place is accepted in all.

tts_voice and tts_model are read only by the provider named in tts_provider for that call (or the .env provider when you set none). If the voice id does not belong to that provider, the provider rejects it and that sentence goes to the fallback provider. For the providers that do not take a per-call voice (elevenlabs, deepgram, azure, murf, vexyl), pick the voice per language in .env instead: ELEVENLABS_VOICE_<LANG>, DEEPGRAM_VOICE_<LANG>, AZURE_TTS_VOICE_<LANG>, MURF_VOICE_<LANG>, VEXYL_TTS_SPEAKER_<LANG>.

tts_provider

Value Per-call voice Per-call model Notes
sarvam yes no (model is SARVAM_TTS_MODEL) Indian languages; voice ids differ between bulbul:v3 and bulbul:v4-flash
google yes no Chirp3-HD voices; the voice name also sets the language
gemini yes yes Prebuilt voices or your own designed voice
smallest yes no Indian languages incl. Malayalam, native 8 kHz
openrouter yes yes Many vendors behind one key; voice ids depend on the model
elevenlabs no no Voice per language in .env
deepgram no no Voice per language in .env
azure no no Voice per language in .env
murf no no Voice per language in .env
vexyl no no Self-hosted; speaker per language in .env

Provider set-up and keys are in Text to speech.

tts_voice by provider

sarvam

The voice id depends on the model in SARVAM_TTS_MODEL:

  • bulbul:v3 (the default model) takes a bare speaker name, for example kavya (the default), shubh, aditya, ritu, amit.
  • bulbul:v4-flash takes a persona id <name>_<lang>_<style>, for example kavya_hi_conversational (the default), aparna_hi_kyc, shilpa_hi_narration.

The gateway converts between the two forms: a bare name on v4-flash becomes that speaker's persona, and a persona on v3 becomes its bare name, with one warning in the log. The language of the call comes from language_code, not from the persona tag, so a Hindi-tagged persona speaks Malayalam correctly on an ml-IN call. There is no Malayalam or Odia persona; use a _hi_ or _enhi_ one.

The bulbul:v4-flash personas built into this release (a name Sarvam adds later is passed through unchanged and Sarvam decides):

Assamese (`_as_`, 2):
  kangkana_as_conversational mouchumi_as_conversational

Bengali (`_bn_`, 3):
  arnab_bn_conversation roopa_bn_conversational bimal_bn_suspense

English (`_en_`, 62):
  aditi_en_stories aparna_en_companion aparna_en_edtech ashwin_en_sports
  ashwin_en_sports_energetic chandrika_en_stories dev_en_recovery dev_en_conversational
  deven_en_conversation ishita_en_customer ishita_en_medical ishita_en_numbers ishita_en_social
  ishita_en_stories kalpit_en_edtech nachiket_en_ads neha_en_customer neha_en_latenight
  nupur_en_kids ojas_en_social ritu_en_edtech ritu_en_latenight ritu_en_medical ritu_en_reels
  rohan_en_recovery roopa_en_conversational rustom_en_suspense sanchita_en_companion
  sanchita_en_insurance sanchita_en_recovery sanchita_en_market sanchita_en_social
  shabana_en_edtech shalini_en_companion shalini_en_customer shubh_en_narration shubh_en_numbers
  shubh_en_ads shubh_en_recovery shubh_en_audiobook shubh_en_narration_gentle shubh_en_sports
  simran_en_narration simran_en_automobile simran_en_conversation simran_en_customer
  simran_en_edtech simran_en_edtech_bot simran_en_sales simran_en_recovery simran_en_ads
  simran_en_therapist sunny_en_social varun_en_ads varun_en_suspense zarina_en_conversation
  amelia_en_conversational sophia_en_conversational girish_en_documentary girish_en_devotional
  payal_en_edtech sarang_en_narration

Hinglish (`_enhi_`, 13):
  ishita_enhi_companion ishita_enhi_customer ishita_enhi_customer_expressive
  sanchita_enhi_companion shalini_enhi_companion shalini_enhi_customer shubh_enhi_companion
  shubh_enhi_ads shubh_enhi_banking simran_enhi_companion simran_enhi_customer
  simran_enhi_banking_expressive sunny_enhi_customer

Gujarati (`_gu_`, 3):
  bhavik_gu_conversation pooja_gu_conversational pooja_gu_customer

Hindi (`_hi_`, 116):
  aayan_hi_conversational amit_hi_conversational ashutosh_hi_conversational
  kabir_hi_conversational kavya_hi_conversational manan_hi_conversational
  rahul_hi_conversational sumit_hi_conversational aditya_hi_conversational aditya_hi_sales
  anand_hi_documentary anand_hi_news aparna_hi_customer aparna_hi_kyc ashok_hi_character
  ashok_hi_news chhavi_hi_kids ishita_hi_ads ishita_hi_edtech ishita_hi_banking
  ishita_hi_ads_informal ishita_hi_devotional ishita_hi_numbers ishita_hi_social kunal_hi_kids
  mahesh_hi_documentary mani_hi_devotional mani_hi_conversational mohit_hi_conversational
  nachiket_hi_devotional priya_hi_recovery ratan_hi_latenight ratan_hi_customer_expressive
  ratan_hi_documentary ratan_hi_devotional ratan_hi_recovery ratan_hi_social ratan_hi_sports
  ratan_hi_latenight_warm rehan_hi_social ritu_hi_customer_utility ritu_hi_kids
  ritu_hi_conversation ritu_hi_customer ritu_hi_edtech ritu_hi_ads_formal ritu_hi_banking
  ritu_hi_ads_informal ritu_hi_insurance ritu_hi_edtech_bot ritu_hi_medical ritu_hi_sales
  ritu_hi_reels ritu_hi_social ritu_hi_customer_warm ritu_hi_social_lively roopa_hi_companion
  roopa_hi_narration roopa_hi_recovery roopa_hi_market roopa_hi_conversational
  sanchita_hi_assistant sanchita_hi_edtech sanchita_hi_banking sanchita_hi_feedback
  sanchita_hi_ads_formal sanchita_hi_ads_informal sanchita_hi_interview sanchita_hi_romantic
  sanchita_hi_market sanchita_hi_social sanchita_hi_kyc sarika_hi_conversation
  shalini_hi_companion shalini_hi_social shreya_hi_conversational shreya_hi_news
  shruti_hi_edtech shubh_hi_customer shubh_hi_ecomm shubh_hi_stories_mixed shubh_hi_devotional
  shubh_hi_ads shubh_hi_recovery shubh_hi_stories_dramatic simran_hi_assistant
  simran_hi_narration simran_hi_automobile simran_hi_conversation simran_hi_news_breaking
  simran_hi_social_energetic simran_hi_social_excited simran_hi_latenight simran_hi_news
  simran_hi_recovery simran_hi_sales suchitra_hi_ecomm suhani_hi_social sunny_hi_ads
  sunny_hi_reels tarun_hi_conversational tarun_hi_sales chaitra_hi_customer shilpa_hi_narration
  tanya_hi_narration aarti_hi_customer advait_hi_character aryaman_hi_ads chirag_hi_social
  girish_hi_devotional mukul_hi_ads mukul_hi_suspense suman_hi_companion vaibhav_hi_social
  vandana_hi_ecomm vipul_hi_social

Kannada (`_kn_`, 4):
  chaitra_kn_conversation chaitra_kn_narration chetan_kn_conversation suchitra_kn_narration

Marathi (`_mr_`, 9):
  ishita_mr_conversational mrunal_mr_narration neha_mr_narration nilesh_mr_conversation
  ritu_mr_insurance ritu_mr_narration rupali_mr_stories soham_mr_narration mukul_mr_stories

Punjabi (`_pa_`, 4):
  anand_pa_conversation anand_pa_customer harpreet_pa_narration jaspal_pa_banking

Tamil (`_ta_`, 4):
  gokul_ta_narration aravind_ta_ads aravind_ta_suspense vijay_ta_narration

Telugu (`_te_`, 4):
  kavitha_te_conversation kavitha_te_narration pooja_te_conversation tarun_te_narration

google

A Chirp3-HD voice: <locale>-Chirp3-HD-<Name>, for example ml-IN-Chirp3-HD-Despina. Keep Chirp3-HD voices: other types do not stream, so replies start later. The locale must match the call language, otherwise Google rejects the request. Three locales differ from the call language: zh-CN uses cmn-CN, ar-AE uses ar-XA, and or-IN has no Google voice at all (an Odia call on google fails over to another provider).

The 30 names:

Female Male
Achernar (default), Aoede, Autonoe, Callirrhoe, Despina, Erinome, Gacrux, Kore, Laomedeia, Leda, Pulcherrima, Sulafat, Vindemiatrix, Zephyr Achird, Algenib, Algieba, Alnilam, Charon, Enceladus, Fenrir, Iapetus, Orus, Puck, Rasalgethi, Sadachbia, Sadaltager, Schedar, Umbriel, Zubenelgenubi

gemini

One of the same 30 names, without a locale: Kore, Puck, Charon and so on. Or the id of a voice you designed or replicated, voice_<uuid>; such a call always goes through Vertex AI. See Custom voices.

smallest

A voice id from the Smallest voice library, for example avni (the default), or a cloned voice id voice_…. Most library voices speak all the Indian languages Smallest supports. Avoid divya: it cuts long sentences short.

openrouter

Voice ids belong to the model:

tts_model Example tts_voice
mistralai/voxtral-mini-tts-3-0 (the default) voice_3150
google/gemini-3.8-flash-tts, google/gemini-3.8-flash-lite-tts Kore, Puck (the 30 names above)
hexgrad/kokoro-82m, x-ai/grok-voice-tts-1.0, elevenlabs/eleven-flash-v2.5, deepgram/aura-2 That vendor's voice id

tts_model

Provider Values
gemini gemini-2.5-flash-tts, gemini-2.5-pro-tts, gemini-3.8-flash-tts, gemini-3.8-flash-lite-tts. The 3.8 models run on Vertex AI and stream. Unset means GEMINI_TTS_MODEL.
openrouter vendor/name, for example mistralai/voxtral-mini-tts-3-0 (default), google/gemini-3.8-flash-lite-tts, google/gemini-3.8-flash-tts, hexgrad/kokoro-82m, x-ai/grok-voice-tts-1.0, elevenlabs/eleven-flash-v2.5, deepgram/aura-2

A model id is letters, digits and _ . -, with one optional /. A voice id is up to 160 letters, digits and _ . : -. Anything else is dropped.

stt_provider

Value Mode Languages
sarvam Streaming The 11 Indian languages below
sarvam-r Batch The 11 Indian languages below
google Streaming All 22 languages below
vexyl-stt Streaming, self-hosted The 11 Indian languages below
deepgram Batch Deepgram's own language list
groq Batch All 22 languages below
gemini Batch All 22 languages below
openai Batch OpenAI's own language list

A per-call recogniser is used as given, even if it does not know the call language, so match the two. With no stt_provider and STT_PROVIDER=auto, Indian languages go to Sarvam and the rest to Groq. Models and tuning for each are in Speech recognition.

language_code

Indian (Sarvam, Google, Groq, Gemini, Vexyl) Other (Google, Groq, Gemini)
ml-IN Malayalam, en-IN English, hi-IN Hindi, ta-IN Tamil, te-IN Telugu, kn-IN Kannada, mr-IN Marathi, gu-IN Gujarati, or-IN Odia, pa-IN Punjabi, bn-IN Bengali en-US English (US), es-ES Spanish, fr-FR French, de-DE German, ar-XA Arabic, ar-AE Arabic (UAE), zh-CN Chinese (Mandarin), ja-JP Japanese, ko-KR Korean, ru-RU Russian, pt-BR Portuguese (Brazil)

The header form is the same code; en-us is accepted and stored as en-US.

botId and realtimeProvider

  • botId: the id of a bot. With the LiteBot brain it is the LiteBot bot id; an unknown id falls back to the default bot. In a header, an id is letters, digits and _ . -, up to 64 characters.
  • realtimeProvider: litebot or gemini-live for speech-to-speech, off or pipeline for the standard pipeline. See Realtime mode.

Inbound: set them with SIP headers

Any PBX that sends INVITEs to the gateway's SIP bridge can add these headers. Nothing else is needed: no HTTP request, no session id. On the gateway, the call log then shows:

INFO: SIP header overrides for <session-id>: {"tts_provider":"sarvam","tts_voice":"shilpa_hi_narration","language_code":"ml-IN","botId":"support"}

The dialled number is stored with the call as destination_number whether or not you send headers.

Asterisk or FreePBX (chan_pjsip)

Headers must be added in a pre-dial handler, so they land on the outgoing INVITE:

[to-vexyl-bot]
exten => _X.,1,NoOp(To the bot)
 same => n,Dial(PJSIP/${EXTEN}@vexyl-gw,60,b(vexyl-settings^s^1(support,ml-IN,sarvam,shilpa_hi_narration,sarvam)))
 same => n,Hangup()

[vexyl-settings]
; ARG1 bot, ARG2 language, ARG3 TTS provider, ARG4 voice, ARG5 recogniser
exten => s,1,Set(PJSIP_HEADER(add,X-Bot-Id)=${ARG1})
 same => n,Set(PJSIP_HEADER(add,X-Language)=${ARG2})
 same => n,Set(PJSIP_HEADER(add,X-TTS-Provider)=${ARG3})
 same => n,Set(PJSIP_HEADER(add,X-TTS-Voice)=${ARG4})
 same => n,Set(PJSIP_HEADER(add,X-STT-Provider)=${ARG5})
 same => n,Return()

Different settings per number:

[to-vexyl-bot]
exten => 5550101,1,Dial(PJSIP/${EXTEN}@vexyl-gw,60,b(vexyl-settings^s^1(sales,en-IN,google,en-IN-Chirp3-HD-Kore,google)))
exten => 5550102,1,Dial(PJSIP/${EXTEN}@vexyl-gw,60,b(vexyl-settings^s^1(support,hi-IN,sarvam,aparna_hi_kyc,sarvam)))

On the older chan_sip driver, use SIPAddHeader(X-Bot-Id: support) (one per header) before Dial() instead of the pre-dial handler.

For a model as well, add Set(PJSIP_HEADER(add,X-TTS-Model)=gemini-3.8-flash-lite-tts) with X-TTS-Provider set to gemini.

Other PBXs

Look for a trunk setting named custom SIP headers, extra headers or header manipulation, and add the headers there, for example X-Bot-Id: support and X-Language: ml-IN. A trunk-level header applies to every call on that trunk, so use one trunk per bot profile if the PBX cannot set headers per route. Vendor menus are in Connecting Panasonic, Avaya, Grandstream and Bitvoice PBXs.

Headers together with X-Session-UUID

If the INVITE also carries X-Session-UUID and you posted metadata under that id, the posted values win. A header only fills a key the POST did not set. Use one method per key to keep it simple.

Inbound: set them from the Asterisk dialplan (AudioSocket)

With AudioSocket there is no SIP leg to the gateway, so post the keys before AudioSocket():

[voice-assistant]
exten => _X.,1,Answer()
 same => n,Set(SESSION_UUID=${TRIM(${SHELL(uuidgen)})})
 same => n,Set(META=caller_id=${URIENCODE(${CALLERID(num)})}&channel=${URIENCODE(${CHANNEL})}&destination_number=${EXTEN}&botId=support&language_code=ml-IN&tts_provider=sarvam&tts_voice=shilpa_hi_narration&stt_provider=sarvam)
 same => n,Set(CURL_RESULT=${CURL(http://127.0.0.1:8081/session/${SESSION_UUID}/metadata,${META})})
 same => n,AudioSocket(${SESSION_UUID},127.0.0.1:8080)
 same => n,Hangup()

A caller-chosen language and per-number settings from the Asterisk database are shown in Dialplan: choose settings from the call.

Settings sent by an upstream PBX

When a customer PBX sends the headers above to your Asterisk, and Asterisk connects the call over AudioSocket, read the headers and post them:

[from-customer-pbx]
exten => _X.,1,Answer()
 same => n,Set(SESSION_UUID=${TRIM(${SHELL(uuidgen)})})
 same => n,Set(VX_BOT=${PJSIP_HEADER(read,X-Bot-Id)})
 same => n,Set(VX_LANG=${PJSIP_HEADER(read,X-Language)})
 same => n,Set(VX_TTS=${PJSIP_HEADER(read,X-TTS-Provider)})
 same => n,Set(VX_VOICE=${PJSIP_HEADER(read,X-TTS-Voice)})
 same => n,Set(VX_STT=${PJSIP_HEADER(read,X-STT-Provider)})
 same => n,Set(META=caller_id=${URIENCODE(${CALLERID(num)})}&channel=${URIENCODE(${CHANNEL})}&botId=${VX_BOT}&language_code=${VX_LANG}&tts_provider=${VX_TTS}&tts_voice=${URIENCODE(${VX_VOICE})}&stt_provider=${VX_STT})
 same => n,Set(CURL_RESULT=${CURL(http://127.0.0.1:8081/session/${SESSION_UUID}/metadata,${META})})
 same => n,AudioSocket(${SESSION_UUID},127.0.0.1:8080)
 same => n,Hangup()

An empty header gives an empty value, which the gateway ignores, so the .env default applies for that key.

Checking what a call used

  • GET /session/<uuid>/metadata shows the stored keys.
  • The metadata POST logs Session metadata stored for <uuid> with the language and any TTS: and STT: provider.
  • SIP header calls log SIP header overrides for <session-id> as above. A rejected value logs Invalid x-… in SIP call.
  • At call start the log shows Initializing STT provider: <provider> for language: <code>.

Next: Outbound SIP calls and per-call settings, or Text to speech for provider set-up.