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.
Query Parameters
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
Body
Display name. Must be unique within the project.
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
Returns the dictionary record. Returns 403 Forbidden if the dictionary
belongs to a project your API key is not scoped to.
Update Dictionary
Only the provided fields are changed.
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}'
client.dictionaries.update(1, is_active=False)
await client.dictionaries.update(1, { isActive: false });
client.dictionaries().update(1, null, null, null, false);
Delete Dictionary
Deletes the dictionary and all its entries.
Response
List Entries
Query Parameters
Case-insensitive substring filter on word.
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
Body
Word to match (≤ 200 chars).
Text the engine pronounces instead (≤ 1000 chars).
Optional IPA transcription. Takes precedence over replacement when set.
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
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
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
Only non-null fields are sent.
Body
Delete Entry
Response
Error responses
| Status | Code | Meaning |
|---|
| 400 | VALIDATION_ERROR | Missing or invalid field; e.g. master-key call without project_id. |
| 401 | UNAUTHORIZED | API key missing, invalid, or not project-scoped. |
| 403 | UNAUTHORIZED | API key is scoped to a different project than the target. |
| 404 | NOT_FOUND | Dictionary or entry does not exist. |