Artifact type: API reference documentation
Audience: Software developers integrating with the API
Role: Documentation author
Note: This document is a writing sample. Details have been fictionalized for portfolio use.
Voice Render API — Create a voice job
Version 1 is the current stable release. Version 2 is available with a restructured output schema; see Versioning and migration.
Overview
Use this endpoint to convert text into speech as a single synthesis job. The API validates the request, applies policy checks, reserves quota, and enqueues a rendering job. On success, it returns 201 Created and a job_id in the response body, which you can use to retrieve the synthesized audio.
For the machine-readable OpenAPI 3 definition of this endpoint, see OpenAPI 3 — Create voice job. The Interactive OpenAPI Reference presents the same specification in Scalar.
Endpoint
POST https://api.example.com/v1/voice/jobs
Content-Type: application/json
Authentication
All requests must include a valid access token.
Header: Authorization: Bearer <token>
Tokens are issued by your identity provider. If the token is missing, invalid, or expired, the API returns 401 (missing or invalid access token). The response body uses the error envelope in Errors; error.code is unauthenticated.
Idempotency
This endpoint supports idempotency to prevent duplicate job creation and duplicate quota charges during retries.
Header: Idempotency-Key: <uuid>
Send a UUID in Idempotency-Key. Use a new UUID for each new logical job; reuse the same UUID when retrying the same request after a timeout or network error.
Idempotency rules
- If you retry the same request (for example, due to a timeout), reuse the same
Idempotency-Key. - If the same key is reused with a different request body, the API returns
409 idempotency_conflict. - Idempotency keys are retained for 24 hours.
- If
Idempotency-Keyis missing, empty, or not a UUID v4, the API returns400withinvalid_input. - If
Content-Typeis missing or notapplication/json, the API returns400withinvalid_input.
Request
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <token> where <token> is a valid access token issued by your identity provider. |
Idempotency-Key | Yes | UUID (v4) used to prevent duplicate job creation during retries. Must be a valid UUID v4; missing or malformed values return 400 with invalid_input. |
Content-Type | Yes | Must be application/json. |
Body schema
| Field | Type | Required | Description |
|---|---|---|---|
input | object | Yes | Input content to synthesize. |
input.content | string | Yes | Minimum length: 1 character. Maximum length: 2,000 characters. |
input.content_type | string | Yes | Must be text/plain. |
voice | object | Yes | Voice selection and rendering options. |
voice.profile_id | string | Yes | Voice profile ID (for example, voice_en_us_001). Retrieve available profiles using GET /v1/voice/profiles. |
voice.style | string | No | Rendering style. Allowed values are defined per voice profile. Retrieve a profile (including its supported styles) with GET /v1/voice/profiles/{profile_id}, or list all profiles with GET /v1/voice/profiles. Default: neutral. |
voice.rate | number | No | Speed multiplier. Range: 0.5–2.0. Default: 1.0. |
output | object | Yes | Output formatting configuration. |
output.format | string | Yes | Audio output format. Currently supports: pcm_s16le. |
output.sample_rate_hz | integer | Yes | Sample rate in Hz. Must be one of: 8000, 16000, 22050, 24000, 44100. Other values return 400 with invalid_input. |
client | object | No | Optional metadata for correlation. |
client.request_tag | string | No | Optional client-defined correlation string. Max length: 128 characters; UTF-8; no structural format required. If provided, the value is echoed on the job resource returned by GET /v1/voice/jobs/{job_id}. |
Input validation and limits
Plan limits
- Maximum input length may vary by plan.
- Requests exceeding configured limits return
400withinvalid_input.
Character and encoding rules
- Input must be valid UTF-8.
- Most Unicode characters, including emoji, are accepted.
- Invalid byte sequences return
400withinvalid_input. - Rendering quality for emoji or uncommon symbols may vary by voice profile.
- Avoid sending control characters outside standard whitespace.
Standard whitespace includes tab (\t), newline (\n), and carriage return (\r). Other control characters, such as null bytes, may cause validation errors or unpredictable rendering behavior.
Example request (curl)
The request body must be valid JSON.
curl https://api.example.com/v1/voice/jobs \
-H "Authorization: Bearer <token>" \
-H "Idempotency-Key: 8b3f1c8e-3f2b-4ad2-9ef0-2a8c9b2a4f19" \
-H "Content-Type: application/json" \
-d '{
"input": {
"content": "Hello, world.",
"content_type": "text/plain"
},
"voice": {
"profile_id": "voice_en_us_001",
"style": "neutral",
"rate": 1.0
},
"output": {
"format": "pcm_s16le",
"sample_rate_hz": 24000
},
"client": {
"request_tag": "msg-001"
}
}'
Response
On success, the API returns 201 Created and a JSON body containing the created job identifier.
Response body
| Field | Type | Notes |
|---|---|---|
job_id | string | UUID (v4) identifying the created job |
Response headers
| Header | Meaning |
|---|---|
X-Quota-Limit | Quota limit for the current billing period (plan cap) |
X-Quota-Remaining | Remaining quota for the current billing period after this request |
X-Request-Id | Unique server-generated identifier for this request |
Example response
{
"job_id": "5b2a5b7a-6c2d-4d40-9e7b-1f25c08e6a1f"
}
Job lifecycle
Voice jobs are processed asynchronously. After creating a job, poll GET /v1/voice/jobs/{job_id} until the job reaches a terminal state.
Job status values
| Status | Meaning |
|---|---|
pending | Accepted and queued; not yet processing. |
processing | Synthesis in progress. |
completed | Succeeded; audio is available via GET /v1/voice/jobs/{job_id}/audio. |
failed | Terminal failure; see the job object's error fields for code, message, and retryable (same semantics as create-time error responses). |
Most jobs complete within a few seconds under normal load.
Retention and expiration
Completed job metadata and audio are retained for a limited period after the job reaches completed. After expiration, retrieval may return 404. Failed jobs follow the same retention window unless your plan states otherwise.
Where to look next
For the full job JSON shape (including request_tag echo and failure detail), see GET /v1/voice/jobs/{job_id} and the Related endpoints list below.
Errors
Errors are returned using a consistent JSON envelope.
{
"error": {
"code": "string",
"message": "string",
"retryable": false
}
}
Error codes
| HTTP status | error.code | Meaning | Retry guidance |
|---|---|---|---|
| 400 | invalid_input | Request is invalid: missing or malformed headers (for example, missing Idempotency-Key or wrong Content-Type), invalid JSON body, missing fields, wrong types, constraint violations, plan limits, invalid UTF-8, or unsupported output parameters. | Do not retry without correcting the request |
| 401 | unauthenticated | Missing or invalid access token | Do not retry until authentication is corrected |
| 402 | quota_exceeded | Insufficient quota to create the job | Do not retry until quota is restored |
| 403 | policy_blocked | Content rejected by policy | Do not retry with the same content |
| 404 | not_found | Job or audio not found, unknown job_id, or resource expired or no longer available (typical on GET /v1/voice/jobs/{job_id} or GET /v1/voice/jobs/{job_id}/audio; not a normal response from this POST create endpoint) | Do not retry blindly; verify job_id, timing, and retention |
| 409 | idempotency_conflict | Same Idempotency-Key reused with a different request body | Generate a new key for a new request |
| 429 | rate_limited | Too many requests in a short period | Retry with backoff and honor Retry-After if present |
| 503 | service_unavailable | Temporary service or upstream provider failure | Retry with exponential backoff |
Example error response
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Please retry after a short delay.",
"retryable": true
}
}
Versioning and migration
This API uses URI versioning (/v1/...). Breaking changes are introduced in new major versions.
Minor, backward-compatible additions (such as new optional fields) do not require a version change.
Version 2 changes
Version 2 restructures the output schema to support future audio formats and extensibility.
In v1:
output.formatoutput.sample_rate_hz
In v2:
output.audio.formatoutput.audio.sample_rate_hzoutput.audio.channels(new, default:1)
The new structure allows the introduction of additional output types without breaking top-level schema.
No behavioral changes were made to authentication, idempotency semantics, or retry handling.
Migrating from v1 to v2
-
Update the endpoint path to
POST /v2/voice/jobs. -
Update the request body:
- Move
output.formattooutput.audio.format. - Move
output.sample_rate_hztooutput.audio.sample_rate_hz. - Optionally specify
output.audio.channels.
- Move
-
Re-run integration tests to confirm:
- Idempotency behavior remains unchanged.
- Retry handling for
429and503remains unchanged.
v1 remains available during the deprecation window to support gradual client migration.
Related endpoints
-
GET /v1/voice/jobs/{job_id}
Retrieve the current status and metadata for a voice job. -
GET /v1/voice/jobs/{job_id}/audio
Retrieve the rendered audio output for a completed job. -
GET /v1/quota
Retrieve current quota limits and remaining usage for the authenticated account.