Skip to content
KugelAudioDocs

Dictionaries API

REST reference for pronunciation dictionaries: list, create, update and delete dictionaries, add entries and bulk replace them from your own glossary.

Custom dictionaries let you control how the TTS pipeline pronounces specific words. Each dictionary is scoped to a project and contains word-to-replacement/IPA mappings that are applied before synthesis.

Use these endpoints to sync dictionaries from your own data sources (PIM, CMS, internal glossary). The TTS-side cache is invalidated after every mutation, so the next synthesis request that uses the dictionary picks up your changes immediately. Authentication uses your project-scoped API key.

Use a dictionary in synthesis

With a project API key, synthesis uses the dictionaries of the key's own project when the request carries no project_id. To use another project's dictionaries, or when you authenticate without a project key, send the project_id from the dictionary response on /v1/tts/generate or in the WebSocket config, optionally with dictionary_ids to pick specific dictionaries:

JSON
{
  "text": "Welcome to KugelAudio.",
  "voice_id": 1071,
  "language": "en",
  "project_id": 42,
  "dictionary_ids": [7]
}

The SDKs send the same field on every generation and session method: project_id in Python and projectId in Java and JavaScript. The LiveKit and Pipecat plugins take it as a constructor option (project_id in Python, projectId in JavaScript).

Common Request Fields

FieldLocationTypeApplies toDescription
project_idqueryintegerEvery endpoint on this pageOptional; if supplied, it must match the key's project.
dict_idpathintegerSingle-dictionary and entry endpointsDictionary ID returned by list or create.
entry_idpathintegerUpdate/delete entryEntry ID returned by list or add.

The project_id query parameter is omitted from the examples because ordinary Dashboard API keys are already project-scoped.

Response Objects

Dictionary responses contain:

FieldTypeDescription
idintegerDictionary ID
project_idintegerOwning project ID
namestringDisplay name
descriptionstring | nullOptional description
languagestring | nullOptional BCP-47 language filter
is_activebooleanWhether the dictionary applies by default
created_atstringCreation timestamp
updated_atstringLast-update timestamp

Entry responses contain:

FieldTypeDescription
idintegerEntry ID
dictionary_idintegerParent dictionary ID
wordstringSource text to match
replacementstringReplacement text sent to synthesis
ipastring | nullOptional IPA transcription
case_sensitivebooleanWhether matching preserves case
created_atstringCreation timestamp
updated_atstringLast-update timestamp

Wrapper responses contain:

OperationFields
List dictionariesdictionaries (array of dictionary objects)
List entriesentries (array), total (integer before pagination), limit (integer), offset (integer)
Bulk replaceupserted (integer), deleted (integer), total (integer; resulting payload size)
Delete dictionary/entrydeleted (boolean, always true on a successful response)

List Dictionaries

Return every dictionary in the caller's project.

GET/v1/dictionaries

Query Parameters

project_id
integer
query

Optional; rejected when it disagrees with the key's project.

Response

JSON
{
  "dictionaries": [
    {
      "id": 1,
      "project_id": 42,
      "name": "Brand names",
      "description": "Customer product names",
      "language": "en",
      "is_active": true,
      "created_at": "2026-05-20T09:00:00+00:00",
      "updated_at": "2026-05-20T09:05:00+00:00"
    }
  ]
}

Example

curl -X GET "https://api.kugelaudio.com/v1/dictionaries" \
  -H "Authorization: Bearer YOUR_API_KEY"

Create Dictionary

POST/v1/dictionaries

Body

name
string
body
required

Display name (1-200 characters). Must be unique within the project; a duplicate answers 409.

description
string
body

Free-form description (maximum 2000 characters). Omit or send an empty string for no description.

language
string
body

BCP-47 language tag (en, de-DE, ...), maximum 16 characters. Omit or send an empty string for all languages.

Example

curl -X POST "https://api.kugelaudio.com/v1/dictionaries" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Brand names", "language": "en"}'

Get Dictionary

GET/v1/dictionaries/{dict_id}

