Skip to main content
Custom dictionaries let you control how the TTS pipeline pronounces specific words. Each dictionary is scoped to a project and contains word → 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 picks up your changes immediately.
Authentication uses your project-scoped API key. Master-key callers must supply ?project_id=<id> on every request because the master key is not pinned to a project.

List Dictionaries

Return every dictionary in the caller’s project.
GET

Query Parameters

project_id
integer
Required for master-key callers; rejected for project-scoped keys whose value disagrees with the key’s project.

Response

{
  "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"
from kugelaudio import KugelAudio

client = KugelAudio(api_key="YOUR_API_KEY")
for d in client.dictionaries.list():
    print(f"{d.id}: {d.name} ({d.language})")
import { KugelAudio } from 'kugelaudio';

const client = new KugelAudio({ apiKey: 'YOUR_API_KEY' });
const dictionaries = await client.dictionaries.list();
for (const d of dictionaries) {
  console.log(`${d.id}: ${d.name} (${d.language})`);
}
KugelAudio client = KugelAudio.fromEnv();
for (Dictionary d : client.dictionaries().list()) {
    System.out.println(d.getId() + ": " + d.getName());
}

Create Dictionary

POST

Body

name
string
required
Display name. Must be unique within the project.
description
string
Free-form description.
language
string
BCP-47 language tag (en, de-DE, …). Omit 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"}'
d = client.dictionaries.create(name="Brand names", language="en")
print(d.id)
const d = await client.dictionaries.create({ name: 'Brand names', language: 'en' });
console.log(d.id);
Dictionary d = client.dictionaries().create("Brand names", null, "en");
System.out.println(d.getId());

Get Dictionary

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

Update Dictionary

PATCH
Only the provided fields are changed.

Body

name
string
description
string
language
string
is_active
boolean
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}'
client.dictionaries.update(1, is_active=False)
await client.dictionaries.update(1, { isActive: false });
client.dictionaries().update(1, null, null, null, false);

Delete Dictionary

DELETE
Deletes the dictionary and all its entries.

Response

{ "deleted": true }

List Entries

GET

Query Parameters

Case-insensitive substring filter on word.
limit
integer
default:"100"
Page size, 1-500.
offset
integer
default:"0"
Pagination offset.

Response

{
  "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

Body

word
string
required
Word to match (≤ 200 chars).
replacement
string
required
Text the engine pronounces instead (≤ 1000 chars).
ipa
string
Optional IPA transcription. Takes precedence over replacement when set.
case_sensitive
boolean
default:"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"}'
e = client.dictionaries.entries.add(
    dictionary_id=1,
    word="Postgres",
    replacement="post-gres",
)
const e = await client.dictionaries.entries.add(1, {
  word: 'Postgres',
  replacement: 'post-gres',
});
DictionaryEntry e = client.dictionaries().entries().add(
    1, new DictionaryEntryInput("Postgres", "post-gres"));

Bulk Replace Entries

PUT
Atomically replace every entry in the dictionary. Entries currently in the dictionary whose word is not in the supplied list are deleted. Idempotent — calling twice with the same payload converges to the same final state.

Body

entries
array
required
Array of { word, replacement, ipa?, case_sensitive? } items. Duplicate word values within the payload are rejected.

Response

{ "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"}
    ]
  }'
result = client.dictionaries.entries.replace_all(
    dictionary_id=1,
    entries=[
        {"word": "Postgres",   "replacement": "post-gres"},
        {"word": "Kubernetes", "replacement": "koo-ber-net-eez"},
    ],
)
print(result.upserted, result.deleted)
const result = await client.dictionaries.entries.replaceAll(1, [
  { word: 'Postgres',   replacement: 'post-gres' },
  { word: 'Kubernetes', replacement: 'koo-ber-net-eez' },
]);
console.log(result.upserted, result.deleted);
BulkReplaceResult result = client.dictionaries().entries().replaceAll(
    1,
    List.of(
        new DictionaryEntryInput("Postgres",   "post-gres"),
        new DictionaryEntryInput("Kubernetes", "koo-ber-net-eez")));

Update Entry

PATCH
Only non-null fields are sent.

Body

word
string
replacement
string
ipa
string
case_sensitive
boolean

Delete Entry

DELETE

Response

{ "deleted": true }

Error responses

StatusCodeMeaning
400VALIDATION_ERRORMissing or invalid field; e.g. master-key call without project_id.
401UNAUTHORIZEDAPI key missing, invalid, or not project-scoped.
403UNAUTHORIZEDAPI key is scoped to a different project than the target.
404NOT_FOUNDDictionary or entry does not exist.