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

# Doblar el proyecto

> Dobla un proyecto ya transcrito a partir de su propio SRT traducido

# Doblar el proyecto

Genere las voces y mezcle la salida de un proyecto existente a partir de un SRT traducido que usted proporciona. VoiceCheap no traduce nada en este endpoint: ni motor de traducción, ni reformulación SmartSync, ni glosario. Cada cue se locuta literalmente sobre el segmento correspondiente de la transcripción del proyecto y después se mezcla con el fondo original, los subtítulos y la sincronización labial opcional, igual que una traducción normal.

Úselo cuando la traducción se produce en otro lugar (un traductor humano, sus propias herramientas, un flujo de automatización) y solo necesita VoiceCheap para generar las voces. La ejecución es asíncrona: consulte [Obtener estado de la traducción](/docs/es/api-reference/translation-status) o escuche los [Webhooks](/docs/es/api-reference/webhooks).

## Requisitos previos

* El proyecto debe existir y su transcripción debe estar completa: créelo con [Crear proyecto](/docs/es/api-reference/create-project) (solo transcripción) o [Iniciar traducción](/docs/es/api-reference/translate), y espere `transcriptionStatus: "success"`.
* Su SRT debe cubrir la transcripción del proyecto: cada segmento necesita al menos un cue, y cada cue debe caer sobre un segmento. Exporte la transcripción con [Exportar transcripción del proyecto](/docs/es/api-reference/project-transcript) y tradúzcala cue por cue para conservar los códigos de tiempo.
* La solicitud se factura como una traducción del mismo medio, con la misma comprobación de créditos. Los modos de sincronización labial añaden sus créditos extra habituales.

## Límite de concurrencia

La creación de proyectos, la traducción y el doblaje comprueban el límite de traducciones activas de su cuenta (10 como máximo, posiblemente menos). Solo las traducciones o los doblajes en curso ocupan plazas; los proyectos de solo transcripción no. Al alcanzar el límite, la API devuelve `CONCURRENT_TRANSLATION_LIMIT_REACHED` (HTTP 429). El límite aplicado aparece en `details.limit`.

También se rechaza una segunda ejecución para el mismo proyecto e idioma de destino mientras haya otra en curso (`TRANSLATION_ALREADY_IN_PROGRESS`, HTTP 409).

## Solicitud

Este endpoint acepta un cuerpo JSON (`Content-Type: application/json`). Las opciones booleanas y numéricas se envían como valores JSON, no como cadenas.

### Encabezados

