> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inworld.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a PVC voice

> Updates editable metadata on a PVC voice. Editing a `PVC_VOICE_STATE_FAILED` voice returns it to `PVC_VOICE_STATE_DRAFT` and preserves its samples, so you can fix the issue and retrain. A voice that is queued, training, or ready cannot be updated. Retry after it finishes, or after re-editing brings a failed voice back to draft.

You can update `displayName`, `description`, and `tags`. Without an `updateMask`, every populated field in the body is applied — pass a mask to change only the named fields. Use **snake_case** field paths, for example `?updateMask=display_name,tags`. A mask that names a field that cannot be changed returns `400`.

<Note>
**Editing a failed voice.** You can [add or remove samples](https://docs.inworld.ai/api-reference/pvcAPI/pvcvoiceservice/upload-pvc-voice-samples.md) and [retrain](https://docs.inworld.ai/api-reference/pvcAPI/pvcvoiceservice/train-pvc-voice.md) without uploading everything again.
</Note>

<Warning>
This endpoint returns `409` while the voice is queued, training, or ready.
</Warning>

## API reference

**Endpoint:** `PATCH https://api.inworld.ai/voices/v1/pvcVoices/{pvcVoiceId}`

### Authorization

- `Authorization` (string; required) — Your [API key](../../../api-reference/introduction). 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](../../../developer-tools/inworld-cli): `inworld workspace add-key`.

### Path parameters

- `pvcVoiceId` (string; required) — `pvcVoiceId` of the PVC voice to update.

### Query parameters

- `updateMask` (string) — Comma-separated list of fields to update. When omitted, every populated field in the request body is applied, overwriting those fields. When supplied, only the named fields are applied, and fields present in the body but omitted from the mask are ignored.
  
  Mask paths use **snake_case** field names, even though the request body uses camelCase. Updatable paths: `display_name`, `description`, `tags`; a mask that names a field that cannot be changed returns `400`.

### Request body

Content type: `application/json`

- `displayName` (string) — The voice's new display name. When sending an `updateMask`, include `display_name` (snake_case) in it to apply this field.
- `description` (string) — The voice's new description. When sending an `updateMask`, include `description` in it to apply this field.
- `tags` (string[]) — The voice's new tags, replacing the existing ones. When sending an `updateMask`, include `tags` in it to apply this field.

#### Request example

```json
{
  "displayName": "renamed-professional-voice"
}
```

### Response

Status: `200`. Content type: `application/json`.

- `name` (string) — Resource name. Format: `workspaces/{workspace}/pvcVoices/{voice}`.
- `pvcVoiceId` (string) — PVC voice ID, derived from `displayName` at creation time. Use this value as `{pvcVoiceId}` on every other PVC endpoint. It is not the voice ID used for synthesis; see `customVoice`.
- `displayName` (string) — The human-readable name shown anywhere the voice is listed or selected.
- `description` (string) — Description of the voice.
- `tags` (string[]) — User-defined tags.
- `langCode` (enum<string>; options: "EN_US", "ZH_CN", "KO_KR", "JA_JP", "RU_RU", "AUTO", "IT_IT", "ES_ES", "PT_BR", "DE_DE", "FR_FR", "AR_SA", "PL_PL", "NL_NL", "HI_IN", "HE_IL") — Legacy enum form of the voice's language, such as `EN_US`. Returned alongside `languageCode`. Immutable after creation.
- `languageCode` (string) — The voice's language as a BCP-47-shaped locale string, e.g. `en-US`. Immutable after creation.
- `state` (enum<string>; options: "PVC_VOICE_STATE_UNSPECIFIED", "PVC_VOICE_STATE_DRAFT", "PVC_VOICE_STATE_QUEUED", "PVC_VOICE_STATE_TRAINING", "PVC_VOICE_STATE_READY", "PVC_VOICE_STATE_FAILED") — 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. It's permanent and 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 to `PVC_VOICE_STATE_DRAFT` with its remaining samples intact.
- `failure` (object) — Populated on a PVC voice when its `state` is `PVC_VOICE_STATE_FAILED`.
  - `reason` (string) — Machine-readable failure code, such as `PVC_VOICE_FAILURE_REASON_INVALID_SAMPLES`. New values may be added over time, so don't validate against a hardcoded list. Fall back to displaying `message` for codes you don't recognize.
  - `message` (string) — Human-readable, scrubbed failure message. Never contains uploaded audio, filenames, or transcripts.
