curl --location --request POST 'https://api.inworld.ai/voices/v1/pvcVoices/<voice-id>:train' \
--header "Authorization: Basic $INWORLD_API_KEY" \
--header 'Content-Type: application/json' \
--data '{}'import requests
voice_id = "<voice-id>"
url = f"https://api.inworld.ai/voices/v1/pvcVoices/{voice_id}:train"
headers = {
"Authorization": "Basic <api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, headers=headers, json={})
print(response.json())const voiceId = '<voice-id>';
const url = `https://api.inworld.ai/voices/v1/pvcVoices/${voiceId}:train`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': 'Basic <api-key>',
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data);{
"name": "workspaces/your_workspace_id/pvcVoices/my-professional-voice",
"voiceId": "my-professional-voice",
"displayName": "my-professional-voice",
"languageCode": "en-US",
"state": "PVC_VOICE_STATE_QUEUED",
"incarnationId": "a1b2c3d4",
"createTime": "2026-08-31T12:00:00Z",
"updateTime": "2026-08-31T12:10:00Z"
}{
"code": 3,
"message": "invalid request: at least 600 seconds of sample audio is required to train, got 214",
"details": []
}{
"code": 123,
"message": "<string>",
"details": [
{
"@type": "<string>"
}
]
}{
"code": 123,
"message": "<string>",
"details": [
{
"@type": "<string>"
}
]
}Train a PVC voice
Starts training on a draft voice’s uploaded samples.
Returns the voice resource itself, not a long-running Operation — poll Get a PVC voice to track progress through PVC_VOICE_STATE_QUEUED → PVC_VOICE_STATE_TRAINING → PVC_VOICE_STATE_READY (or PVC_VOICE_STATE_FAILED).
Calling Train again while already queued or training is a no-op and returns the voice unchanged.
curl --location --request POST 'https://api.inworld.ai/voices/v1/pvcVoices/<voice-id>:train' \
--header "Authorization: Basic $INWORLD_API_KEY" \
--header 'Content-Type: application/json' \
--data '{}'import requests
voice_id = "<voice-id>"
url = f"https://api.inworld.ai/voices/v1/pvcVoices/{voice_id}:train"
headers = {
"Authorization": "Basic <api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, headers=headers, json={})
print(response.json())const voiceId = '<voice-id>';
const url = `https://api.inworld.ai/voices/v1/pvcVoices/${voiceId}:train`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': 'Basic <api-key>',
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data);{
"name": "workspaces/your_workspace_id/pvcVoices/my-professional-voice",
"voiceId": "my-professional-voice",
"displayName": "my-professional-voice",
"languageCode": "en-US",
"state": "PVC_VOICE_STATE_QUEUED",
"incarnationId": "a1b2c3d4",
"createTime": "2026-08-31T12:00:00Z",
"updateTime": "2026-08-31T12:10:00Z"
}{
"code": 3,
"message": "invalid request: at least 600 seconds of sample audio is required to train, got 214",
"details": []
}{
"code": 123,
"message": "<string>",
"details": [
{
"@type": "<string>"
}
]
}{
"code": 123,
"message": "<string>",
"details": [
{
"@type": "<string>"
}
]
}state: "PVC_VOICE_STATE_QUEUED"). Poll Get a PVC voice to track progress — there’s no separate operation ID to look up.PVC_VOICE_STATE_QUEUED or PVC_VOICE_STATE_TRAINING is a no-op — it returns the current voice unchanged rather than starting a second run.
Training starts are rate-limited per plan; exceeding your plan’s rate returns 429.
How long training takes
Wall time scales with how much audio you uploaded, not how many samples it’s split across. As a rough guide, training an hour of audio takes on the order of 10-30 minutes; expect it to take longer under heavy platform demand.Authorizations
Your API key. Read permissions are required for GET endpoints. Write permissions are required for POST, PATCH, and DELETE endpoints.
For Basic authentication, please populate Basic $INWORLD_API_KEY. You can create a key in one command with the Inworld CLI: inworld workspace add-key.
Path Parameters
Voice ID of the draft PVC voice to train.
Body
The body is of type object.
Response
A successful response.
A Professional Voice Clone resource.
Resource name. Format: workspaces/{workspace}/pvcVoices/{voice}.
Voice ID, derived from displayName at creation time. Use this value as {voiceId} on every other PVC endpoint, and as the voiceId in TTS synthesis requests once the voice is PVC_VOICE_STATE_READY.
The human-readable name shown anywhere the voice is listed or selected.
The voice's language as a BCP-47-shaped locale string, e.g. en-US. Immutable after creation.
Lifecycle state of a PVC voice.
PVC_VOICE_STATE_DRAFT: Editable. Samples can be added, trimmed, or removed, and metadata can be updated.PVC_VOICE_STATE_QUEUED: Training requested; waiting for a training slot.PVC_VOICE_STATE_TRAINING: Actively training.PVC_VOICE_STATE_READY: Training succeeded. Usable for TTS synthesis; permanent — cannot be deleted through this API.PVC_VOICE_STATE_FAILED: Training failed. Editing the voice (e.g. renaming it, or adding/removing a sample) returns it toPVC_VOICE_STATE_DRAFTwith its remaining samples intact.
PVC_VOICE_STATE_UNSPECIFIED, PVC_VOICE_STATE_DRAFT, PVC_VOICE_STATE_QUEUED, PVC_VOICE_STATE_TRAINING, PVC_VOICE_STATE_READY, PVC_VOICE_STATE_FAILED Populated on a PVC voice when its state is PVC_VOICE_STATE_FAILED.
Show child attributes
Show child attributes
Identifier that stays stable across edits to the same voice, and changes each time it is retrained. Use it to tell two reads of the same voiceId apart across a retrain.
Audio samples currently attached to the voice.
Show child attributes
Show child attributes