# Create an assistant Source: https://docs.upliftai.org/api-reference/assistants/create-an-assistant /voice-agents/api-reference/openapi.yaml post /realtime-assistants Returns the assistant as stored. Provider and model strings are not checked here — they resolve when a call starts, so a wrong value fails that call, not this request. # Delete an assistant Source: https://docs.upliftai.org/api-reference/assistants/delete-an-assistant /voice-agents/api-reference/openapi.yaml delete /realtime-assistants/{realtimeAssistantId} Deleting is quiet: you get a 204 even if that id never existed, so a success is not proof anything was there. Every published version goes with it. Campaigns keep the now-dangling `assistantId`, and a running one pauses itself at its next dial with `statusMessage: "Paused: assistant version not found"`. The assistant's session list stops resolving, so pull the call history before you delete. # Get an assistant Source: https://docs.upliftai.org/api-reference/assistants/get-an-assistant /voice-agents/api-reference/openapi.yaml get /realtime-assistants/{realtimeAssistantId} Config comes back verbatim — omitted fields stay omitted — which makes this the read step before an update, since updates replace rather than merge. You get the draft unless you ask for a `version`. # Get an assistant's publish state Source: https://docs.upliftai.org/api-reference/assistants/get-an-assistants-publish-state /voice-agents/api-reference/openapi.yaml get /realtime-assistants/{realtimeAssistantId}/metadata Where `draft` and `prod` point, without any config. `prod` is absent until the first publish. When `draft.version` is ahead of `prod.version`, the draft has edits that calls don't run yet. # List assistants Source: https://docs.upliftai.org/api-reference/assistants/list-assistants /voice-agents/api-reference/openapi.yaml get /realtime-assistants Every assistant in the API key's project, as its draft. `aliases` on each row says whether it has been published and whether the draft is ahead of what calls run. # List published versions Source: https://docs.upliftai.org/api-reference/assistants/list-published-versions /voice-agents/api-reference/openapi.yaml get /realtime-assistants/{realtimeAssistantId}/versions Every version you published, newest first, without configs. Read one in full with [Get an assistant](/api-reference/assistants/get-an-assistant) and `?version=`. `aliases` marks the one calls run. Empty until the first publish. # Publish an assistant version Source: https://docs.upliftai.org/api-reference/assistants/publish-an-assistant-version /voice-agents/api-reference/openapi.yaml post /realtime-assistants/{realtimeAssistantId}/publish Choose the version calls run. `version: "draft"` publishes your saved edits. A number points calls back at a version you published before, which is how you roll back. Neither touches the draft. Until the first publish every save goes live at once. After it, saves change the draft only, so publish again to put them on calls. Publishing what `prod` already runs changes nothing and still returns 200. See [Versions](/voice-agents/assistants/versions). # Update an assistant Source: https://docs.upliftai.org/api-reference/assistants/update-an-assistant /voice-agents/api-reference/openapi.yaml post /realtime-assistants/{realtimeAssistantId} This replaces rather than merges, so read the assistant, change what you need, and send the whole object back. Leaving out `description` or `public` clears them — `public` resets to false — while `name` and `config` keep their stored values. Only populated parts of `config` are validated, so a config missing `agent.instructions` saves fine and leaves the assistant unable to take calls. Until the assistant's first publish, a save goes live at once. After that, an update changes the draft only. Calls and share links keep running the published version, and the `public` check reads it too, until you [publish](/api-reference/assistants/publish-an-assistant-version) again. `noiseCancellation` and `backgroundNoise` are checked on every save that sends `config`. An assistant stored with a bad value before these checks gets a 400 on that save until you fix it. A save without `config` still goes through. # Build an assistant from a brief Source: https://docs.upliftai.org/api-reference/authoring-an-assistant/build-an-assistant-from-a-brief /voice-agents/api-reference/openapi.yaml post /realtime-assistants/builds The assistant exists the moment this returns, but a background pipeline writes and hardens its Urdu prompt over the next few minutes — until the status poll reads `done`, its entire prompt is the literal text `(building…)`, so don't put it on a call yet. [Poll the build status](/api-reference/authoring-an-assistant/poll-a-builds-progress) for the outcome. Every request builds a fresh assistant; to change one you already built, [refine it](/api-reference/authoring-an-assistant/refine-a-built-assistants-prompt) or [update its config directly](/api-reference/assistants/update-an-assistant). # Get an assistant's build record Source: https://docs.upliftai.org/api-reference/authoring-an-assistant/get-an-assistants-build-record /voice-agents/api-reference/openapi.yaml get /realtime-assistants/builds/{assistantId} The durable record behind an assistant's builds: the brief, prompt version, gate result, and chosen sample transcripts — it never expires. It is not live progress: `status` is whatever the last run wrote when it ended, so an in-flight refine still reads the previous outcome. Poll the build status for anything currently running. # Poll a build's progress Source: https://docs.upliftai.org/api-reference/authoring-an-assistant/poll-a-builds-progress /voice-agents/api-reference/openapi.yaml get /realtime-assistants/builds/status/{buildId} Works for refine runs too — poll whichever `buildId` you got back. Runs take minutes, so poll every few seconds. `done` only means the pipeline stopped; whether the grader was satisfied is `result.passed`. Live detail (`phase`, `round`, transcript counts) is kept for an hour after the run last moved; after that the poll still returns the outcome — `result` on success, `error` on failure. # Refine a built assistant's prompt Source: https://docs.upliftai.org/api-reference/authoring-an-assistant/refine-a-built-assistants-prompt /voice-agents/api-reference/openapi.yaml post /realtime-assistants/builds/{assistantId}/refine A finished refine regenerates the assistant's `name` and its whole `config` from the brief, overwriting any edits you made by hand — only the prompt carries forward. It reruns the same rehearse-and-grade rounds as a build, so it takes minutes: [poll the build status](/api-reference/authoring-an-assistant/poll-a-builds-progress). Starting one while another build is in flight still returns 200, then surfaces as `failed` in that poll. # Rehearse a call against a persona Source: https://docs.upliftai.org/api-reference/authoring-an-assistant/rehearse-a-call-against-a-persona /voice-agents/api-reference/openapi.yaml post /realtime-assistants/builds/{assistantId}/simulate Text-only: no call is placed, and it runs whatever prompt the assistant has saved right now, on the assistant's own LLM provider, model, and generation settings. Despite the `builds` path, it works on any assistant — built or hand-written. Three inputs aim the simulation: - `persona` — who answers. `behavior` goes verbatim into the caller model's system prompt; `path` defaults to `negative`, because the uncooperative arc is where prompts break. - `context` — the scene: what the caller already knows about who is calling and why. Omit it to fall back to the build brief's context; with neither, the caller answers like a genuine cold call. - `variables` — the customer record for this one call, same as `variables` on a real outbound call. Both sides see it: the agent substitutes its tokens, the caller answers consistently with it. Anything wrong with the setup — an unsupported provider, a bad body — fails as a plain JSON 400 before any streaming starts. Seen enough mid-conversation? Just disconnect: the run stops server-side, and so does the spend. # Aggregate stats for a campaign Source: https://docs.upliftai.org/api-reference/campaign-outcomes/aggregate-stats-for-a-campaign /voice-agents/api-reference/openapi.yaml get /campaigns/{campaignId}/stats Counters for every run of the campaign, written by the post-call pipeline — all zero until the first call finishes. **Note:** Scores, conversions, and sentiment are computed by LLMs — best effort, with room to be wrong. We are continuously closing the tracking gaps, but for sensitive workloads we recommend running your own analysis on the transcripts delivered over [webhooks](/voice-agents/webhooks/overview). # List a campaign's callback requests Source: https://docs.upliftai.org/api-reference/campaign-outcomes/list-a-campaigns-callback-requests /voice-agents/api-reference/openapi.yaml get /campaigns/{campaignId}/callbacks The contacts who asked to be phoned back by a person, soonest first. Post-call analysis opens these — nothing in the API creates one. Every callback comes back, the already-handled (`done`) alongside the still-waiting (`open`), and there is no status query parameter — so to build a "who still needs a call" queue, filter to `open` in your own code. **Note:** Scores, conversions, and sentiment are computed by LLMs — best effort, with room to be wrong. We are continuously closing the tracking gaps, but for sensitive workloads we recommend running your own analysis on the transcripts delivered over [webhooks](/voice-agents/webhooks/overview). # List a campaign's calls Source: https://docs.upliftai.org/api-reference/campaign-outcomes/list-a-campaigns-calls /voice-agents/api-reference/openapi.yaml get /campaigns/{campaignId}/calls One row per enrolled contact per campaign run, showing that contact's latest attempt — never-dialed contacts included. Earlier attempts collapse into the latest one, and a relaunch or recurrence can put the same `contactId` on the page twice. Rows come in no particular order, so don't read the top of the list as the most recent. Where `callId` is present it is the session id. Pass it to the [session detail](/api-reference/sessions-%26-call-records/get-a-calls-transcript-and-outcomes) for that call's transcript and recording. **Note:** Scores, conversions, and sentiment are computed by LLMs — best effort, with room to be wrong. We are continuously closing the tracking gaps, but for sensitive workloads we recommend running your own analysis on the transcripts delivered over [webhooks](/voice-agents/webhooks/overview). # List a campaign's conversion records Source: https://docs.upliftai.org/api-reference/campaign-outcomes/list-a-campaigns-conversion-records /voice-agents/api-reference/openapi.yaml get /campaigns/{campaignId}/conversions One row per call that post-call analysis scored as an outcome — never-dialed, unanswered, and ungraded contacts are absent. Newest first. This route returns the stored record as-is; [`/campaigns/{campaignId}/results`](/api-reference/campaign-outcomes/list-a-campaigns-outcomes) serves the same rows joined to each contact's name and phone and labelled with the campaign's goal vocabulary, which is the easier one to render. **Note:** Scores, conversions, and sentiment are computed by LLMs — best effort, with room to be wrong. We are continuously closing the tracking gaps, but for sensitive workloads we recommend running your own analysis on the transcripts delivered over [webhooks](/voice-agents/webhooks/overview). # List a campaign's outcomes Source: https://docs.upliftai.org/api-reference/campaign-outcomes/list-a-campaigns-outcomes /voice-agents/api-reference/openapi.yaml get /campaigns/{campaignId}/results One row per connected call, carrying the outcome post-call analysis recorded — a booked appointment, a qualified lead, an RSVP, whatever the campaign's goal defines as a result. The shape of each outcome and the labels to render it under follow that goal; `vocabulary` tells your UI which set applies. Contacts never dialed or never answered are not here — they live in [`/campaigns/{campaignId}/calls`](/api-reference/campaign-outcomes/list-a-campaigns-calls) — and a campaign with no scorecard produces no rows at all. **Note:** Scores, conversions, and sentiment are computed by LLMs — best effort, with room to be wrong. We are continuously closing the tracking gaps, but for sensitive workloads we recommend running your own analysis on the transcripts delivered over [webhooks](/voice-agents/webhooks/overview). # Update a callback's status Source: https://docs.upliftai.org/api-reference/campaign-outcomes/update-a-callbacks-status /voice-agents/api-reference/openapi.yaml patch /campaigns/callbacks/{callbackId} Flip a callback between `open` and `done` as your team works through them. Only the status changes, and either direction is idempotent. Ids come from the campaign's callbacks list and contain `#` and `+`, so percent-encode the path value — an under-encoded id returns not found. # Append contacts to a campaign Source: https://docs.upliftai.org/api-reference/campaigns/append-contacts-to-a-campaign /voice-agents/api-reference/openapi.yaml post /campaigns/{campaignId}/contacts The only way to add contacts after launch — the other ways in: [Contacts](/voice-agents/campaigns/contacts). Only a `continuous` campaign that is `running` or `paused` takes them. Draft, completed, closed and bounded campaigns all 409 — a bounded campaign's contacts are fixed at the snapshot taken at launch. A bad row is rejected on its own — the rest of the batch still lands, and the response is a 200 either way. Read `summary` for what was actually accepted and why the rest were excluded. Column naming rules: [Special column names](/voice-agents/campaigns/contacts#special-column-names). # Close a continuous campaign Source: https://docs.upliftai.org/api-reference/campaigns/close-a-continuous-campaign /voice-agents/api-reference/openapi.yaml post /campaigns/{campaignId}/close How a continuous campaign ends: it accepts no more contacts and completes once queued work and in-flight calls finish. Idempotent, allowed from any state a continuous campaign can be in, leaves `state` untouched, and cannot be undone. Closing before launch still lets you launch — it just dials the contacts it already has. Bounded campaigns 409, since they complete on their own once the list snapshotted at launch runs out. # Create a campaign Source: https://docs.upliftai.org/api-reference/campaigns/create-a-campaign /voice-agents/api-reference/openapi.yaml post /campaigns The campaign lands in `draft` and dials nothing until you launch it. You don't need the full config up front: a draft can be created without an `assistant.assistantId` and [updated field by field](/api-reference/campaigns/update-a-campaign) until launch, which is where missing pieces are rejected. What you do send is checked now: a `contacts.listId` that doesn't exist fails this request. All the ways contacts get into a campaign: [Contacts](/voice-agents/campaigns/contacts). The one choice you can't revisit is `mode` — `bounded` or `continuous` is fixed at creation. # Get a campaign Source: https://docs.upliftai.org/api-reference/campaigns/get-a-campaign /voice-agents/api-reference/openapi.yaml get /campaigns/{campaignId} Read a campaign's `state` here before calling pause, resume, launch or close — each of those only works from specific states (pause needs `running`, resume needs `paused`) and returns a 409 from any other. Polling a running campaign also catches a scheduled launch firing, or a pause the platform applied on its own. # Launch a campaign now Source: https://docs.upliftai.org/api-reference/campaigns/launch-a-campaign-now /voice-agents/api-reference/openapi.yaml post /campaigns/{campaignId}/launch Dialing starts as soon as the calling window allows. **The contact list is snapshotted at launch.** The configured list is copied into the run, so rows you add or remove from it afterward never reach this campaign. On a `continuous` campaign, [append contacts to the campaign](/api-reference/campaigns/append-contacts-to-a-campaign) instead. Launch works from three states: `draft`, `scheduled` (fires it early), and `launch_failed` (retries it). Launching a scheduled campaign early leaves `scheduledFor` sitting in the config, but it will not fire a second time. # List campaigns Source: https://docs.upliftai.org/api-reference/campaigns/list-campaigns /voice-agents/api-reference/openapi.yaml get /campaigns Every campaign in the API key's project, a page at a time. # Pause campaign dialing Source: https://docs.upliftai.org/api-reference/campaigns/pause-campaign-dialing /voice-agents/api-reference/openapi.yaml post /campaigns/{campaignId}/pause The dialer stops claiming new contacts, but calls already in flight run to the end — expect live calls for a few more minutes. Only a `running` campaign can pause; anything else, a second pause included, is a 409. # Resume a paused campaign Source: https://docs.upliftai.org/api-reference/campaigns/resume-a-paused-campaign /voice-agents/api-reference/openapi.yaml post /campaigns/{campaignId}/resume The dialer starts claiming contacts again within a few seconds, and `statusMessage` is cleared. Only a `paused` campaign can resume — anything else, `running` included, is a 409. A campaign the platform paused for credits will pause again on the next credit failure. # Schedule a campaign launch Source: https://docs.upliftai.org/api-reference/campaigns/schedule-a-campaign-launch /voice-agents/api-reference/openapi.yaml post /campaigns/{campaignId}/schedule The campaign moves to `scheduled` and sits there until `scheduledFor`, when the platform launches it — even outside the calling window; dialing then waits for the window to open. The contact list is snapshotted at that launch moment, not now: edits to the list before `scheduledFor` make it in, edits after never reach the campaign. Only a `draft` campaign can be scheduled — to move a time already set, PATCH `config.schedule.scheduledFor`. # Update a campaign Source: https://docs.upliftai.org/api-reference/campaigns/update-a-campaign /voice-agents/api-reference/openapi.yaml patch /campaigns/{campaignId} What you can edit depends on the state: - `draft`, `launch_failed` — everything - `scheduled` — everything except `goal` and `assistant` - `launching`, `running`, `paused`, `draining` — only `assistantVersion`, `schedule.callingWindow`, `dialing`, `tools` and `delivery`; `name` is frozen too - `completed` — nothing Config sections replace rather than merge, so send each section back whole. `schedule` is the exception: `scheduledFor` and `callingWindow` are kept independently. # Create a contact list Source: https://docs.upliftai.org/api-reference/contacts/create-a-contact-list /voice-agents/api-reference/openapi.yaml post /contact-lists Turn rows into a stored list you can call: an upload from [`/contact-lists/uploads`](/api-reference/contacts/upload-a-contact-file), inline CSV or pasted lines, or a Google Sheet. The import validates rows and deduplicates by phone number, keeping only contacts that can be dialed. Pass the returned `listId` as a campaign's `config.contacts.listId`. A list is never edited — to call a different set of people, import again. To see the outcome before creating: [dry-run](/api-reference/contacts/dry-run-a-contact-import) inline rows or a sheet, [re-preview](/api-reference/contacts/re-preview-an-upload-with-a-mapping) an uploaded file. Column naming rules: [Special column names](/voice-agents/campaigns/contacts#special-column-names). # Dry-run a contact import Source: https://docs.upliftai.org/api-reference/contacts/dry-run-a-contact-import /voice-agents/api-reference/openapi.yaml post /contact-sources/preview See exactly who a campaign would dial before you commit. It takes the same inputs as [`/contact-lists`](/api-reference/contacts/create-a-contact-list): a Google Sheet `ref`, or inline `data` holding CSV text or pasted "Name, +phone" lines. Nothing is stored or registered, so run it as often as you like. The one input it doesn't take is an uploaded file — [re-preview the upload](/api-reference/contacts/re-preview-an-upload-with-a-mapping) instead. For sheets the read is live, and the sheet must be reachable: shared as anyone-with-the-link, or read through a Google `connectionId`. When it isn't, the 500 doesn't forward Google's reason — a wrong spreadsheet id, a missing tab, and an unshared file all look identical. Already registered the source? [Re-check it](/api-reference/google-sheet-sources/re-check-a-registered-sheet) with the stored mapping instead. Column naming rules: [Special column names](/voice-agents/campaigns/contacts#special-column-names). # Get a contact list's metadata Source: https://docs.upliftai.org/api-reference/contacts/get-a-contact-lists-metadata /voice-agents/api-reference/openapi.yaml get /contact-lists/{listId} A list's metadata — how many contacts were kept and which rows were dropped, as recorded when the list was created. It is never recomputed after import, so it is not campaign progress. For the contacts themselves, page [`/contact-lists/{listId}/contacts`](/api-reference/contacts/page-through-a-lists-contacts). # List contact lists Source: https://docs.upliftai.org/api-reference/contacts/list-contact-lists /voice-agents/api-reference/openapi.yaml get /contact-lists One page of your contact lists, newest first. Each list's `summary` and `contactCount` are a snapshot of the import that created it — never recomputed, so they tell you nothing about how a campaign using the list is going. # Page through a list's contacts Source: https://docs.upliftai.org/api-reference/contacts/page-through-a-lists-contacts /voice-agents/api-reference/openapi.yaml get /contact-lists/{listId}/contacts The rows as they were saved when the list was created, not a fresh read of the source file or sheet. `q` filters after each page is read, so a page can come back empty while later pages still hold matches — keep going until `nextCursor` is absent. Pages are ordered by `contactId` as text rather than import order, and there is no sort parameter. # Re-preview an upload with a mapping Source: https://docs.upliftai.org/api-reference/contacts/re-preview-an-upload-with-a-mapping /voice-agents/api-reference/openapi.yaml post /contact-lists/uploads/{uploadId}/preview Try a column mapping before you commit to it. Nothing is written, so re-run this as often as you need while adjusting — a body is still required, so send `{}` to just re-run auto-detection. When the sample looks right, create the list from the same `uploadId` and mapping. Column naming rules: [Special column names](/voice-agents/campaigns/contacts#special-column-names). # Upload a contact file Source: https://docs.upliftai.org/api-reference/contacts/upload-a-contact-file /voice-agents/api-reference/openapi.yaml post /contact-lists/uploads Step one of the file flow. The API detects CSV vs `.xlsx` from the contents, reads only the first sheet of a workbook, parses the rows and stores them — no contact list yet. Pass the returned `uploadId` to [`/contact-lists`](/api-reference/contacts/create-a-contact-list) to create one, or re-preview first if the detected mapping needs correcting. A missing name or phone column is not an upload error: it lands in `mappingIssues` and those rows count as excluded. Column naming rules: [Special column names](/voice-agents/campaigns/contacts#special-column-names). # Create Phrase Replacement Config Source: https://docs.upliftai.org/api-reference/endpoint/create-phrase-replacement-config post /synthesis/phrase-replacement-config Create a new phrase replacement configuration Creates a new configuration for phrase replacements that can be applied during text-to-speech synthesis. # Get Phrase Replacement Config Source: https://docs.upliftai.org/api-reference/endpoint/get-phrase-replacement-config get /synthesis/phrase-replacement-config/{configId} Get a specific phrase replacement configuration Retrieves a specific phrase replacement configuration by its ID. # List Phrase Replacement Configs Source: https://docs.upliftai.org/api-reference/endpoint/list-phrase-replacement-configs get /synthesis/phrase-replacement-config List all phrase replacement configurations Returns a list of all phrase replacement configurations for the current organization and project. # Speech to Text (Beta) Source: https://docs.upliftai.org/api-reference/endpoint/speech-to-text POST /transcribe/speech-to-text Transcribe audio files to text using AI models Converts audio files to text using Uplift's speech recognition models optimized for Urdu language. **Model Selection:** - **scribe**: Full accuracy model - 40 credits per minute - **scribe-mini**: Cost-effective model - 20 credits per minute (50% cheaper) **Domain Optimization:** Use the `domain` parameter for improved accuracy in specialized contexts: - **phone-commerce**: Optimized for e-commerce conversations - **farming**: Optimized for agricultural terminology This API is currently in beta. Features and pricing may change. ```json Response theme={null} { "text": "آج کا موسم کیسا ہے؟ میں نے سنا ہے کہ بارش ہونے والی ہے۔" } ``` # Retrieve Async Audio Source: https://docs.upliftai.org/api-reference/endpoint/stream-audio get /synthesis/stream-audio/{mediaId} Retrieve audio generated by async text-to-speech endpoints This endpoint retrieves audio files generated by both async TTS endpoints. Requires the mediaId and JWT token returned from the async synthesis request. **Behavior depends on the originating endpoint:** **From `/text-to-speech-async`:** - Returns complete audio file when ready - Typically available within 1-2 seconds - Best for bots and webhooks needing complete files **From `/text-to-speech/stream-async`:** - Supports chunked streaming with ~150-200ms first chunk latency - Audio streams progressively as it's generated - Best for frontend apps and real-time playback **Key features:** - **Direct access** - Can be accessed directly by clients without your API key - **Secure** - JWT token ensures only authorized access to the audio - **CDN-ready** - Optimized for content delivery networks # Text to Speech Source: https://docs.upliftai.org/api-reference/endpoint/text-to-speech post /synthesis/text-to-speech Converts the provided text to speech audio using the specified voice. For best results, we expect you to use Urdu script. To get better pronounciation of English words, use ASCII characters for them. Example "یہ ایک exerted force ہے" Returns the audio data directly in the response. # Async Text to Speech Source: https://docs.upliftai.org/api-reference/endpoint/text-to-speech-async post /synthesis/text-to-speech-async Asynchronous text-to-speech synthesis for bot and server-side workflows This endpoint initiates text-to-speech synthesis and immediately returns a mediaId and token. The audio is generated asynchronously and can be retrieved using the returned credentials. **When to use this endpoint:** - **Bot integrations** (WhatsApp, Telegram, etc.) - Avoid audio passing through your system - **Webhook workflows** - When you need to process audio generation separately - **Batch processing** - When converting multiple texts without blocking - **Direct client delivery** - Let clients fetch audio directly using the secure token For best results with Urdu, use Urdu script. For English words within Urdu text, use ASCII characters. Example: "یہ ایک exerted force ہے" The generated audio URL can be shared directly with end users or services without proxying through your server. # Stream Text to Speech Source: https://docs.upliftai.org/api-reference/endpoint/text-to-speech-stream post /synthesis/text-to-speech/stream Converts the provided text to speech audio using the specified voice, response is streamed with "Transfer-Encoding" "chunked" header. We currently aim the p90 first chunk latency to be around 150-200ms in Pakistan, we are also actively working reducing this. For best results, we expect you to use Urdu script. To get better pronounciation of English words, use ASCII characters for them. Example "یہ ایک exerted force ہے" Returns the audio data directly in the response. # Async Streaming Text to Speech Source: https://docs.upliftai.org/api-reference/endpoint/text-to-speech-stream-async post /synthesis/text-to-speech/stream-async Asynchronous streaming text-to-speech for direct client delivery This endpoint initiates streaming text-to-speech synthesis and immediately returns a mediaId and token. Unlike regular async TTS, this enables chunked streaming with ~150-200ms first chunk latency when retrieved. **When to use this endpoint:** - **Frontend streaming** - Stream audio directly to browsers without proxy - **Low-latency playback** - Start playing audio before full generation completes - **CDN streaming** - Progressive download through content delivery networks - **Mobile apps** - Reduce initial buffering time For best results with Urdu, use Urdu script. For English words within Urdu text, use ASCII characters. Example: "یہ ایک exerted force ہے" The audio streams progressively when retrieved via the /stream-audio endpoint. # Update Phrase Replacement Config Source: https://docs.upliftai.org/api-reference/endpoint/update-phrase-replacement-config post /synthesis/phrase-replacement-config/{configId} Update an existing phrase replacement configuration Updates an existing phrase replacement configuration with new phrase replacements. # Get a contact source's metadata Source: https://docs.upliftai.org/api-reference/google-sheet-sources/get-a-contact-sources-metadata /voice-agents/api-reference/openapi.yaml get /contact-sources/{sourceId} The source's metadata — sheet ref, connection, column mapping — not the sheet's contacts. Campaign launches re-read the live sheet, so an old registration still dials today's rows. To see the rows the source produces right now, preview it. # List registered contact sources Source: https://docs.upliftai.org/api-reference/google-sheet-sources/list-registered-contact-sources /voice-agents/api-reference/openapi.yaml get /contact-sources Lists the Google Sheets sources registered in this API key's project, ordered by `sourceId` — arbitrary, but stable across pages. Nothing here reads Google, so a deleted or unshared sheet still lists normally; only a preview tells you whether it still resolves. CSV and pasted imports aren't sources — those land in [`/contact-lists`](/api-reference/contacts/create-a-contact-list). # Re-check a registered sheet Source: https://docs.upliftai.org/api-reference/google-sheet-sources/re-check-a-registered-sheet /voice-agents/api-reference/openapi.yaml post /contact-sources/{sourceId}/preview See what a registered source's sheet would produce for a campaign right now. It re-reads the sheet live with the stored `ref` and `mapping`, and persists nothing — worth a run before you launch against a source you registered days ago. To try a different mapping, use [`/contact-sources/preview`](/api-reference/contacts/dry-run-a-contact-import). # Register a Google Sheet as a source Source: https://docs.upliftai.org/api-reference/google-sheet-sources/register-a-google-sheet-as-a-source /voice-agents/api-reference/openapi.yaml post /contact-sources Register a Google Sheet as a reusable contact source. The source is a pointer to the live sheet, not a copy: a campaign reads whatever rows the sheet holds at that moment, so to change the contacts you edit the sheet. The registration itself never changes — to point at a different sheet or use a different column mapping, register a new source. Nothing is checked at registration today (this may change): a wrong spreadsheet id, an unshared sheet, or an unknown `connectionId` all register fine and only fail later — so [preview the source](/api-reference/google-sheet-sources/re-check-a-registered-sheet) right after creating it. Only a Google Sheet can be registered here. For a CSV or pasted rows, [create a contact list](/api-reference/contacts/create-a-contact-list) instead — a one-time import rather than a live source. Column naming rules: [Special column names](/voice-agents/campaigns/contacts#special-column-names). # Get a call's transcript and outcomes Source: https://docs.upliftai.org/api-reference/sessions-&-call-records/get-a-calls-transcript-and-outcomes /voice-agents/api-reference/openapi.yaml get /realtime-assistants/sessions/{sessionId}/detail Read this after a call ends. The transcript comes from the post-call report, so it is empty on calls that never connected and briefly empty on one that just hung up; `grade`, `conversion` and `callback` come from that same pipeline, so treat all three as optional. While a call is still live, poll [`/realtime-assistants/sessions/{sessionId}`](/api-reference/sessions-%26-call-records/get-a-sessions-status) instead. # Get a session's status Source: https://docs.upliftai.org/api-reference/sessions-&-call-records/get-a-sessions-status /voice-agents/api-reference/openapi.yaml get /realtime-assistants/sessions/{sessionId} Session metadata — where one browser session or phone call stands right now. Poll this while a call is in flight; once it ends, read the [session detail](/api-reference/sessions-%26-call-records/get-a-calls-transcript-and-outcomes) for the transcript, tool calls, and grade. # List an assistant's sessions Source: https://docs.upliftai.org/api-reference/sessions-&-call-records/list-an-assistants-sessions /voice-agents/api-reference/openapi.yaml get /realtime-assistants/{realtimeAssistantId}/sessions Every web session and phone call one assistant handled, newest first. Filters are applied after each page is read, so a page can come back short or even empty with more behind it — keep paging until `nextCursor` is absent. Records are kept for 90 days. # Listen in on a live call Source: https://docs.upliftai.org/api-reference/sessions-&-call-records/listen-in-on-a-live-call /voice-agents/api-reference/openapi.yaml post /realtime-assistants/sessions/{sessionId}/listen Get a LiveKit token for the room a live call is already running in. The listener joins hidden and subscribe-only — they can hear the call but not speak into it — and the token expires ten minutes after it is issued. Live transcription arrives on the `lk.transcription` text stream from the moment you connect, with nothing backfilled. # Create a web session token Source: https://docs.upliftai.org/api-reference/starting-a-conversation/create-a-web-session-token /voice-agents/api-reference/openapi.yaml post /realtime-assistants/{realtimeAssistantId}/session Mint a LiveKit token for a browser or mobile client to talk to a stored assistant. It is a standard LiveKit token — connect to `wsUrl` with any LiveKit client SDK or UI component library and the assistant joins the room on its own. The token is good for the assistant's `config.session.ttl`, 600 seconds by default — that caps how long the client has to connect, not how long the conversation can run, so mint one token per conversation. The session record is written when the token is minted, so an unused token still leaves a session behind. # Dispatch a call from this assistant Source: https://docs.upliftai.org/api-reference/starting-a-conversation/dispatch-a-call-from-this-assistant /voice-agents/api-reference/openapi.yaml post /realtime-assistants/{realtimeAssistantId}/call Dial a customer as this assistant. `dispatched` means the carrier accepted the call and it is about to ring, not that anyone picked up. The returned `callId` is also the `sessionId`: [poll the session status](/api-reference/sessions-%26-call-records/get-a-sessions-status) for ringing, answer, and outcome. The response's `externalRef` is the telephony reference to quote in support tickets. Send an `Idempotency-Key` on retries so a timed-out request can't place a second call; the key also makes the `callId` start with `api_` instead of being a fresh UUID. # Open a session without an API key Source: https://docs.upliftai.org/api-reference/starting-a-conversation/open-a-session-without-an-api-key /voice-agents/api-reference/openapi.yaml post /realtime-assistants/{realtimeAssistantId}/public-session Open a web session with no API key, so client-side code can mint its own token. Works only for assistants marked `public`, which makes the assistant id the entire credential: anyone who reads it out of your page source can start sessions billed to your organization. The assistant joins the room once the client connects. # Start a session from inline config Source: https://docs.upliftai.org/api-reference/starting-a-conversation/start-a-session-from-inline-config /voice-agents/api-reference/openapi.yaml post /realtime-assistants/adhoc/session Identical to [Create a web session token](/api-reference/starting-a-conversation/create-a-web-session-token) — you get a LiveKit `wsUrl` and token, and everything on that page applies. The difference: the assistant is created on the fly from the `config` you send, instead of using a stored id. It stays hidden from the stored-assistant routes and **is gone five hours later**. The inline `config` follows the same rules as [Create an assistant](/api-reference/assistants/create-an-assistant): provider and model strings resolve when the client connects, so an unsupported value kills that conversation rather than failing this request. For Urdu, tell the model to reply in Nastaliq script, not Roman Urdu. # Async TTS Concepts Source: https://docs.upliftai.org/async-concepts Understanding synchronous vs asynchronous text-to-speech Learn when to use async text-to-speech for optimal performance in your applications. ## Choosing the Right Approach **Quick Rule**: Use async TTS when you don't want audio data passing through your server. ### Comparison | Method | Best For | Response Time | How It Works | | - | - | - | - | | **Sync**
`/text-to-speech` | Direct playback, small texts | \~500ms-2s total | Returns complete audio in response | | **Streaming**
`/text-to-speech/stream` | Real-time playback through your server | \~150-200ms first chunk | Streams audio chunks through your server | | **Async**
`/text-to-speech-async` | Bots, webhooks, CDN delivery | Instant (returns URL) | Returns URL, complete audio available in 1-2s | | **Async Streaming**
`/text-to-speech/stream-async` | Frontend streaming without proxy | Instant (returns URL) | Returns URL, \~150-200ms first chunk when retrieved | ## When to Use Each Method ### Use **Async** (`/text-to-speech-async`): * WhatsApp/Telegram bots that need complete audio files * Webhook workflows where you process later * Batch processing multiple texts ### Use **Async Streaming** (`/text-to-speech/stream-async`): * Frontend apps that want streaming without proxy * When you need low latency first-byte delivery (\~150-200ms) * Direct client streaming from CDN * Real-time playback that starts before full generation ### Use **Regular Streaming** (`/text-to-speech/stream`): * When you need to process audio through your server * Adding custom headers or authentication ### Use **Sync** (`/text-to-speech`): * Simple, one-time conversions * Small texts with immediate playback ## How Async TTS Works ```mermaid theme={null} sequenceDiagram participant Client participant Your Server participant Uplift AI participant End User Client->>Your Server: Request audio Your Server->>Uplift AI: POST /text-to-speech-async Uplift AI-->>Your Server: {mediaId, token} Your Server-->>Client: Audio URL Client->>Uplift AI: GET /stream-audio/{mediaId}?token={token} Uplift AI-->>Client: Audio file ``` ## Simple Example ### WhatsApp Bot Integration ```python theme={null} import requests import json def send_voice_to_whatsapp(text: str, phone_number: str): # Step 1: Get audio URL from Uplift AI response = requests.post( "https://api.upliftai.org/v1/synthesis/text-to-speech-async", headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, json={ "voiceId": "v_meklc281", # Urdu female voice "text": text, "outputFormat": "MP3_22050_64" # Smaller for WhatsApp } ) result = response.json() audio_url = f"https://api.upliftai.org/v1/synthesis/stream-audio/{result['mediaId']}?token={result['token']}" # Step 2: Send URL directly to WhatsApp whatsapp_response = requests.post( "https://graph.facebook.com/v17.0/YOUR_PHONE_ID/messages", headers={'Authorization': 'Bearer WHATSAPP_TOKEN'}, json={ "messaging_product": "whatsapp", "to": phone_number, "type": "audio", "audio": {"link": audio_url} # Direct URL - no download needed! } ) return whatsapp_response.json() ``` ## Key Benefits Audio goes directly from Uplift AI to your users Get URL immediately, audio generates in background JWT tokens ensure only authorized access URLs work with any CDN or caching layer ## Voice & Format Options Use the [same voice IDs](/orator_voices) and output formats as regular TTS: ### Output Formats * `PCM_22050_16` - Raw PCM for direct audio processing * `MP3_22050_64` - Best for messaging apps (smaller files) * `MP3_22050_128` - Best quality/size balance * `WAV_22050_32` - When you need lossless audio * `ULAW_8000_8` - For telephony systems ## Next Steps See the full API documentation Listen to available voices Learn about sync TTS # Welcome to Uplift AI Source: https://docs.upliftai.org/introduction Build voice-powered applications with state-of-the-art Urdu TTS **Quick Start**: Jump straight to [Text-to-Speech API](/orator) or explore our [Interactive Demos](#try-it-now) ## What We Do Uplift AI provides developers with powerful APIs to create voice-enabled applications, specializing in high-quality Urdu text-to-speech that actually sounds natural. Real-time streaming for conversational AI applications Native pronunciation, not robotic transliteration ## Try It Now ```bash theme={null} curl -X POST https://api.upliftai.org/v1/synthesis/text-to-speech \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "voiceId": "v_8eelc901", "text": "Hello, this is a test", "outputFormat": "MP3_22050_128" }' \ --output test.mp3 ``` ```javascript theme={null} const response = await fetch('https://api.upliftai.org/v1/synthesis/text-to-speech', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ voiceId: 'v_8eelc901', text: 'Hello, this is a test', outputFormat: 'MP3_22050_128' }) }); const audioBlob = await response.blob(); // Play or save the audio ``` ```python theme={null} import requests response = requests.post( 'https://api.upliftai.org/v1/synthesis/text-to-speech', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, json={ 'voiceId': 'v_8eelc901', 'text': 'Hello, this is a test', 'outputFormat': 'MP3_22050_128' } ) with open('output.mp3', 'wb') as f: f.write(response.content) ``` ## Popular Use Cases Build conversational bots that speak naturally in Urdu Create real-time voice assistants with LiveKit Make content accessible with natural voice narration Convert articles to audio with authentic news anchor voices ## Choose Your Integration Path Visit [upliftai.org](https://upliftai.org) to generate your API key Simple HTTP requests for one-time conversions Real-time streaming for conversational AI Choose from multiple voice profiles optimized for different use cases: * **Info/Education** - Clear and articulate * **Nostalgic News** - Classic broadcaster style * **Dada Jee** - Storytelling voice * **Gen Z** - Modern, energetic delivery [Listen to voice samples →](/orator_voices) Check out our [tutorials](/tutorials) for complete implementation examples ## Real-World Example See how businesses use Uplift AI to enhance user engagement: Banking app with voice interface ## Need Help? Complete API documentation Get help from our team Join our developer community # Quickstart Guide Source: https://docs.upliftai.org/orator High-quality Pakistani text-to-speech with natural pronunciation ## Overview Orator is our specialized TTS API that delivers natural-sounding voice synthesis for Pakistani languages. It handles native pronunciation, mixed language text, and cultural nuances that generic TTS services miss. ### Why Orator? Trained on authentic Urdu speech patterns, not transliterations Seamlessly handles Urdu-English code-switching common in conversation Multiple voices matching regional preferences and use cases Simple REST & WebSocket APIs with comprehensive SDKs ## Quick Start ### 1. Get Your API Key Visit [upliftai.org](https://upliftai.org) to create your account Navigate to API Keys section and click "Generate New Key"
Pakistani TTS Demo
### 2. Make Your First Request Once you have your API key, you can make your first text-to-speech request. Here's a simple example using JavaScript: ```javascript theme={null} async function convertTextToSpeech() { try { const response = await fetch('https://api.upliftai.org/v1/synthesis/text-to-speech', { method: 'POST', headers: { 'Authorization': 'Bearer sk_api_your_key_here', 'Content-Type': 'application/json' }, body: JSON.stringify({ "voiceId": "v_8eelc901", // See available voice Ids in the documentation below "text": "سلام، آپ اِس وقت اوریٹر کی آواز سن رہے ہیں۔", // => "Salaam, Aap ise waqt Orator ki awaaz suun rahay hain." "outputFormat": "MP3_22050_128" }) }); // Get audio duration from headers const audioDuration = response.headers.get('x-uplift-ai-audio-duration'); console.log(`Audio duration: ${audioDuration}ms`); // The response body is an audio blob, you can save this or send it to your frontend to be played const audioBlob = await response.blob(); } catch (error) { console.error('Error:', error); } } // Call the function convertTextToSpeech(); ``` ### 3. Best Practices for Urdu Text **Pro Tip**: Test your text in small chunks first to ensure proper pronunciation before processing large documents. | Text Type | Recommendation | Example | | - | - | - | | Pure Urdu | Use Urdu script | آپ کیسے ہیں؟ میں ٹھیک ہوں۔ | | English in Urdu | Keep in ASCII | یہ ایک exerted force ہے | | Numbers | Use Western numerals | 2024 instead of ۲۰۲۴ | | Brands | Use official spelling | WhatsApp, not واٹس ایپ | ## Advanced Features ### Phrase Replacement for Perfect Pronunciation Perfect for handling: * **Brand names**: Convert English spellings to Urdu phonetics * **Technical terms**: Ensure consistent pronunciation * **LLM outputs**: Fix common misspellings from AI models * **Regional variations**: Adapt to local dialects For example: ```json theme={null} [ {"phrase":"سیونگز اکاؤنٹ","replacement":"savings account"}, // In Urdu Isabion is pronunced Izabiyan, but LLMs don't understand this {"phrase":"Isabion","replacement":"ایزابیان"}, // Sometimes the English spellings of brand names don't capture Urdu pronunciation {"phrase": "Meezan bank", "replacement": "میزان بینک"} ] ``` Here is how you can create a replacement configuration: ```javascript theme={null} // Create a phrase replacement configuration async function createPhraseReplacements() { try { const response = await fetch('https://api.upliftai.org/v1/synthesis/phrase-replacement-config', { method: 'POST', headers: { 'Authorization': 'Bearer sk_api_your_key_here', 'Content-Type': 'application/json' }, body: JSON.stringify({ "phraseReplacements": [ { "phrase": "سیونگز اکاؤنٹ", "replacement": "Savings account", }, { "phrase": "Meezan bank", "replacement": "میزان بینک" } ] }) }); const data = await response.json(); console.log('Configuration created with ID:', data.configId); return data.configId; // save this configId for later reuse } catch (error) { console.error('Error creating phrase replacements:', error); } } // Use the configuration ID in your text-to-speech request async function ttsWithPhraseReplacement(configId) { // Similar to the first example, but add phraseReplacementConfigId const requestBody = { "voiceId": "v_8eelc901", "text": "Meezan Bank اعتماد کا ضامن", "outputFormat": "MP3_22050_128", "phraseReplacementConfigId": configId }; // Make API request as shown in the first example } ``` ## Voice Profiles Each voice is optimized for specific use cases and audiences: **Info/Education**: `voiceId: "v_8eelc901"` Listen to articles, information, and videos. Fast and easy to understand. **Nostalgic News**: `voiceId: "v_30s70t3a"` A classic Pakistani news voice that we heard growing up. We bring those memories back. Stay calm, stay tuned. **Dada Jee**: `voiceId: "v_yypgzenx"` Listen to stories with a suspensful and deep voice. **Gen Z** (under development): `voiceId: "v_kwmp7zxt"` If you want your info clean and fast, this voice is for you. For more voices and langauges check [this voices page](/orator_voices) ## Common Use Cases Make whatsapp agents that respond to in voice. Make real time customer support over call. Farming advisory, simple medical FAQs i.e "Sar dardh k liye kia karoun" -> respond in voice News & blogs can offer Urdu play buttons that will play the audio Make captivating stories ## Error Handling ```javascript theme={null} try { const response = await fetch(TTS_ENDPOINT, options); if (!response.ok) { const error = await response.json(); switch(response.status) { case 400: console.error('Invalid request:', error.message); break; case 401: console.error('Invalid API key'); break; case 429: console.error('Rate limit exceeded, retry after:', response.headers.get('Retry-After')); break; case 500: console.error('Server error, please retry'); break; } } } catch (error) { console.error('Network error:', error); } ``` ## Need Help? Complete endpoint documentation Real-time streaming setup Get help with integration # Request Limits Source: https://docs.upliftai.org/orator_limits | API | Limit Type | Current Limit | | - | - | - | | Text To Speech | Maximum Characters | 2,500 per request | | Text To Speech | Rate Limit | 100 requests per minute | **Need higher limits?** All service limits can be increased upon request. Please contact us at [founders@upliftai.org](mailto:founders@upliftai.org) to discuss your requirements. # Voices Source: https://docs.upliftai.org/orator_voices Built to sound like Pakistan. 82 native voices — every accent, every walk of life. ## Voice catalog All voices speak Urdu. Pass any `voiceId` below to the synthesis API. Complete machine-readable data, including sample transcripts, is at [/voices.json](https://docs.upliftai.org/voices.json). | voiceId | Name | Description | Urdu accent | Gender | Age | Tags | | - | - | - | - | - | - | - | | `jinn` | Jinn | Ancient watcher carrying millennia of cold philosophical dread. | Standard | Male | Mythic | Dramatic, Mythic, Horror | | `sindhi-patriarch` | Sindhi Patriarch | Deep Sindhi-accented elder. Sufi gravity. | Sindhi | Male | Senior | Character, Devotional, Authoritative | | `punjabi-masi` | Punjabi Masi | Pragmatic Punjabi-accented young female — warm and earthy. | Punjabi | Female | Young | Character, Warm | | `cricket-commentator` | Cricket Commentator | Animated commentator calling a high-voltage T20 match — energetic, broadcast-grade. | Standard | Female | Young | Sports, Energetic, Professional | | `pashtun-pensioner` | Pashtun Pensioner | Dignified Pashtun-accented elder. Warm, weathered tone. | Pashto | Male | Senior | Character, Warm | | `crime-don` | Crime Don | Dark, menacing Bollywood-style villain. | Standard | Male | Adult | Dramatic, Character, Villain | | `schoolgirl` | Schoolgirl | Bright 12-year-old female. | Standard | Female | Child | Character, Youth, Educational | | `bengali-grandfather` | Bengali Grandfather | Warm Bengali-accented grandfather — garrulous, nostalgic. | Bengali | Male | Senior | Narration, Character, Warm | | `churail` | Churail | Cold, predatory female. | Standard | Female | Mythic | Dramatic, Horror, Mythic | | `balochi-seamstress` | Balochi Seamstress | Balochi-accented young woman — resilient, warm, community-driven delivery. | Balochi | Female | Young | Character, Community | | `iqbalian-shayar` | Iqbalian Shayar | Dramatic Urdu poetry recitation. Bold, passionate cadence. | Standard | Male | Adult | Poetry, Dramatic, Devotional | | `late-night-rj` | Late Night RJ | Warm late-night female radio host — smooth, intimate tone. | Standard | Female | Young | Warm, Entertainment, Professional | | `dha-matriarch` | DHA Matriarch | English-educated burger-Urdu grande dame — composed authority. | Burger | Female | Senior | Character, Authoritative | | `bihari-organizer` | Bihari Organizer | Bihari-accented young woman — capable, community-driven energy. | Bihari | Female | Young | Character, Community | | `memon-organizer` | Memon Organizer | Memon-accented young woman — warm, quietly capable community voice. | Memon | Female | Young | Character, Community | | `crisp-storyteller` | Crisp Storyteller | Crisp, contemplative male narration. | Standard | Male | Adult | Narration, Audiobook, Contemplative | | `qissa-khawan` | Qissa Khawan | Bold, classic male storyteller. Solemn pacing. | Standard | Male | Adult | Narration, Audiobook, Authoritative | | `female-narrator` | Female Narrator | Female documentary narrator — clean, balanced delivery. | Standard | Female | Adult | Narration, Professional, Documentary | | `male-narrator` | Male Narrator | Authoritative male documentary narrator. | Standard | Male | Adult | Narration, Professional, Documentary | | `podcast-host` | Podcast Host | Reflective male podcast host — measured pace. | Standard | Male | Adult | Narration, Professional, Contemplative | | `horror-narrator` | Horror Narrator | Spooky, menacing male voice. | Standard | Male | Adult | Dramatic, Horror, Narration | | `prime-time-anchor` | Prime Time Anchor | Male prime-time news anchor. | Standard | Male | Adult | News, Professional, Authoritative | | `senior-anchor` | Senior Anchor | Clean male anchor — balanced tone, good tempo. | Standard | Male | Senior | News, Authoritative, Professional | | `news-anchor` | News Anchor | Female anchor — sharp, clean, broadcast-ready delivery. | Standard | Female | Adult | News, Professional | | `news-reader` | News Reader | Soft female news voice — slightly fast pace. | Standard | Female | Young | News, Professional | | `field-correspondent` | Field Correspondent | Energetic male field reporter — sharp, fast-paced. | Standard | Male | Young | News, Energetic, Professional | | `paediatrician` | Paediatrician | Warm female paediatrician — calm authority. | Standard | Female | Adult | Professional, Medical, Warm | | `diabetologist` | Diabetologist | Plain-speaking male doctor — calm and educational. | Standard | Male | Adult | Professional, Medical, Educational | | `family-lawyer` | Family Lawyer | Composed female advocate — authoritative delivery. | Standard | Female | Adult | Professional, Authoritative | | `defense-advocate` | Defense Advocate | Methodical male defense advocate — precise tone. | Standard | Male | Adult | Professional, Authoritative | | `navy-officer` | Navy Officer | Seasoned commanding officer voice — authoritative, ship-tested delivery. | Standard | Male | Adult | Professional, Authoritative | | `stock-analyst` | Stock Analyst | Composed PSX commodity analyst — measured Urdu. | Standard | Male | Adult | Professional, Educational, Financial | | `helpdesk-agent` | Helpdesk Agent | Warm female customer service voice — patient, empathetic. | Standard | Female | Young | Professional, Customer Service, Warm | | `broadband-support` | Broadband Support | Polished male telecom support — empathetic, methodical, formal Urdu register. | Standard | Male | Adult | Professional, Customer Service | | `montessori-teacher` | Montessori Teacher | Warm-but-firm female teacher. | Standard | Female | Adult | Educational, Warm, Professional | | `urdu-professor` | Urdu Professor | Measured male university lecturer — erudite delivery. | Standard | Male | Adult | Educational, Authoritative, Professional | | `washroom-singer` | Washroom Singer | A gloriously tone-deaf shower singer — every note off-key, yet utterly unable to stop belting. | Standard | Male | Adult | Character, Funny, Warm | | `khateeb` | Khateeb | Impassioned male religious orator — Arabic and Urdu in one breath. | Standard | Male | Adult | Religious, Dramatic, Public Speaking | | `seerah-scholar` | Seerah Scholar | Seasoned scholar — methodical, authoritative delivery. | Standard | Male | Adult | Religious, Educational, Authoritative | | `hadith-narrator` | Hadith Narrator | Female religious teacher — calm scholarly authority. | Standard | Female | Adult | Religious, Educational, Authoritative | | `seerah-educator` | Seerah Educator | Measured male hadith narrator — scholarly warmth and isnad-recitation cadence. | Standard | Male | Adult | Religious, Educational | | `dawah-youtuber` | Dawah YouTuber | Warm young male dawah voice — hadith and Islamic knowledge in a friendly register. | Standard | Male | Young | Religious, Educational, Warm | | `nosey-aunty` | Nosey Aunty | Mohalla broadcaster — chai, gossip, righteous indignation. | Standard | Female | Adult | Character, Funny | | `orangi-khala` | Orangi Khala | Khandan-proud matriarch — proud, expressive, hearty delivery. | Standard | Female | Senior | Character, Funny | | `rohtaki-aunty` | Rohtaki Aunty | Punjabi-accented rural aunty — sharp wit, comedic timing, hearty delivery. | Punjabi | Female | Adult | Character, Funny | | `dua-uncle` | Dua Uncle | Frail elderly male, plaintive tone. | Standard | Male | Senior | Character, Street | | `bus-conductor` | Bus Conductor | Wiry, relentless minibus conductor — energetic shouting. | Standard | Male | Young | Character, Street, Energetic | | `lahori-barber` | Lahori Barber | Old-city barber running a one-man radio station — comedic, fast-talking. | Standard | Male | Adult | Character, Funny, Street | | `shopkeeper` | Shopkeeper | Warm kiryana shopkeeper — friendly haggling tone. | Standard | Male | Adult | Character, Street, Warm | | `street-vendor` | Street Vendor | Sun-baked thela-wala — sharp-tongued, energetic delivery. | Standard | Male | Adult | Character, Street, Energetic | | `traffic-cop` | Traffic Cop | Firm authority with dry chacha-style wit. | Standard | Male | Adult | Character, Funny, Street | | `khwajasara` | Khwajasara | Androgynous Pakistani character voice — warm, wry, dignified delivery. | Standard | — | Adult | Character, Community, Emotional | | `dha-teen-girl` | DHA Teen Girl | Burger-Urdu teen girl — dramatic, expressive delivery. | Burger | Female | Teen | Character, Funny, Youth | | `dha-fitness-devotee` | DHA Fitness Devotee | Self-aware burger-Urdu fitness voice. | Burger | Female | Young | Character, Lifestyle, Funny | | `dha-hostess` | DHA Hostess | Polished mid-40s burger-Urdu — composed delivery. | Burger | Female | Adult | Character, Dramatic | | `nazimabad-boy` | Nazimabad Boy | Urdu-medium teen boy — natural delivery. | Standard | Male | Teen | Character, Youth | | `karachi-romeo` | Karachi Romeo | Young male voice — thinks he's a charmer. | Standard | Male | Young | Character, Funny, Street | | `wholesale-trader` | Wholesale Trader | Middle-aged Karachi trader — steady, measured delivery. | Standard | Male | Adult | Character, Professional | | `college-girl` | College Girl | Determined Karachi college-aged female — natural, conversational delivery. | Standard | Female | Teen | Character, Youth | | `gaming-kid` | Gaming Kid | Mischievous 8-year-old male — playful delivery. | Standard | Male | Child | Character, Funny, Youth | | `udaas-aashiq` | Udaas Aashiq | Subdued young male — pre-tears delivery. | Standard | Male | Young | Emotional, Vulnerable | | `heartbroken` | Heartbroken | Young male voice-note — soft, weary, emotional. | Standard | Male | Young | Emotional, Vulnerable | | `udaas-philosopher` | Udaas Philosopher | Slow, drawled male monologue. | Standard | Male | Young | Emotional, Funny, Character | | `mohalla-storyteller` | Mohalla Storyteller | Bihari-accented adult male — corner-shop storyteller cadence. | Bihari | Male | Adult | Narration, Character | | `mohalla-patriarch` | Mohalla Patriarch | Bihari-accented elder male — gruff-but-warm authority. | Bihari | Male | Senior | Character, Authoritative | | `balochi-elder` | Balochi Elder | Balochi-accented elder male — weathered gravity. | Balochi | Male | Senior | Character, Authoritative | | `bengali-businesswoman` | Bengali Businesswoman | Bengali-accented young female — quietly driven entrepreneur voice. | Bengali | Female | Young | Character, Professional | | `punjabi-manager` | Punjabi Manager | Punjabi-accented middle-aged male — steady mid-career professional voice. | Punjabi | Male | Adult | Character, Professional | | `lahori-story-uncle` | Lahori Story Uncle | Late-50s uncle with Punjabi-Urdu accent. Deep voice. | Punjabi | Male | Senior | Character, Warm | | `pashtun-teen` | Pashtun Teen | Pashtun-accented teen boy — sharp-witted youth delivery. | Pashto | Male | Teen | Character, Youth | | `pashtun-woman` | Pashtun Woman | Pashtun-accented young female — pragmatic, community-driven tone. | Pashto | Female | Young | Character, Community | | `pashtun-navigator` | Pashtun Navigator | Pashtun-accented middle-aged male — weathered, steady delivery. | Pashto | Male | Adult | Character, Street | | `sindhi-professional` | Sindhi Professional | Sindhi-accented young female professional — composed, contemporary delivery. | Sindhi | Female | Young | Character, Professional | | `sindhi-networker` | Sindhi Networker | Sindhi-accented young male — easy, friendly community-networker tone. | Sindhi | Male | Young | Character, Community | | `sindhi-navigator` | Sindhi Navigator | Sindhi-accented middle-aged male — weary delivery. | Sindhi | Male | Adult | Character, Emotional | | `memon-trader` | Memon Trader | Memon-accented middle-aged male — methodical wholesale-trader delivery. | Memon | Male | Adult | Character, Professional | | `headmaster` | Headmaster | Authoritative middle-aged male — commanding delivery. | Standard | Male | Adult | Authoritative, Character | | `jalsa-mimic` | Jalsa Mimic | Passionate jalsa speech cadence. | Standard | Male | Adult | Funny, Character, Entertainment | | `street-vlogger` | Street Vlogger | Karachi street vlogger — warm female storytelling with on-the-ground energy. | Standard | Female | Young | Entertainment, Narration, Warm | | `biryani-reviewer` | Biryani Reviewer | Energetic male food vlogger — emphatic, hungry delivery. | Standard | Male | Young | Entertainment, Funny, Energetic | | `female-debater` | Female Debater | Sharp, fast female debate cadence. | Standard | Female | Young | Dramatic, Energetic, Public Speaking | | `male-debater` | Male Debater | Incisive male debate voice. | Standard | Male | Young | Dramatic, Energetic, Public Speaking | ## Using a voice Pass the `voiceId` from any card above into a synthesis request. ```javascript theme={null} await fetch("https://api.upliftai.org/v1/synthesis/text-to-speech", { method: "POST", headers: { "Authorization": "Bearer sk_api_your_key_here", "Content-Type": "application/json" }, body: JSON.stringify({ voiceId: "jinn", text: "میں نے تمہیں پکارا تھا، صدیوں پہلے۔", outputFormat: "MP3_22050_128" }) }) ``` ## Earlier voices These remain available for existing integrations. | voiceId | Voice | | - | - | | `v_meklc281` | Urdu — Info/Education V2 | | `v_8eelc901` | Urdu — Info/Education | | `v_30s70t3a` | Urdu — Nostalgic News | | `v_yypgzenx` | Urdu — Dada Jee | | `v_kwmp7zxt` | Urdu — Gen Z (beta) | | `v_sd0kl3m9` | Sindhi — Female | | `v_sd6mn4p2` | Sindhi — Male Calm | | `v_sd9qr7x5` | Sindhi — Male News | | `v_bl0ab8c4` | Balochi — Male | | `v_bl1de2f7` | Balochi — Female | ## Tell us what you're building Reach us at [founders@upliftai.org](mailto:founders@upliftai.org) — we'd love to help you ship. # Error Handling Source: https://docs.upliftai.org/sdk/nodejs/error-handling Typed errors and retry behavior in the Node.js SDK ## Error Classes The SDK throws typed errors so you can handle specific failure cases. All errors include a `requestId` for debugging. ```typescript theme={null} import UpliftAI, { UpliftAIError, UpliftAIAuthError, // 401 UpliftAIInsufficientBalanceError, // 402 UpliftAIRateLimitError, // 429 } from '@upliftai/sdk-js'; const client = new UpliftAI({ apiKey: 'your-api-key', }); try { const { audio } = await client.tts.create({ text: 'ٹیسٹ', voiceId: 'v_meklc281', }); } catch (err) { if (err instanceof UpliftAIAuthError) { // 401 — invalid or missing API key console.error('Check your API key'); } else if (err instanceof UpliftAIRateLimitError) { // 429 — rate limited (auto-retried based on maxRetries) console.error('Rate limited, back off and retry'); } else if (err instanceof UpliftAIInsufficientBalanceError) { // 402 — top up your account console.error('Insufficient balance'); } else if (err instanceof UpliftAIError) { // Other API errors console.error(err.statusCode, err.code, err.requestId); } } ``` ## Automatic Retries The SDK automatically retries on transient errors with exponential backoff and jitter: | Status Code | Description | Auto-Retried? | | - | - | - | | `408` | Request Timeout | Yes | | `429` | Rate Limited | Yes | | `500` | Internal Server Error | Yes | | `502` | Bad Gateway | Yes | | `503` | Service Unavailable | Yes | | `504` | Gateway Timeout | Yes | | `401` | Unauthorized | No | | `402` | Insufficient Balance | No | | `400` | Bad Request | No | Configure retry behavior via the `maxRetries` client option: ```typescript theme={null} const client = new UpliftAI({ apiKey: 'your-api-key', maxRetries: 3, // default is 2 timeout: 60000, // default is 30000ms }); ``` # Getting Started Source: https://docs.upliftai.org/sdk/nodejs/overview Official Node.js SDK for the Uplift AI API Requires **Node.js 18+**. Install from [npm](https://www.npmjs.com/package/@upliftai/sdk-js). ## Installation ```bash theme={null} npm install @upliftai/sdk-js ``` ## Configuration ```typescript theme={null} import UpliftAI from '@upliftai/sdk-js'; const client = new UpliftAI({ apiKey: 'your-api-key', }); ``` Or use an environment variable: ```bash theme={null} export UPLIFTAI_API_KEY=sk_... ``` ```typescript theme={null} const client = new UpliftAI(); // picks up from env ``` ### Client Options | Option | Type | Default | Description | | - | - | - | - | | `apiKey` | `string` | `UPLIFTAI_API_KEY` env | Your API key | | `baseUrl` | `string` | `https://api.upliftai.org` | API base URL | | `timeout` | `number` | `30000` | Request timeout (ms) | | `maxRetries` | `number` | `2` | Auto-retries on 429/5xx errors | ## Quick Example ```typescript theme={null} import UpliftAI from '@upliftai/sdk-js'; import { writeFileSync } from 'fs'; const client = new UpliftAI({ apiKey: 'your-api-key', }); const { audio, metadata } = await client.tts.create({ text: 'السلام علیکم، یہ ایک ٹیسٹ ہے', voiceId: 'v_meklc281', outputFormat: 'MP3_22050_128', }); writeFileSync('output.mp3', audio); console.log(`Audio size: ${audio.length} bytes`); ``` ## Available Methods Generate complete audio buffers HTTP streaming for low-latency playback Background jobs with pre-signed URLs Real-time multiplexed streaming Transcribe audio files Control pronunciation of specific words ## Output Formats | Format | Identifier | Use Case | | - | - | - | | WAV | `WAV_22050_32` · `WAV_22050_16` | Highest quality, larger files | | MP3 | `MP3_22050_128` · `MP3_22050_64` · `MP3_22050_32` | Compressed, good for web | | PCM | `PCM_22050_16` | Raw audio, WebSocket default | | ULAW | `ULAW_8000_8` | Telephony (Twilio, SIP, PSTN) | # Phrase Replacements Source: https://docs.upliftai.org/sdk/nodejs/phrase-replacements Control pronunciation of brand names, technical terms, and LLM outputs ## Overview Phrase replacements let you control how specific words and phrases are pronounced. This is useful for: * **Brand names** — convert English spellings to Urdu phonetics * **Technical terms** — ensure consistent pronunciation * **LLM outputs** — fix common misspellings from AI models * **Regional variations** — adapt to local dialects ## `client.tts.phraseReplacements.create(config)` Create a reusable phrase replacement configuration. ```typescript theme={null} import UpliftAI from '@upliftai/sdk-js'; const client = new UpliftAI({ apiKey: 'your-api-key', }); const config = await client.tts.phraseReplacements.create({ phraseReplacements: [ { phrase: 'Meezan bank', replacement: 'میزان بینک' }, { phrase: 'سیونگز اکاؤنٹ', replacement: 'savings account' }, // LLMs sometimes misspell Urdu — "Isabion" is pronounced "Izabiyan" { phrase: 'Isabion', replacement: 'ایزابیان' }, ], }); console.log('Config ID:', config.configId); ``` ## Using in TTS Requests Pass the `configId` to any TTS method: ```typescript theme={null} const { audio } = await client.tts.create({ text: 'Meezan Bank اعتماد کا ضامن', voiceId: 'v_meklc281', phraseReplacementConfigId: config.configId, }); ``` ## `client.tts.phraseReplacements.list()` List all your phrase replacement configurations. ```typescript theme={null} const configs = await client.tts.phraseReplacements.list(); ``` ## `client.tts.phraseReplacements.get(configId)` Fetch a specific configuration by ID. ```typescript theme={null} const config = await client.tts.phraseReplacements.get('config_abc123'); ``` ## `client.tts.phraseReplacements.update(configId, config)` Update an existing configuration. ```typescript theme={null} await client.tts.phraseReplacements.update('config_abc123', [ { phrase: 'API', replacement: 'اے پی آئی' }, { phrase: 'SDK', replacement: 'ایس ڈی کے' }, ]); ``` # Speech to Text Source: https://docs.upliftai.org/sdk/nodejs/speech-to-text Transcribe audio files with client.stt.transcribe() ## `client.stt.transcribe(request)` Transcribe audio to text. Accepts a file path, `Buffer`, or readable stream. ### From File Path ```typescript theme={null} import UpliftAI from '@upliftai/sdk-js'; const client = new UpliftAI({ apiKey: 'your-api-key', }); const { transcript } = await client.stt.transcribe({ file: './recording.mp3', model: 'scribe', language: 'ur', }); console.log(transcript); ``` ### From Buffer ```typescript theme={null} import UpliftAI from '@upliftai/sdk-js'; import { readFileSync } from 'fs'; const client = new UpliftAI({ apiKey: 'your-api-key', }); const buffer = readFileSync('./recording.mp3'); const { transcript } = await client.stt.transcribe({ file: buffer, fileName: 'recording.mp3', // required for format detection model: 'scribe', language: 'ur', }); ``` ### Request Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `file` | `string · Buffer · ReadableStream` | Yes | Audio input — file path, buffer, or stream | | `fileName` | `string` | When using Buffer/Stream | File name for format detection | | `model` | `'scribe' · 'scribe-mini'` | No | Transcription model (default: `scribe`) | | `language` | `string` | No | Language code (e.g. `ur` for Urdu) | | `domain` | `'phone-commerce' · 'farming'` | No | Domain-specific model for better accuracy | ### Response | Field | Type | Description | | - | - | - | | `transcript` | `string` | The transcribed text | # Text to Speech Source: https://docs.upliftai.org/sdk/nodejs/text-to-speech Generate complete audio buffers with client.tts.create() ## `client.tts.create(request)` Returns the full audio buffer in a single response. Best for batch/offline use where you don't need real-time playback. ```typescript theme={null} import UpliftAI from '@upliftai/sdk-js'; import { writeFileSync } from 'fs'; const client = new UpliftAI({ apiKey: 'your-api-key', }); const { audio, metadata } = await client.tts.create({ text: 'السلام علیکم، یہ ایک ٹیسٹ ہے', voiceId: 'v_meklc281', outputFormat: 'MP3_22050_128', }); writeFileSync('output.mp3', audio); console.log('Metadata:', metadata); console.log(`Audio size: ${audio.length} bytes`); ``` ### Request Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `text` | `string` | Yes | Text to synthesize (Urdu, English, or mixed) | | `voiceId` | `string` | Yes | Voice profile ID (e.g. `v_meklc281`) | | `outputFormat` | `string` | No | Audio format — defaults to `WAV_22050_32` | | `phraseReplacementConfigId` | `string` | No | ID from [phrase replacements](/sdk/nodejs/phrase-replacements) | ### Response | Field | Type | Description | | - | - | - | | `audio` | `Buffer` | Complete audio data | | `metadata.requestId` | `string` | Request ID for debugging | | `metadata.contentType` | `string` | MIME type of the audio | | `metadata.sampleRate` | `number` | Audio sample rate | # Async Text to Speech Source: https://docs.upliftai.org/sdk/nodejs/text-to-speech-async Background TTS jobs with pre-signed URLs using client.tts.enqueue() ## `client.tts.enqueue(request)` Enqueue a TTS job and retrieve the audio later. Returns a `temporaryUrl` you can pass directly to a frontend, WhatsApp, or `