Skip to main content
Portfolio Context

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

info

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-Key is missing, empty, or not a UUID v4, the API returns 400 with invalid_input.
  • If Content-Type is missing or not application/json, the API returns 400 with invalid_input.

Request

Headers

HeaderRequiredDescription
AuthorizationYesBearer <token> where <token> is a valid access token issued by your identity provider.
Idempotency-KeyYesUUID (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-TypeYesMust be application/json.

Body schema

FieldTypeRequiredDescription
inputobjectYesInput content to synthesize.
input.contentstringYesMinimum length: 1 character. Maximum length: 2,000 characters.
input.content_typestringYesMust be text/plain.
voiceobjectYesVoice selection and rendering options.
voice.profile_idstringYesVoice profile ID (for example, voice_en_us_001). Retrieve available profiles using GET /v1/voice/profiles.
voice.stylestringNoRendering 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.ratenumberNoSpeed multiplier. Range: 0.5–2.0. Default: 1.0.
outputobjectYesOutput formatting configuration.
output.formatstringYesAudio output format. Currently supports: pcm_s16le.
output.sample_rate_hzintegerYesSample rate in Hz. Must be one of: 8000, 16000, 22050, 24000, 44100. Other values return 400 with invalid_input.
clientobjectNoOptional metadata for correlation.
client.request_tagstringNoOptional 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 400 with invalid_input.

Character and encoding rules

  • Input must be valid UTF-8.
  • Most Unicode characters, including emoji, are accepted.
  • Invalid byte sequences return 400 with invalid_input.
  • Rendering quality for emoji or uncommon symbols may vary by voice profile.
  • Avoid sending control characters outside standard whitespace.
note

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

FieldTypeNotes
job_idstringUUID (v4) identifying the created job

Response headers

HeaderMeaning
X-Quota-LimitQuota limit for the current billing period (plan cap)
X-Quota-RemainingRemaining quota for the current billing period after this request
X-Request-IdUnique 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

StatusMeaning
pendingAccepted and queued; not yet processing.
processingSynthesis in progress.
completedSucceeded; audio is available via GET /v1/voice/jobs/{job_id}/audio.
failedTerminal 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 statuserror.codeMeaningRetry guidance
400invalid_inputRequest 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
401unauthenticatedMissing or invalid access tokenDo not retry until authentication is corrected
402quota_exceededInsufficient quota to create the jobDo not retry until quota is restored
403policy_blockedContent rejected by policyDo not retry with the same content
404not_foundJob 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
409idempotency_conflictSame Idempotency-Key reused with a different request bodyGenerate a new key for a new request
429rate_limitedToo many requests in a short periodRetry with backoff and honor Retry-After if present
503service_unavailableTemporary service or upstream provider failureRetry 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.format
  • output.sample_rate_hz

In v2:

  • output.audio.format
  • output.audio.sample_rate_hz
  • output.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

  1. Update the endpoint path to POST /v2/voice/jobs.

  2. Update the request body:

    • Move output.format to output.audio.format.
    • Move output.sample_rate_hz to output.audio.sample_rate_hz.
    • Optionally specify output.audio.channels.
  3. Re-run integration tests to confirm:

    • Idempotency behavior remains unchanged.
    • Retry handling for 429 and 503 remains unchanged.

v1 remains available during the deprecation window to support gradual client migration.

  • 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.