Skip to main content
Speech recognition mishears words it has never seen: Powai comes out as Huawei, IRDAI as I read AI. Text-to-speech mispronounces brand names. A domain dictionary fixes both for every agent that uses it. You write a dictionary once per domain (real_estate, banking, your company name) and tag any number of agents with it.

The three sections

Every row is a key → value pair, and both word sections read the same way: wrong → right. A vocabulary row with an empty value simply boosts that term - use it for words the recognizer should expect but has no single typical mistake for.

Step 1 - Write the dictionary

step1_dictionary.py
Output
resolved_key = None means this is a custom domain with only your rows (see Bundled dictionaries). Both halves of every vocabulary row are sent to the recognizer as keyterms, correct terms first. The list is capped on the server (50 terms for Deepgram), so keep the dictionary focused on words that actually go wrong.
upsert() replaces your rows for that domain - it does not merge. To add one term, read the dictionary, edit the list, and send the whole list back (see Add a term later).

Step 2 - Tag the agent

A dictionary does nothing until an agent is tagged with it. The tag on the agent is what the call reads:
step2_tag.py
Output
You can also tag at creation time: client.agents.voice.create(..., domain="acme-homes"). An agent has one dictionary. Tagging it with a new domain moves it out of the old one. To remove the tag:
The next call uses the dictionary - no redeploy.

Bundled dictionaries

Three domains ship with ready-made rows: banking, real_estate, hospital. Tag an agent with one and it works with no rows of your own:
Rows you upsert() under that name are layered on top of the bundled rows; yours win when the same key appears in both. get() shows the merged result in vocabulary, and the bundled half on its own in seed_vocabulary. resolved_key tells you whether a name maps to a bundled dictionary: None means it is a custom domain with only your rows.
Domain names are matched loosely: a name that contains words such as realty, property, bank, loan, insurance, health or clinic maps to the matching bundled dictionary. acme-realty gets the real_estate rows underneath yours. Check resolved_key after your first upsert() if you want a dictionary with only your own rows.
To start a custom domain from a bundled one:

Add a term later

Send every section back - a section you leave out is cleared.

Method reference

Each section accepts a dict ({"key": "value"}) or a list of {"key": ..., "value": ...} rows - the shape get() returns.

Troubleshooting

  • No change on calls - the agent is not tagged. Check (await client.agents.get("my-support")).voices[0].domain.
  • Some terms are still misheard - the keyterm list may be over the cap. Remove rows that are not causing problems.
  • Rows I never wrote show up in get() - the domain name matched a bundled dictionary. Check resolved_key.
  • A term I added disappeared - upsert() replaced the domain with a list that did not include it. Read, edit, write back.

Next steps

Configure an agent

Voice, number, background sound, noise cancellation.

Voice profiles

Pick the STT and TTS stack the dictionary feeds.