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

# Pronunciation Dictionaries overview

> Resource hierarchy, lifecycle, Cartesia server-side sync, and how the API differs from ElevenLabs's.

A **pronunciation dictionary** is a workspace-scoped lexicon that maps words or phrases to either a text replacement (*alias*) or a phonetic string (*phoneme*). Attach one or more dictionaries to an agent and the runtime applies the rules at speech-synthesis time so brand names, acronyms, and technical terms come out the way you want.

## Resource hierarchy

```
pronunciation_dictionary               (mutable metadata + latest_version_id pointer)
└── pronunciation_dictionary_version   (immutable; one row per rule edit)
    └── pronunciation_dictionary_rule  (one row per word; alias or phoneme)
```

Agents link to dictionaries through a separate **locator** table, written via `PUT /v1/agents/:agentId/pronunciation-dictionaries`. A locator carries a `versionId` field: `null` means "follow latest" (resolved at session start), a specific `pdver_…` ID pins the agent to that version.

## Lifecycle

```mermaid theme={null}
flowchart LR
  Create([POST /v1/pronunciation-dictionaries/from-file\nor /from-rules]) --> Version[(Version created — immutable)]
  Version --> Sync([cartesia.uploadVersion\n→ cartesia_dict_id cached on version row])
  Version --> EditRules([POST .../add-rules\n.../remove-rules\n.../set-rules])
  EditRules --> NewVersion[(New version — previous unchanged)]
  NewVersion --> Sync
  Version --> Attach([PUT /v1/agents/:id/pronunciation-dictionaries])
  Attach --> Resolve([GET /v1/runtime/agent-config/:agentId/pronunciation])
  Resolve --> Runtime[(Rules applied at TTS time)]
```

Every mutation that produces a new version triggers a **Cartesia sync**. Deleting a dictionary is blocked (`409 dictionary_in_use`) while any agent locator references it — detach all agents first.

## Identifiers

| Resource   | ID prefix | Example             |
| ---------- | --------- | ------------------- |
| Dictionary | `pdict_`  | `pdict_01hwxxxxxx`  |
| Version    | `pdver_`  | `pdver_01hwxxxxxx`  |
| Rule       | `pdrule_` | `pdrule_01hwxxxxxx` |

## Rule types

* **Alias** — replaces the grapheme with spoken text before TTS. `"UN"` → `"United Nations"`. Works on every TTS provider.
* **Phoneme** — replaces the grapheme with an IPA or CMU phonetic string. Effective on **Cartesia** (server-side, **IPA only** — see note below), **ElevenLabs Flash v2** (inline SSML, IPA or CMU), and **AWS Polly Neural** (inline SSML, IPA or CMU). Other models pass the rule through silently.

The alphabet (`ipa` or `cmu`) is stored per rule, but **every phoneme rule in a single version must share one alphabet**. The API rejects (`400`) any create or edit that would put both IPA and CMU phoneme rules in the same version — PLS allows exactly one `<lexicon alphabet="…">` per file, so a mixed-alphabet write would silently mislabel rules on download / re-import. Split IPA and CMU entries into separate dictionaries.

<Note>
  **Cartesia is IPA-only at the model layer.** Cartesia's hosted Pronunciation Dictionary API speaks IPA, and the sync layer converts IPA phonemes to Cartesia's pipe format (`<<a|b|c>>`) automatically. CMU phoneme rules are forwarded to Cartesia verbatim — Cartesia does not speak CMU natively, so those entries fall back to the default pronunciation. If you need a phoneme rule to fire on Cartesia, author it in IPA. CMU phonemes still apply on ElevenLabs Flash v2 and AWS Polly Neural via inline SSML.
</Note>

## Cartesia server-side sync

When a new version is created, the API fires a fire-and-forget call to Cartesia's hosted Pronunciation Dictionary API and caches the returned `pronunciation_dict_id` as `cartesiaDictId` on the version row. Subsequent reads of the same version reuse the cached ID; Cartesia is never re-asked for it. If `CARTESIA_API_KEY` is unset, the service logs `cartesia.uploadVersion CARTESIA_API_KEY unset, skipping` and the version proceeds without an ID — phoneme rules then degrade to passthrough on Cartesia voices but alias rules continue to apply via in-runtime substitution.

V1 attaches **one** dictionary per agent to Cartesia. If multiple locators are present, the runtime passes only the first locator's `cartesia_dict_id` to the Cartesia plugin and logs `cartesia.multi_dict_attach` for the rest.

## How Zeptar's API differs from ElevenLabs's

| Aspect               | ElevenLabs                                                       | Zeptar                                                                                                                        |
| -------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Follow-latest        | Always pinned via `version_id`                                   | `version_id: null` in a locator means "follow latest"; resolved at TTS time                                                   |
| Workspace access     | `permission_on_resource` field (`admin/editor/commenter/viewer`) | Not present — Zeptar uses its own organization permission model                                                               |
| Alphabet coexistence | Single alphabet per dictionary                                   | Single alphabet per **version**: the API rejects mixed-alphabet writes with `400`. Use separate dictionaries for IPA and CMU. |
| Cartesia integration | Not exposed                                                      | Native server-side sync; cached `cartesiaDictId` per version                                                                  |

## Authorization

All pronunciation dictionary endpoints require an authenticated session cookie or a workspace API key. The API enforces organization scoping: every dictionary belongs to exactly one organization, and requests are scoped to the caller's active organization. Cross-organization access returns `404` — there is no enumeration distinction between "wrong org" and "not found." The runtime resolver endpoint (`/v1/runtime/agent-config/:agentId/pronunciation`) is gated by the `AGENT_RUNTIME_SHARED_SECRET` header instead.