- `customVoice` (string) — Resource name of the trained voice, in the format `workspaces/{workspace}/voices/{voice}`. Set once the voice is `PVC_VOICE_STATE_READY`. Manage and use the trained voice through the Voices API.
- `samples` (object[]) — Audio samples currently attached to the voice.
  - `sampleId` (string) — Sample ID. Use this value as `{sampleId}` when trimming or deleting the sample.
  - `name` (string) — Resource name. Format: `workspaces/{workspace}/pvcVoices/{voice}/samples/{sample}`.
  - `fileName` (string) — Original file name of the uploaded audio.
  - `sizeBytes` (string) — Size of the uploaded file, in bytes. Encoded as a string, like other 64-bit integers in this API.
  - `durationSecs` (number) — Analyzed duration of the sample, in seconds, before any trim is applied.
  - `mimeType` (enum<string>; options: "audio/wav", "audio/webm", "audio/mpeg") — Detected audio format, sniffed from the file's byte content.
  - `hash` (string) — Base64-encoded MD5 of the stored object, for verifying upload integrity against the source file.
  - `removeBackgroundNoise` (boolean) — Whether background-noise removal is applied to this sample during training, as set by `remove_background_noise` on upload.
  - `trimStartMs` (integer) — Trim start offset in milliseconds, if set.
  - `trimEndMs` (integer) — Trim end offset in milliseconds, if set.
  - `role` (enum<string>; options: "PVC_SAMPLE_ROLE_UNSPECIFIED", "PVC_SAMPLE_ROLE_TRAINING", "PVC_SAMPLE_ROLE_GOLDEN", "PVC_SAMPLE_ROLE_AVOID") — How the sample is used in training. `PVC_SAMPLE_ROLE_UNSPECIFIED` is treated as `PVC_SAMPLE_ROLE_TRAINING` (ordinary training material). `PVC_SAMPLE_ROLE_GOLDEN` marks a positive reference whose character training should prefer; `PVC_SAMPLE_ROLE_AVOID` marks an advisory negative reference. New values may be added over time.
- `createTime` (string)
- `updateTime` (string)

### Response examples

#### 200: default_response

```json
{
  "name": "string",
  "pvcVoiceId": "string",
  "displayName": "string",
  "description": "string",
  "tags": [
    "string"
  ],
  "langCode": "EN_US",
  "languageCode": "string",
  "state": "PVC_VOICE_STATE_UNSPECIFIED",
  "failure": {
    "reason": "string",
    "message": "string"
  },
  "customVoice": "string",
  "samples": [
    {
      "sampleId": "string",
      "name": "string",
      "fileName": "string",
      "sizeBytes": "string",
      "durationSecs": 0,
      "mimeType": "audio/wav",
      "hash": "string",
      "removeBackgroundNoise": true,
      "trimStartMs": 0,
      "trimEndMs": 0,
      "role": "PVC_SAMPLE_ROLE_UNSPECIFIED"
    }
  ],
  "createTime": "string",
  "updateTime": "string"
}
```

#### 400: immutable_mask

```json
{
  "code": 3,
  "message": "update_mask contains fields that cannot be changed",
  "details": []
}
```

#### 409: frozen

```json
{
  "code": 9,
  "message": "PVC voice cannot be modified while it is training",
  "details": []
}
```

#### default: default_response

```json
{
  "code": 0,
  "message": "string",
  "details": [
    {
      "@type": "string"
    }
  ]
}
```

### Code examples

#### cURL

```bash
curl --location --request PATCH 'https://api.inworld.ai/voices/v1/pvcVoices/<pvc-voice-id>?updateMask=display_name' \
--header "Authorization: Basic $INWORLD_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
  "displayName": "renamed-professional-voice"
}'
```

#### Python

```python
import requests

pvc_voice_id = "<pvc-voice-id>"
url = f"https://api.inworld.ai/voices/v1/pvcVoices/{pvc_voice_id}"
headers = {
    "Authorization": "Basic <api-key>",
    "Content-Type": "application/json"
}
params = {"updateMask": "display_name"}
payload = {"displayName": "renamed-professional-voice"}

response = requests.patch(url, headers=headers, params=params, json=payload)
print(response.json())
```

#### JavaScript

```javascript
const pvcVoiceId = '<pvc-voice-id>';
const url = `https://api.inworld.ai/voices/v1/pvcVoices/${pvcVoiceId}?updateMask=display_name`;

const response = await fetch(url, {
  method: 'PATCH',
  headers: {
    'Authorization': 'Basic <api-key>',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ displayName: 'renamed-professional-voice' }),
});

const data = await response.json();
console.log(data);
```
