> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kugelaudio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dictionaries in the SDKs

> SDK reference for pronunciation dictionaries in Python, TypeScript, Java, Rust, C++ and Dart: create them, manage entries, bulk sync, and apply at synthesis.

Custom dictionaries are per-project pronunciation and replacement lists.
Each entry maps a written word to the text or IPA the TTS pipeline should
pronounce instead. Use them for brand names, product names, acronyms, and
domain vocabulary that should sound consistent across requests. The concept is
covered in [Dictionaries](/features/dictionaries).

Generating speech with a key of the dictionary's project applies it without
a project ID; see [Dictionaries at synthesis](#dictionaries-at-synthesis).
Changes are available to synthesis after the dictionary mutation finishes.

For the full HTTP contract, field limits, master-key `project_id` rules,
and error codes, see the [Dictionaries API reference](/api-reference/endpoints/dictionaries).

## Create a dictionary

<CodeGroup>

```python Python
import os

from kugelaudio import KugelAudio

client = KugelAudio(api_key=os.environ["KUGELAUDIO_API_KEY"])

dictionary = client.dictionaries.create(
    name="Brand names",
    description="Product and company names",
    language="en",
)

print(dictionary.id)
```

```typescript TypeScript
import { KugelAudio } from 'kugelaudio';

const client = new KugelAudio({ apiKey: process.env.KUGELAUDIO_API_KEY! });

const dictionary = await client.dictionaries.create({
  name: 'Brand names',
  description: 'Product and company names',
  language: 'en',
});

console.log(dictionary.id);
```

```java Java
import com.kugelaudio.sdk.Dictionary;
import com.kugelaudio.sdk.KugelAudio;

KugelAudio client = KugelAudio.fromEnv();

Dictionary dictionary = client.dictionaries().create(
    "Brand names",
    "Product and company names",
    "en"
);

System.out.println(dictionary.getId());
```

```rust Rust
use kugelaudio::{DictionaryCreate, KugelAudio};

let client = KugelAudio::new(std::env::var("KUGELAUDIO_API_KEY").unwrap_or_default())?;

let dictionary = client
    .dictionaries()
    .create(
        &DictionaryCreate::new("Brand names")
            .description("Product and company names")
            .language("en"),
        None, // project_id
    )
    .await?;

println!("{}", dictionary.id);
```

```cpp C++
kugelaudio::Client client{std::getenv("KUGELAUDIO_API_KEY")};

kugelaudio::DictionaryCreate request;
request.name = "Brand names";
request.description = "Product and company names";
request.language = "en";
kugelaudio::Dictionary dictionary = client.dictionaries().create(request);

std::cout << dictionary.id << '\n';
```

```dart Dart
import 'dart:io';

import 'package:kugelaudio/kugelaudio.dart';

final client = KugelAudio(apiKey: Platform.environment['KUGELAUDIO_API_KEY']!);

final dictionary = await client.dictionaries.create(
  name: 'Brand names',
  description: 'Product and company names',
  language: 'en',
);

print(dictionary.id);
```

</CodeGroup>

## Manage dictionaries

<CodeGroup>

```python Python
# List every dictionary in the project
dictionaries = client.dictionaries.list()

# Fetch one
dictionary = client.dictionaries.get(dictionary.id)

# Update name / description / language / is_active (only provided fields change)
dictionary = client.dictionaries.update(
    dictionary.id,
    description="Updated description",
)
```

```typescript TypeScript
// List every dictionary in the project
const dictionaries = await client.dictionaries.list();

// Fetch one
const dict = await client.dictionaries.get(dictionary.id);

// Update name / description / language / isActive (only provided fields change)
await client.dictionaries.update(dictionary.id, {
  description: 'Updated description',
});
```

```java Java
// List every dictionary in the project
List<Dictionary> dictionaries = client.dictionaries().list();

// Fetch one
Dictionary dict = client.dictionaries().get(dictionary.getId());

// Update name / description / language / isActive (null leaves a field unchanged)
client.dictionaries().update(
    dictionary.getId(),
    null,                      // name
    "Updated description",     // description
    null,                      // language
    false                      // isActive
);
```

```rust Rust
use kugelaudio::DictionaryUpdate;

// List every dictionary in the project
let dictionaries = client.dictionaries().list(None).await?;

// Fetch one
let dict = client.dictionaries().get(dictionary.id, None).await?;

// Update name / description / language / is_active (only set fields change)
let dict = client
    .dictionaries()
    .update(
        dictionary.id,
        &DictionaryUpdate::new().description("Updated description"),
        None,
    )
    .await?;
```

```cpp C++
// List every dictionary in the project
std::vector<kugelaudio::Dictionary> dictionaries = client.dictionaries().list();

// Fetch one
kugelaudio::Dictionary dict = client.dictionaries().get(dictionary.id);

// Update name / description / language / is_active (only set fields change)
kugelaudio::DictionaryUpdate update;
update.description = "Updated description";
dict = client.dictionaries().update(dictionary.id, update);
```

```dart Dart
// List every dictionary in the project
final dictionaries = await client.dictionaries.list();

// Fetch one
final dict = await client.dictionaries.get(dictionary.id);

// Update name / description / language / isActive (only given fields change)
await client.dictionaries.update(
  dictionary.id,
  description: 'Updated description',
);
```

</CodeGroup>

<Note>
  Every dictionary and entry method also takes a project ID: a keyword-only
  `project_id` in Python, an optional final `{ projectId }` argument in TypeScript,
  an overload with a final `Long projectId` in Java, a final `Option<i64>` argument
  in Rust (`EntryListOptions::project_id` for listing entries), a final
  `std::optional<std::int64_t>` argument in C++, and a named `projectId` argument in
  Dart. It is required **only** for master-key callers acting on a specific project;
  with a normal project-scoped API key, omit it.
</Note>

## Add and manage entries

Use `replacement` for normal spelling-based fixes. Use `ipa` when you need
an exact phonetic pronunciation; IPA takes precedence over `replacement`.

<CodeGroup>

```python Python
created_entry = client.dictionaries.entries.add(
    dictionary.id,
    word="Postgres",
    replacement="post-gres",
)

page = client.dictionaries.entries.list(
    dictionary.id,
    search="post",
    limit=50,
)

for entry in page.entries:
    print(f"{entry.word} → {entry.replacement}")

client.dictionaries.entries.update(
    dictionary.id,
    page.entries[0].id,
    replacement="postgres",
)

client.dictionaries.entries.delete(dictionary.id, page.entries[0].id)
```

```typescript TypeScript
const createdEntry = await client.dictionaries.entries.add(dictionary.id, {
  word: 'Postgres',
  replacement: 'post-gres',
});

const page = await client.dictionaries.entries.list(dictionary.id, {
  search: 'post',
  limit: 50,
});

for (const entry of page.entries) {
  console.log(`${entry.word} → ${entry.replacement}`);
}

await client.dictionaries.entries.update(
  dictionary.id,
  page.entries[0].id,
  { replacement: 'postgres' }
);

await client.dictionaries.entries.delete(dictionary.id, createdEntry.id);
```

```java Java
import com.kugelaudio.sdk.DictionaryEntry;
import com.kugelaudio.sdk.DictionaryEntryInput;
import com.kugelaudio.sdk.DictionaryEntryListResponse;

DictionaryEntry createdEntry = client.dictionaries().entries().add(
    dictionary.getId(),
    new DictionaryEntryInput("Postgres", "post-gres")
);

DictionaryEntryListResponse page = client.dictionaries().entries().list(
    dictionary.getId(),
    "post",  // search
    50,      // limit
    null,    // offset
    null     // projectId
);

for (DictionaryEntry entry : page.getEntries()) {
    System.out.println(entry.getWord() + " → " + entry.getReplacement());
}

// update(dictionaryId, entryId, word, replacement, ipa, caseSensitive); null leaves a field unchanged
client.dictionaries().entries().update(
    dictionary.getId(),
    page.getEntries().get(0).getId(),
    null,
    "postgres",
    null,
    null
);

client.dictionaries().entries().delete(
    dictionary.getId(),
    createdEntry.getId()
);
```

```rust Rust
use kugelaudio::{DictionaryEntryUpdate, EntryListOptions, NewDictionaryEntry};

let entries = client.dictionaries().entries();

let created_entry = entries
    .add(dictionary.id, &NewDictionaryEntry::new("Postgres", "post-gres"), None)
    .await?;

let page = entries
    .list(dictionary.id, &EntryListOptions::default().search("post").limit(50))
    .await?;

for entry in &page.entries {
    println!("{} → {}", entry.word, entry.replacement);
}

entries
    .update(
        dictionary.id,
        page.entries[0].id,
        &DictionaryEntryUpdate::new().replacement("postgres"),
        None,
    )
    .await?;

entries.delete(dictionary.id, created_entry.id, None).await?;
```

```cpp C++
const kugelaudio::DictionaryEntriesResource& entries = client.dictionaries().entries();

kugelaudio::DictionaryEntry created_entry = entries.add(dictionary.id, {"Postgres", "post-gres"});

kugelaudio::DictionaryEntryListOptions options;
options.search = "post";
options.limit = 50;
kugelaudio::DictionaryEntryList page = entries.list(dictionary.id, options);

for (const auto& entry : page.entries) {
  std::cout << entry.word << " → " << entry.replacement << '\n';
}

kugelaudio::DictionaryEntryUpdate update;
update.replacement = "postgres";
entries.update(dictionary.id, page.entries[0].id, update);

entries.remove(dictionary.id, created_entry.id);
```

```dart Dart
final createdEntry = await client.dictionaries.entries.add(
  dictionary.id,
  word: 'Postgres',
  replacement: 'post-gres',
);

final page = await client.dictionaries.entries.list(
  dictionary.id,
  search: 'post',
  limit: 50,
);

for (final entry in page.entries) {
  print('${entry.word} → ${entry.replacement}');
}

await client.dictionaries.entries.update(
  dictionary.id,
  page.entries[0].id,
  replacement: 'postgres',
);

await client.dictionaries.entries.delete(dictionary.id, createdEntry.id);
```

</CodeGroup>

## Atomic bulk sync

The bulk replace (`replace_all` in Python, Rust and C++, `replaceAll` in TypeScript,
Java and Dart) upserts every entry in one transaction and deletes entries currently in the
dictionary whose `word` is not in the payload. Use it to sync from a CMS, PIM, or
internal glossary.

<Warning>
The bulk replace is intentionally destructive for omitted words. Only call it
with the complete desired contents of that dictionary.
</Warning>

<CodeGroup>

```python Python
result = client.dictionaries.entries.replace_all(
    dictionary.id,
    entries=[
        {"word": "Postgres", "replacement": "post-gres"},
        {"word": "Kubernetes", "replacement": "koo-ber-net-eez"},
    ],
)

print(f"upserted={result.upserted} deleted={result.deleted}")
```

```typescript TypeScript
const result = await client.dictionaries.entries.replaceAll(dictionary.id, [
  { word: 'Postgres', replacement: 'post-gres' },
  { word: 'Kubernetes', replacement: 'koo-ber-net-eez' },
]);

console.log(`upserted=${result.upserted} deleted=${result.deleted}`);
```

```java Java
import com.kugelaudio.sdk.BulkReplaceResult;
import java.util.List;

BulkReplaceResult result = client.dictionaries().entries().replaceAll(
    dictionary.getId(),
    List.of(
        new DictionaryEntryInput("Postgres", "post-gres"),
        new DictionaryEntryInput("Kubernetes", "koo-ber-net-eez")
    )
);

System.out.printf(
    "upserted=%d deleted=%d%n",
    result.getUpserted(),
    result.getDeleted()
);
```

```rust Rust
use kugelaudio::DictionaryEntryInput;

let result = client
    .dictionaries()
    .entries()
    .replace_all(
        dictionary.id,
        &[
            DictionaryEntryInput::new("Postgres", "post-gres"),
            DictionaryEntryInput::new("Kubernetes", "koo-ber-net-eez"),
        ],
        None,
    )
    .await?;

println!("upserted={} deleted={}", result.upserted, result.deleted);
```

```cpp C++
kugelaudio::BulkReplaceResult result = client.dictionaries().entries().replace_all(
    dictionary.id,
    {
        {"Postgres", "post-gres"},
        {"Kubernetes", "koo-ber-net-eez"},
    });

std::cout << "upserted=" << result.upserted << " deleted=" << result.deleted << '\n';
```

```dart Dart
final result = await client.dictionaries.entries.replaceAll(dictionary.id, const [
  DictionaryEntryInput(word: 'Postgres', replacement: 'post-gres'),
  DictionaryEntryInput(word: 'Kubernetes', replacement: 'koo-ber-net-eez'),
]);

print('upserted=${result.upserted} deleted=${result.deleted}');
```

</CodeGroup>

## Dictionaries at synthesis

Dictionaries apply from the request's project: the project ID you pass, or, when
you omit it, the project of your API key. The project's active dictionaries for the
request language apply. Add dictionary IDs for an exact selection (inactive ones
included), or pass an empty list to disable dictionaries for the request. A
non-empty dictionary-ID list from a caller without a project is rejected. If you
serve many end customers, give each its own project and key with the
[Projects API](/api-reference/endpoints/projects). Keep normalization enabled unless
you have a specific reason to bypass it.

<CodeGroup>

```python Python
audio = client.tts.generate(
    text="Willkommen bei Kugel Audio.",
    voice_id=1071,
    language="de",
    project_id=dictionary.project_id,
    dictionary_ids=[dictionary.id],  # optional: exactly this dictionary
)
```

```typescript TypeScript
const audio = await client.tts.generate({
  text: 'Postgres runs our product analytics.',
  modelId: 'kugel-3.5',
  voiceId: 1071,
  language: 'en',
  normalize: true,
  projectId: dictionary.projectId,
  dictionaryIds: [dictionary.id], // optional: exactly this dictionary
});
```

```java Java
AudioResponse audio = client.tts().generate(
    GenerateRequest.builder("Willkommen bei Kugel Audio.")
        .voiceId(1071)
        .language("de")
        .projectId(dictionary.getProjectId())
        .dictionaryIds(List.of((int) dictionary.getId())) // optional: exactly this dictionary
        .build()
);
```

```rust Rust
let audio = client
    .tts()
    .generate(
        &TtsRequest::new("Willkommen bei Kugel Audio.")
            .voice_id(1071)
            .language("de")
            .project_id(dictionary.project_id)
            .dictionary_ids(vec![dictionary.id]), // optional: exactly this dictionary
    )
    .await?;
```

```cpp C++
kugelaudio::GenerateRequest request;
request.text = "Willkommen bei Kugel Audio.";
request.voice_id = 1071;
request.language = "de";
request.project_id = dictionary.project_id;
request.dictionary_ids = std::vector<std::int64_t>{dictionary.id};  // optional: exactly this dictionary
kugelaudio::AudioResponse audio = client.tts().generate(request);
```

```dart Dart
final audio = await client.tts.generate(
  text: 'Willkommen bei Kugel Audio.',
  voiceId: 1071,
  language: 'de',
  projectId: dictionary.projectId,
  dictionaryIds: [dictionary.id], // optional: exactly this dictionary
);
```

</CodeGroup>

Streaming and multi-context sessions take the same two settings in their
configuration and send them with the session configuration; they apply to every
turn or context on that connection:

<Tabs>
<Tab title="Python">

Every generation and session method accepts `project_id` and `dictionary_ids`:
`generate()`, `generate_async()`, `stream()`, `stream_async()`,
`streaming_session()`, `streaming_session_sync()` and `multi_context_session()`.

```python
async with client.tts.streaming_session(
    voice_id=1071, language="en", project_id=dictionary.project_id
) as session:
    ...
```

</Tab>
<Tab title="TypeScript">

`GenerateOptions`, `StreamConfig` and `MultiContextConfig` take `projectId` and
`dictionaryIds`:

```typescript
const session = client.tts.streamingSession(
  { voiceId: 1071, language: 'en', projectId: dictionary.projectId },
  { onChunk: (chunk) => playAudio(chunk.audio) },
);

const multi = client.tts.createMultiContextSession({
  defaultVoiceId: 1071,
  language: 'en',
  projectId: dictionary.projectId,
  dictionaryIds: [dictionary.id],
});
```

</Tab>
<Tab title="Java">

`GenerateRequest`, `StreamConfig` and `MultiContextConfig` builders take
`.projectId(long)` and `.dictionaryIds(List<Integer>)` (`List.of()` disables
dictionaries):

```java
StreamConfig config = StreamConfig.builder()
    .voiceId(1071)
    .language("en")
    .projectId(dictionary.getProjectId())
    .build();
```

</Tab>
<Tab title="Rust">

`TtsRequest`, `StreamConfig` and `MultiContextConfig` take `.project_id(i64)` and
`.dictionary_ids(Vec<i64>)` (`vec![]` disables dictionaries):

```rust
let session = client.tts().streaming_session(
    StreamConfig::new(1071)
        .language("en")
        .project_id(dictionary.project_id),
);

let multi = client.tts().multi_context_session(
    MultiContextConfig::default()
        .default_voice_id(1071)
        .language("en")
        .project_id(dictionary.project_id)
        .dictionary_ids(vec![dictionary.id]),
);
```

</Tab>
<Tab title="C++">

`GenerateRequest`, `StreamConfig` and `MultiContextConfig` have `project_id` and
`dictionary_ids` fields (an empty vector disables dictionaries):

```cpp
kugelaudio::StreamConfig config;
config.voice_id = 1071;
config.language = "en";
config.project_id = dictionary.project_id;
auto session = client.tts().streaming_session(config);
```

</Tab>
<Tab title="Dart">

`generate()`, `stream()`, `StreamConfig` and `MultiContextConfig` take `projectId`
and `dictionaryIds` (`[]` disables dictionaries):

```dart
final session = client.tts.streamingSession(
  StreamConfig(voiceId: 1071, language: 'en', projectId: dictionary.projectId),
);

final multi = client.tts.multiContextSession(MultiContextConfig(
  defaultVoiceId: 1071,
  language: 'en',
  projectId: dictionary.projectId,
  dictionaryIds: [dictionary.id],
));
```

</Tab>
</Tabs>

## Delete a dictionary

Deleting a dictionary also deletes its entries:

<CodeGroup>

```python Python
client.dictionaries.delete(dictionary.id)
```

```typescript TypeScript
await client.dictionaries.delete(dictionary.id);
```

```java Java
client.dictionaries().delete(dictionary.getId());
```

```rust Rust
client.dictionaries().delete(dictionary.id, None).await?;
```

```cpp C++
client.dictionaries().remove(dictionary.id);
```

```dart Dart
await client.dictionaries.delete(dictionary.id);
```

</CodeGroup>

## Next steps

- [Types and errors](/sdks/types#dictionary-types): `Dictionary`, `DictionaryEntry`, and result models
- [Text normalization](/sdks/normalization): how normalization interacts with dictionaries
- [Dictionaries API reference](/api-reference/endpoints/dictionaries): the HTTP contract and field limits