<ParamField header="x-api-key" type="string" required>
  Tu clave de API VoiceCheap. Obtén una en [app.voicecheap.ai/page-api](https://app.voicecheap.ai/page-api).
</ParamField>

### Parámetros de ruta

<ParamField path="projectId" type="string" required>
  El identificador del proyecto devuelto por [Crear proyecto](/docs/es/api-reference/create-project) o [Iniciar traducción](/docs/es/api-reference/translate).
</ParamField>

### Parámetros del cuerpo

<ParamField body="targetLanguage" type="string" required>
  El idioma de `translatedSrt`, en minúsculas, de la misma lista que [Iniciar traducción](/docs/es/api-reference/translate). Si el proyecto aún no tiene un trabajo para este idioma, se crea uno y el idioma se añade al proyecto.
</ParamField>

<ParamField body="translatedSrt" type="string" required>
  El contenido SRT final en el idioma de destino (número de cue, línea de tiempo, texto). Máximo 1.000.000 de caracteres. Los cues se emparejan con los segmentos de la transcripción por marcas de tiempo: un cue que empieza dentro de 50 ms del inicio de un segmento se asigna a ese segmento; si no, al segmento con el que más se solapa. Varios cues en un mismo segmento se unen con un espacio. El tiempo, el locutor y el orden de cada segmento locutado proceden siempre de la transcripción del proyecto; sus códigos de tiempo solo sirven para el emparejamiento.
</ParamField>

<ParamField body="webhookUrl" type="string">
  Un endpoint https que recibe a partir de ahora los [eventos de webhook](/docs/es/api-reference/webhooks) de este proyecto y sustituye el endpoint configurado en la cuenta.

  **Predeterminado:** El endpoint ya guardado en el proyecto y, después, el endpoint de webhook de la cuenta.
</ParamField>

<ParamField body="keepBackgroundMusic" type="boolean">
  Conservar el audio de fondo original (música, ambiente) bajo las voces generadas.

  **Predeterminado:** `true`
</ParamField>

<ParamField body="keepOriginalVoice" type="boolean">
  Mantener la voz original en bajo volumen bajo la voz generada.

  **Predeterminado:** `false`
</ParamField>

<ParamField body="originalVoiceVolume" type="number">
  Volumen de la voz original de `1` a `70` cuando `keepOriginalVoice` está activado.

  **Predeterminado:** `30`
</ParamField>

<ParamField body="voiceMode" type="string">
  Estrategia de voz: `cloning` (clonar a los hablantes del proyecto) o `custom` (usar una voz del catálogo o tu voz personalizada guardada).

  **Predeterminado:** `cloning`
</ParamField>

<ParamField body="voiceId" type="string">
  Obligatorio cuando `voiceMode` es `custom`. Usa un ID de `dubbing_voices_list`: una voz del catálogo o una voz personalizada guardada por la cuenta
  autenticada. La facturación del equipo no da acceso a las voces guardadas de otro miembro.
</ParamField>

<ParamField body="voiceIsolatorOption" type="string">
  Modo de aislamiento de voz cuando `keepBackgroundMusic` está activado: `studio` o `realistic`.

  **Predeterminado:** `studio`
</ParamField>

<ParamField body="subtitles" type="boolean">
  Incrustar subtítulos en el vídeo de salida. Requiere un proyecto de vídeo.

  **Predeterminado:** `false`
</ParamField>

<ParamField body="subtitlesSource" type="string">
  Texto de los subtítulos cuando `subtitles` está activado: `translated` (su SRT) u `original` (la transcripción de origen).

  **Predeterminado:** `translated`
</ParamField>

<ParamField body="lipSyncMode" type="string">
  Ejecutar la sincronización labial cuando el doblaje esté listo: `standard`, `pro` o `studio`. Las reglas de plan, duración y créditos son las mismas que en [Iniciar traducción](/docs/es/api-reference/translate). Requiere un proyecto de vídeo.

  **Predeterminado:** omita el campo para saltar la sincronización labial.
</ParamField>

<ParamField body="activeSpeakerDetectionEnabled" type="boolean">
  Activar la detección del locutor activo para la ejecución de sincronización labial solicitada.

  **Predeterminado:** `false`
</ParamField>

<ParamField body="voiceCloningSettings" type="object">
  Ajuste fino del clonado de voz, como objeto JSON con `stability` y `similarity` entre `0` y `1`. Solo con `voiceMode: "cloning"`. Consulte [Iniciar traducción](/docs/es/api-reference/translate) para orientar cada valor.
</ParamField>

<ParamField body="translationTimeSkips" type="array">
  Intervalos del tiempo de origen que permanecen en el idioma original, como `[{ "startTime": 10.2, "endTime": 12.8 }]`. Requiere un proyecto de vídeo y `keepBackgroundMusic: true`, no puede combinarse con `keepOriginalVoice` y no puede solaparse con segmentos de la transcripción.
</ParamField>

## Respuesta

<ResponseField name="success" type="boolean" required>
  Siempre `true` en las solicitudes correctas.
</ResponseField>

<ResponseField name="message" type="string" required>
  Una confirmación legible.
</ResponseField>

<ResponseField name="projectId" type="string" required>
  El proyecto que se está doblando.
</ResponseField>

<ResponseField name="workId" type="string" required>
  El trabajo de traducción usado para `targetLanguage`. Coincide con `workId` en los endpoints de estado, detalles y transcripción.
</ResponseField>

<ResponseField name="estimatedDuration" type="number" required>
  Estimación aproximada del procesamiento en minutos: cinco minutos de procesamiento por cada minuto iniciado del medio de origen.
</ResponseField>

```json Response theme={null}
{
  "success": true,
  "message": "Dubbing started successfully from the provided translation. Use the status endpoint to track progress.",
  "projectId": "project_123",
  "workId": "work_456",
  "estimatedDuration": 10
}
```

## Seguimiento de la ejecución

El proyecto pasa al flujo `translation` en cuanto empieza el doblaje, así que [Obtener estado de la traducción](/docs/es/api-reference/translation-status) sigue la nueva ejecución: `status` pasa de `processing` a `success` (con `translatedVideoUrl` y `translatedAudioUrl`) o a `failed` (con `error`). Los webhooks `project.translation.completed` y `project.translation.failed` se envían con el mismo `targetLanguage`. En [Obtener detalles del proyecto](/docs/es/api-reference/project-details), la versión traducida informa `settings.dubbingMode: "dub-only"` y `settings.smartSync: false`.

Los fallos de alineación aparecen después de aceptar la solicitud POST. El endpoint de estado devuelve `status: "failed"` con `error.code` y `error.message`; el webhook `project.translation.failed` lleva el mismo código y mensaje. `TRANSLATED_SRT_UNMATCHED_CUES` indica en el mensaje los números de cue sin correspondencia, y `TRANSLATED_SRT_MISSING_SEGMENTS` los índices de segmento sin cue. Estas respuestas asíncronas no incluyen un campo `details` estructurado. Corrija el SRT y vuelva a llamar al endpoint: la versión fallida permanece en el historial y se crea una nueva.

### Comprobar el ritmo

Exporte la transcripción traducida como JSON con [Exportar transcripción del proyecto](/docs/es/api-reference/project-transcript). Cada segmento incluye `finalSpeedFactor`, la relación entre el habla generada y la ventana del segmento. Acorte los cues que queden aproximadamente por encima de `1.3` y vuelva a ejecutar el doblaje con el SRT revisado.

## Flujo de ejemplo

Una automatización típica (por ejemplo un escenario de n8n o Make) encadena los endpoints así:

1. `POST /v1/projects` con el medio, `targetLanguage` y un `originalLanguage` explícito (solo transcripción).
2. Consulte `GET /v1/translate/{projectId}/status` hasta que `transcriptionStatus` sea `success`, o espere el webhook `project.created`.
3. `POST /v1/projects/{projectId}/transcript` con `{ "source": "original", "outputFormat": "srt" }` y traduzca los cues conservando la numeración y los códigos de tiempo.
4. `POST /v1/projects/{projectId}/dub` con el SRT traducido.
5. Consulte el endpoint de estado hasta que `status` sea `success`, o espere `project.translation.completed`.
6. Descargue `translatedVideoUrl` (o `lipSync.videoUrl` si se solicitó sincronización labial).
7. `DELETE /v1/translate/{projectId}` cuando las salidas estén guardadas de su lado.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.voicecheap.ai/v1/projects/project_123/dub" \
    -H "x-api-key: vc_your-api-key" \
    -H "Content-Type: application/json" \
    -d '{
      "targetLanguage": "english",
      "translatedSrt": "1\n00:00:00,000 --> 00:00:02,400\nWelcome to this lesson.\n\n2\n00:00:02,400 --> 00:00:05,100\nToday we talk about fractions.",
      "keepBackgroundMusic": true,
      "subtitles": true,
      "subtitlesSource": "translated"
    }'
  ```

  ```typescript TypeScript theme={null}
  interface DubProjectResponse {
    success: boolean;
    message: string;
    projectId: string;
    workId: string;
    estimatedDuration: number;
  }

  const API_BASE_URL = 'https://api.voicecheap.ai/v1';
  const headers = { 'x-api-key': 'vc_your-api-key', 'Content-Type': 'application/json' };

  async function waitForStatus(projectId: string, isReady: (status: any) => boolean): Promise<any> {
    while (true) {
      const status = await fetch(`${API_BASE_URL}/translate/${projectId}/status`, { headers }).then((response) => response.json());
      if (status.status === 'failed') throw new Error(`${status.error?.code}: ${status.error?.message}`);
      if (isReady(status)) return status;
      await new Promise((resolve) => setTimeout(resolve, 15_000));
    }
  }

  async function dubExistingProject(projectId: string, translatedSrt: string): Promise<string> {
    // 1. The project was created earlier with POST /v1/projects; wait for its transcript.
    await waitForStatus(projectId, (status) => status.transcriptionStatus === 'success');

    // 2. Send the translation you produced from the exported transcript.
    const dubResponse = await fetch(`${API_BASE_URL}/projects/${projectId}/dub`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ targetLanguage: 'english', translatedSrt, keepBackgroundMusic: true }),
    });
    if (!dubResponse.ok) {
      const error = await dubResponse.json();
      throw new Error(`${error.code}: ${error.message}`);
    }
    const { workId } = (await dubResponse.json()) as DubProjectResponse;
    console.log('Dubbing started on work', workId);

    // 3. Wait for the dubbed output.
    const completed = await waitForStatus(projectId, (status) => status.status === 'success');
    return completed.translatedVideoUrl as string;
  }
  ```

  ```python Python theme={null}
  import time
  import requests

  API_BASE_URL = 'https://api.voicecheap.ai/v1'
  HEADERS = {'x-api-key': 'vc_your-api-key'}


  def wait_for_status(project_id, is_ready):
      while True:
          status = requests.get(f'{API_BASE_URL}/translate/{project_id}/status', headers=HEADERS).json()
          if status['status'] == 'failed':
              raise RuntimeError(f"{status['error']['code']}: {status['error']['message']}")
          if is_ready(status):
              return status
          time.sleep(15)


  def dub_existing_project(project_id, translated_srt):
      wait_for_status(project_id, lambda status: status['transcriptionStatus'] == 'success')

      response = requests.post(
          f'{API_BASE_URL}/projects/{project_id}/dub',
          headers=HEADERS,
          json={'targetLanguage': 'english', 'translatedSrt': translated_srt, 'keepBackgroundMusic': True},
      )
      response.raise_for_status()
      print('Dubbing started on work', response.json()['workId'])

      completed = wait_for_status(project_id, lambda status: status['status'] == 'success')
      return completed['translatedVideoUrl']
  ```
</CodeGroup>

## Errores

| Estado | Código | Descripción |
| - | - | - |
| 400 | `VALIDATION_ERROR` | Falta un campo del cuerpo o no es válido (`details` enumera cada campo) |
| 400 | `INVALID_TRANSLATED_SRT` | translatedSrt no contiene cues SRT válidos |
| 400 | `INVALID_BOOLEAN_VALUE` | Una opción booleana no se envió como booleano JSON |
| 400 | `INVALID_NUMBER_VALUE` | Una opción numérica no se envió como número JSON |
| 400 | `DURATION_DETECTION_FAILED` | La duración almacenada del medio del proyecto no está disponible |
| 400 | `VOICE_ID_REQUIRED` | Se seleccionó el modo de voces personalizadas sin un ID de voz |
| 400 | `VOICE_ID_NOT_ALLOWED_WITH_CLONING` | Se combinó un ID de voz con clonación de voz |
| 400 | `VOICE_CLONING_SETTINGS_NOT_ALLOWED` | Se combinaron los ajustes de clonación con el modo de voces personalizadas |
| 400 | `TRANSLATION_TIME_SKIP_CONFLICT` | Los saltos de tiempo entran en conflicto con otra opción de audio |
| 400 | `TRANSLATION_TIME_SKIP_REQUIRES_BACKGROUND` | Los saltos de tiempo requieren audio de fondo |
| 400 | `TRANSLATION_TIME_SKIP_OUT_OF_RANGE` | Un salto de tiempo se extiende más allá de la duración del medio |
| 400 | `SUBTITLES_NOT_AVAILABLE_FOR_AUDIO` | Se solicitaron subtítulos incrustados para la entrada de audio |
| 400 | `LIPSYNC_NOT_AVAILABLE_FOR_AUDIO` | Se solicitó sincronización labial para la entrada de audio |
| 400 | `TRANSLATION_TIME_SKIP_NOT_AVAILABLE_FOR_AUDIO` | Se solicitaron saltos de tiempo para la entrada de audio |
| 400 | `LIPSYNC_VIDEO_TOO_LONG` | Se solicitó sincronización labial para medios de mayor duración que la admitida |
| 401 | `MISSING_API_KEY` | Se requiere una clave API |
| 401 | `INVALID_API_KEY_FORMAT` | La clave API debe comenzar con `vc_` |
| 401 | `INVALID_API_KEY` | La clave API proporcionada no es válida |
| 403 | `SUBSCRIPTION_REQUIRED` | El acceso a la API requiere un plan Starter o superior |
| 403 | `FORBIDDEN` | No tiene permiso para acceder al proyecto |
| 403 | `INSUFFICIENT_CREDITS` | No hay créditos suficientes para doblar este proyecto |
| 403 | `CUSTOM_VOICE_ACCESS_DENIED` | La voz seleccionada no es una voz del catálogo ni una voz personalizada guardada por la cuenta autenticada. |
| 403 | `LIPSYNC_MODE_ACCESS_DENIED` | El modo de sincronización labial premium no está disponible en el plan actual |
| 404 | `PROJECT_NOT_FOUND` | El proyecto no existe |
| 409 | `DUB_ONLY_REQUIRES_TRANSCRIPTION` | La transcripción del proyecto aún se está procesando o ha fallado |
| 409 | `TRANSLATION_ALREADY_IN_PROGRESS` | Ya hay un doblaje en curso para este idioma en el proyecto |
| 429 | `RATE_LIMIT_EXCEEDED` | Demasiadas solicitudes (límite: 10 solicitudes por minuto) |
| 429 | `CONCURRENT_TRANSLATION_LIMIT_REACHED` | Se alcanzó el límite de simultaneidad de la cuenta; consulte `details.limit` |
| 500 | `INTERNAL_ERROR` | Error inesperado del servidor |

Los fallos de alineación detectados mientras se ejecuta la operación (`TRANSLATED_SRT_UNMATCHED_CUES`, `TRANSLATED_SRT_MISSING_SEGMENTS`) los devuelven el endpoint de estado y el webhook de fallo, no esta solicitud.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.