version as v2.0.{ status: 200 } means it has been persisted; but each style's binding is lazily rebuilt on the next generate.version is already not v1.0, VOICE_MIGRATE_NOT_NEEDED is returned.code has only three values: VOICE_MIGRATE_NOT_NEEDED, VOICE_MIGRATE_NO_SOURCE, VOICE_MIGRATE_NOT_ALLOWED.VOICE_MIGRATE_NOT_ALLOWED is a parent code with two different-reason sub-codes under it;message copy and never appear in any field of the HTTP body (modules/errorCodes.js:287-301, composeErrorResponse in modules/routeHelpers.js).| Actual reason | Internal sub-code | HTTP body code |
|---|---|---|
| Source or target version is v4.0 (cross model family; needs a separate pipeline; half-migration is refused) | none | VOICE_MIGRATE_NOT_ALLOWED |
Voice has professional-cloning (LoRA) artifacts; metadata.loraId is present | VOICE_MIGRATE_LORA_VOICE | VOICE_MIGRATE_NOT_ALLOWED |
code; it can only display the already-localized message.version and metadata.withLora before calling and decide yourself.VOICE_MIGRATE_NO_SOURCE is an independent parent code, meaning the voice has neither a style list nor usable original reference audio, so there is nothing to rebuild from.v2.0 / v3.0 / v4.0; historical voices may still be v1.0 (modules/voiceVersion.js:26,29).Legacy alias: /api/tts/voice/{id}/migrateV2— still supported for backward compatibility; use the primary path for new integrations.
{ status, code, message, requestId? }. code is always the parent code — sub codes only change message and never appear in the body. requestId is present only on reportable-tier errors, so use the X-Vocu-App-Request-Id response header when troubleshooting (it is written unconditionally on every response). See the "Errors" document for the full code reference.