# Generate speech (single voice)

> `POST /job/add/paperoffice_voice___tts` · Section: Media AI — Voice · PaperOffice AI API, release `R20261020.2258`.

`POST https://api.paperoffice.ai/latest/job/add/paperoffice_voice___tts`

## Overview
Convert text to natural speech using a **single catalog voice** (PaperOffice neural TTS).

Use this endpoint when you need one speaker, a fixed voice from the catalog, and audio in the same response (`output=inline`) or as JSON (`base64` / `url`).

**Pipeline:** `paperoffice_voice___tts` (fixed URL path).

## Prerequisites
1. Call **`POST /voice/voices`** and pick a `name` from the language bucket you need.
2. Default demo pair: **`Abigail`** with **`language=en`** (English, female).
3. Each voice belongs to one catalog language (e.g. `Anneliese` → `de`). If `language` does not match the voice, **`language` wins**: the API substitutes the default female catalog voice for that language (e.g. `Abigail` + `language=de` → `Anneliese`) and returns `warnings[]`. Omit `language` (or send `auto`) to keep the catalog language of the chosen voice.

## Request parameters
| Parameter | Required | Default | Notes |
|-----------|----------|---------|-------|
| `text` | yes | *(showroom demo)* | Up to 50,000 characters. Default script uses emotion markers and a reference ID (`SO-24091`). |
| `voice` | yes | `Abigail` | Name from `POST /voice/voices`. Same bucket as `language` avoids `warnings[]`; on mismatch `language` wins (voice substituted, e.g. Abigail + de → Anneliese). |
| `language` | yes | `en` | Catalog bucket code (e.g. `en`, `de`). Wins on mismatch with `voice`; omit or `auto` keeps the voice catalog language. |
| `quality` | no | `premium` | `basic` (fast), `premium` (balanced), `ultra` (best). |
| `output_format` | no | `mp3` | `mp3`, `wav`, `ogg`. |
| `output` | no | `inline` | `inline` = audio stream in HTTP body; `base64` / `url` = JSON. |
| `processing_lane` | no | workspace / fair-use | Start-SLA wait-to-start: `no_sla`\|`sla_24h`\|`sla_12h`\|`sla_6h`\|`sla_1h`\|`instant`. Legacy `priority` is compat only. |

**Text preparation (TTS):** When `language=de`, dates and common German business identifiers (order IDs, invoice refs) in `text` are normalized for natural speech. Other languages use standard number/date handling for the selected `language`.

## Connection hold & polling (about 5 minute limit)

This is separate from **Start-SLA** (`processing_lane`, wait-to-start / job price factor).

- **Default (`client_wait` omitted or `true`):** The API holds **this HTTP connection** for up to **about 5 minutes (about 295 seconds)** — platform connection limit — and returns the finished result when ready.
- **Hard timeout:** If the job is not done within that window → **HTTP 202** with `job_id` and `poll_url`. **Continue polling** `GET /job/get/{job_id}` every 5–10 seconds until `job_status` is `completed` or `failed`.
- **Immediate poll mode:** `client_wait=false` or `async_only=true` → `job_id` in about 100ms, then poll as above.

Job processing time can exceed 5 minutes; only the **HTTP wait** is capped.

## Start-SLA / processing_lane — Voice

- **Omitted / unchecked in Postman** → workspace `default_processing_lane` (else fair-use `no_sla`).
- Lanes: `no_sla` | `sla_24h` | `sla_12h` | `sla_6h` | `sla_1h` | `instant` (job price ×1 … ×5).
- **`instant`** → interactive / sync-capable start (UI wait jobs).
- Does **not** change the about 5 minute connection hold or when HTTP 202 is returned.
- Legacy numeric `priority` remains for compatibility; **`processing_lane` wins** when both are sent.

See collection guide **Start-SLA (`processing_lane`) — wait-to-start** for tier tables (pricing is not duplicated in this request doc).

## Best practices
1. **First try in Postman:** Send the prefilled showroom script (`[happy]`, reference **SO-24091**, `voice=Abigail`, `language=en`, `output=inline`) and press **Send** to hear emotions and ID normalization.
2. List voices with `POST /voice/voices` before your first TTS job.
3. Keep `voice` and `language` in the **same bucket** to avoid `warnings[]`.
4. Leave `priority` **unchecked** in Postman unless you intentionally want a higher queue tier.
5. Use `output=inline` for quick tests in Postman (audio in the HTTP body); use `url` or `base64` when you need a link or JSON payload.
6. For German business IDs and dates, switch to `language=de` and a German catalog voice (e.g. `Anneliese`) with text like `RG-88421` and `15.03.2026`.
7. After **HTTP 202**, poll `GET /job/get/{job_id}` — do not resubmit the same job unless it failed.
8. For voice cloning or multiple speakers, use the related endpoints below (same pipeline URL).

## Long text (automatic segmentation)
You may send up to **50,000 characters** in one job. The API accepts the full text in one request — **do not** split the text yourself unless you want separate jobs.

## Related

endpoints (same pipeline URL, different body)

| Use case | Postman item |
|----------|----------------|
| Clone voice from audio sample | Clone voice (TTS) — `voice_sample`, no catalog `voice` |
| Multiple speakers in one text | Generate multi-speaker TTS — `voice=Multi` + inline tags |

**Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `text` | string | Yes | Text to convert to speech (max 50,000 characters). Inline emotion markers: [chuckle] [laugh] [sigh] [breath] [whisper] [happy] [excited]. Default demo showcases emotions and reference IDs (e.g. SO-24091). (default: "{see_params_tab}") |
| `voice` | string | Yes | Voice name from POST /voice/voices. Prefer the same language bucket as `language`. On mismatch, language wins: the API substitutes the default female catalog voice for that language (e.g. Abigail + language=de → Anneliese) and returns warnings[]. Default demo: Abigail (en, female). (default: "Abigail") |
| `language` | string | Yes | Language code from the voice catalog (required). Language wins on mismatch with `voice` (voice is substituted). Omit or send auto to keep the voice catalog language. Default: en for Abigail. List pairs via POST /voice/voices. (default: "en") |
| `quality` | string | No | Quality tier: basic (fast), premium (balanced), ultra (best). Default: premium. (allowed: basic, premium, ultra) (default: "premium") |
| `output_format` | string | No | Audio format: mp3 (compressed), wav (lossless), ogg. Default: mp3. (allowed: mp3, wav, ogg) (default: "mp3") |
| `output` | string | No | Response delivery: inline (audio stream, default), base64 (JSON), url (download link in JSON). (allowed: inline, base64, url) (default: "inline") |
| `client_wait` | boolean | No | Connection hold: true (default) = API holds this HTTP request while the job runs, up to the dynamic pipeline timeout (priority, pages, pipeline type; minimum about 20s, infrastructure cap about 295s). Returns the finished result when ready, or HTTP 202 with job_id and poll_url if the hold is exceeded. false = immediate job_id. |
| `async_only` | boolean | No | Same as client_wait=false: immediate job_id — poll GET /job/get/{job_id} every 5–10s. (default: false) |
| `processing_lane` | string | No | Start-SLA lane (wait-to-start guarantee, not completion). Values: no_sla (fair use ×1), sla_24h (×1.5), sla_12h (×2), sla_6h (×3), sla_1h (×4), instant (×5 interactive start). Prefer this over legacy numeric priority. If omitted: workspace default_processing_lane, else no_sla. (allowed: no_sla, sla_24h, sla_12h, sla_6h, sla_1h, instant) |

**Request body:** `multipart/form-data` (or `application/x-www-form-urlencoded`) — use form fields from the parameters table; upload via `file`, `file_1`, or `files` (all accepted) or pass `files` as a JSON array of HTTPS URLs / `source_url` / `file_url`. Do **not** send `application/json` unless the endpoint is listed as JSON-only.

## Responses

Returns JSON with `status: "success"`; key fields: `job_id` (poll `GET /job/get/{job_id}`) or inline `result` when `client_wait=true`; after poll read `result ?? job_result`. Optional `_billing` and `processing_time`. Errors: HTTP 4xx/5xx with `status: "error"`, `code`, and `message`.

## Connection hold & polling

- **Default (`client_wait` omitted or `true`):** The API holds **this HTTP connection** while the job runs, up to the **dynamic pipeline timeout** (priority, pages, pipeline type; minimum about 20s, infrastructure cap about 295s). Returns the finished result when ready.
- **Hold exceeded:** If the job is not finished in time → **HTTP 202** with `job_id`, `poll_url`, `max_wait_seconds`, and `client_wait: false`. The job **continues in the background** — poll `GET /job/get/{job_id}` every 5–10 seconds.
- **Immediate poll mode:** `client_wait=false` or `async_only=true` → `job_id` immediately (about 100ms), then poll as above.

**Response fields:** `client_wait` (boolean) — whether this response used connection hold; `max_wait_seconds` — actual hold duration attempted.

**Completed payload key:** After polling, OCR pipelines return the payload in `job_result`; workflow/IDP (`POST /job/add/workflow`) return it in `result`. Always read `result ?? job_result`. See **Job result schema**.

**Note:** Prefer `processing_lane` Start-SLA for wait-to-start / job price — see collection guide **Start-SLA (`processing_lane`) — wait-to-start**. Legacy `priority` is compat only.

## Essentials (all endpoints)

- **Base URL:** `https://api.paperoffice.ai/latest` · **Auth:** `Authorization: Bearer po_sk_…` or `po_ut_…` (server-side only); browser code uses `po_pk_…` with an allowlisted `Origin`. No Bearer at all on a product API returns HTTP 403 `TIER_RESTRICTED`.
- **Errors:** HTTP 4xx/5xx with `status: "error"`, `code` and `message`. Retry 429 (`Retry-After`) and 5xx with exponential backoff; do not retry validation errors.
- **Jobs:** `client_wait=true` (default) holds the connection. HTTP 202 means the job is still running — poll `GET /job/get/{job_id}` every 5–10 s until `job_status` is `completed`, then read `result ?? job_result`. `processing_lane` sets the Start-SLA; legacy `priority` is not part of the public contract.
- **Billing:** every billable product call costs at least 5 credits; read `_billing.job.credits_billed` on billed responses.
- **Mutations** use `POST` with action paths (e.g. `POST /documents/document-delete`), not HTTP `DELETE`.

---

All endpoints: [llms.txt](https://api.paperoffice.ai/latest/docs/llms.txt) · Everything in one file: [llms-full.txt](https://api.paperoffice.ai/latest/docs/llms-full.txt) · [OpenAPI 3.1](https://api.paperoffice.ai/latest/docs/openapi.json) · This page: https://api.paperoffice.ai/latest/docs/endpoints/po-tts-generate-speech.md

*Generated 2026-10-20 from live API documentation (release `R20261020.2258`).*