Returns the dictionary record. Returns 403 Forbidden if the dictionary belongs to a project your API key is not scoped to.


Update Dictionary

PATCH/v1/dictionaries/{dict_id}

Only the provided fields are changed.

Body

name
string
body

New name (1-200 characters).

description
string
body

New description (maximum 2000 characters). Send an empty string to clear it; null leaves it unchanged.

language
string
body

New language tag (maximum 16 characters). Send an empty string to clear it; null leaves it unchanged.

is_active
boolean
body

Disable a dictionary without deleting it.

Example

curl -X PATCH "https://api.kugelaudio.com/v1/dictionaries/1" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}'

Delete Dictionary

DELETE/v1/dictionaries/{dict_id}

Deletes the dictionary and all its entries.

Response

JSON
{ "deleted": true }

List Entries

GET/v1/dictionaries/{dict_id}/entries

Query Parameters

search
string
query

Case-insensitive substring filter on word.

limit
integer
querydefault: 100

Page size, 1-500.

offset
integer
querydefault: 0

Pagination offset.

Response

JSON
{
  "entries": [
    {
      "id": 11,
      "dictionary_id": 1,
      "word": "Postgres",
      "replacement": "post-gres",
      "ipa": null,
      "case_sensitive": false,
      "created_at": "2026-05-20T09:00:00+00:00",
      "updated_at": "2026-05-20T09:00:00+00:00"
    }
  ],
  "total": 12,
  "limit": 100,
  "offset": 0
}

Add Entry

POST/v1/dictionaries/{dict_id}/entries

Body

word
string
body
required

Word to match (≤ 200 chars). Must be unique within the dictionary; a duplicate answers 409.

replacement
string
body
required

Text the engine pronounces instead (≤ 1000 chars).

ipa
string
body

Optional IPA transcription (maximum 200 characters). Takes precedence over replacement when set.

case_sensitive
boolean
bodydefault: false

Match the original case exactly.

Example

curl -X POST "https://api.kugelaudio.com/v1/dictionaries/1/entries" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"word": "Postgres", "replacement": "post-gres"}'

Bulk Replace Entries

PUT/v1/dictionaries/{dict_id}/entries

Replace every entry in the dictionary. Entries currently in the dictionary whose word is not in the supplied list are deleted. The operation is idempotent — calling twice with the same payload converges to the same final state.

Body

entries
array
body
required

Array of { word, replacement, ipa?, case_sensitive? } items. word and replacement are required on every item; their limits and case_sensitive: false default match Add Entry. Duplicate word values within the payload are rejected.

Response

JSON
{ "upserted": 25, "deleted": 3, "total": 25 }

Example

curl -X PUT "https://api.kugelaudio.com/v1/dictionaries/1/entries" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      {"word": "Postgres",   "replacement": "post-gres"},
      {"word": "Kubernetes", "replacement": "koo-ber-net-eez"}
    ]
  }'

Update Entry

PATCH/v1/dictionaries/{dict_id}/entries/{entry_id}

Only non-null fields are sent.

Body

word
string
body

New word (1-200 characters).

replacement
string
body

New replacement (1-1000 characters).

ipa
string
body

New IPA transcription (maximum 200 characters). Send an empty string to clear it; null leaves it unchanged.

case_sensitive
boolean
body

Delete Entry

DELETE/v1/dictionaries/{dict_id}/entries/{entry_id}

Response

JSON
{ "deleted": true }

Deletion is idempotent after the parent dictionary has been authorized: a missing entry_id also returns { "deleted": true }.


Error responses

StatusCodeMeaning
400VALIDATION_ERRORMissing or invalid field.
401UNAUTHORIZEDAPI key missing, invalid, or not project-scoped.
403UNAUTHORIZEDAPI key is scoped to a different project than the target.
404NOT_FOUNDDictionary does not exist, or an entry requested for update does not exist.
409VALIDATION_ERRORThe name is already used by another dictionary in the project, or the word by another entry in the dictionary. Choose a different one, or edit the existing one.
503INTERNAL_ERRORDictionary management is unavailable on this deployment.