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

# 음성 미리 보기 저장

> 본인 소유의 만료되지 않은 복제 또는 디자인 음성 미리 보기를 재사용 가능한 음성으로 저장합니다. 동일한 미리 보기를 반복 저장하면 기존에 저장된 음성을 재사용합니다.

본인 소유의 만료되지 않은 복제 또는 디자인 음성 미리 보기를 재사용 가능한 음성으로 저장합니다. 동일한 미리 보기를 반복 저장하면 기존에 저장된 음성을 재사용합니다.

```http theme={null}
POST https://api.voicecheap.ai/v1/actions/voices_save_preview
```

## 인증 및 권한

<ParamField header="x-api-key" type="string" required>
  이 헤더에 VoiceCheap API 키를 전달하세요. 동일한 API 키를 사용하는 Authorization: Bearer도 지원합니다. OAuth 액세스 토큰은 MCP에서만 허용되며, 이
  REST 엔드포인트에서는 사용할 수 없습니다.
</ParamField>

필요한 권한: `voices:write`.

활성 유료 구독이 필요합니다. 권한은 팀 역할, 리소스 소유권, 미디어 크레딧 또는 요금제 한도를 변경하지 않습니다.

## 매개변수

<ParamField body="requestId" type="string">
  전송 계층에서는 선택 사항입니다. 생략하면 서버가 유효성 검사 전에 식별자를 추가합니다. 응답이 유실된 경우 안전하게 복구할 수 있도록 고정된 UUID v4를 직접 제공하세요.

  형식: "uuid". 패턴: `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$`.
</ParamField>

<ParamField body="previewId" type="string" required>
  텍스트 값입니다.

  형식: "uuid". 패턴: `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`.
</ParamField>

<ParamField body="name" type="string" required>
  텍스트 값입니다.

  최소 길이: 1. 최대 길이: 80.
</ParamField>

<Accordion title="전체 입력 스키마">
  아래 JSON Schema는 검색을 통해 제공되는 정확한 필드 이름, 중첩 유형, 기본값, 범위 및 허용값을 유지합니다. 추가적인 필드 간 규칙은 작업 참고 사항에 나와 있습니다.

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "requestId": {
        "description": "UUID v4 for this action. Reuse the same ID with identical arguments on retries.",
        "type": "string",
        "format": "uuid",
        "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
      },
      "previewId": {
        "type": "string",
        "format": "uuid",
        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$"
      },
      "name": {
        "type": "string",
        "minLength": 1,
        "maxLength": 80
      }
    },
    "required": ["previewId", "name"],
    "additionalProperties": false
  }
  ```
</Accordion>

## 멱등성

각 변경 작업에 대해 requestId 또는 Idempotency-Key HTTP 헤더에 UUID v4를 사용하세요. 네트워크 오류 후에는 동일한 식별자와 동일한 인수를 재사용하세요. 둘 다 제공하는 경우 값이 일치해야 합니다. 저장된 요청을 재전송해도 작업이 반복되거나 월간 호출이 추가로 차감되지 않습니다. 실패하거나 중단된 요청은 자동으로 다시 시작되지 않습니다.

첫 실행이 일반적으로 동기식인 경우에도, 재시도 시 동일한 요청이 아직 실행 중이면 모든 변경 작업에서 HTTP 202를 반환할 수 있습니다. 실행은 완료되었지만 resultStored가 false이면, 재시도 시 원래 결과 대신 저장된 요청 상태를 HTTP 200으로 반환합니다. 해당 영역의 기록, 결과 또는 다운로드 액션으로 출력을 조회하세요.

## 요청 예제

예제의 리소스 식별자와 미디어 키를 계정에서 반환된 값으로 바꾸세요. 동일한 변경 작업을 재시도할 때는 요청 식별자를 유지하세요.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST 'https://api.voicecheap.ai/v1/actions/voices_save_preview' \
    --header "x-api-key: $VOICECHEAP_API_KEY" \
    --header 'Content-Type: application/json' \
    --data '{"requestId":"284a861a-4da8-4d67-ad48-8123e76c8e91","previewId":"44444444-4444-4444-8444-444444444444","name":"Studio narrator"}'
  ```

  ```javascript JavaScript theme={null}
  const input = {
    requestId: '284a861a-4da8-4d67-ad48-8123e76c8e91',
    previewId: '44444444-4444-4444-8444-444444444444',
    name: 'Studio narrator',
  };
  const response = await fetch('https://api.voicecheap.ai/v1/actions/voices_save_preview', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.VOICECHEAP_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(input),
  });
  const body = await response.json();
  if (!response.ok) throw new Error(body.code + ': ' + body.message);
  console.log(body);
  ```

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

  response = requests.post(
      "https://api.voicecheap.ai/v1/actions/voices_save_preview",
      headers={"x-api-key": os.environ["VOICECHEAP_API_KEY"]},
      json={"requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91", "previewId": "44444444-4444-4444-8444-444444444444", "name": "Studio narrator"},
      timeout=120,
  )
  body = response.json()
  if not response.ok:
      raise RuntimeError(f"{body.get('code')}: {body.get('message')}")
  print(body)
  ```
</RequestExample>

## 응답

이 작업은 실행 중에 HTTP 202를 반환합니다. requestId를 저장하고 해당 식별자와 원래 작업 이름으로 action\_request\_get을 폴링하세요. 폴링 상태는 running, completed, failed, interrupted입니다. 완료된 결과가 별도의 미디어 작업을 가리킬 수 있으므로 해당 작업의 상태 또는 출력 액션을 이어서 사용하세요.

저장된 재사용 가능한 TTS 음성, 갱신된 저장 슬롯 허용량, 남아 있는 활성 미저장 미리보기 및 생성 허용량입니다.

<ResponseExample>
  ```json HTTP 202 theme={null}
  {
    "data": {
      "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91",
      "status": "running",
      "pollAction": "action_request_get",
      "pollAfterSeconds": 5
    },
    "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91"
  }
  ```

  ```json HTTP 200 theme={null}
  {
    "data": {
      "voicePreviews": [],
      "creationUsage": {
        "cloneTrial": {
          "limit": 1,
          "used": 1,
          "remaining": 0
        },
        "design": {
          "limit": 5,
          "used": 0,
          "remaining": 5,
          "resetsAt": 1792195200000
        },
        "previewExpiresAfterHours": 24
      },
      "voice": {
        "accent": "original",
        "id": "clone:44444444-4444-4444-8444-444444444444",
        "name": "Studio narrator",
        "previewUrl": "https://files.example.com/saved-narrator.mp3",
        "source": "clone"
      },
      "cloneUsage": {
        "canClone": true,
        "limit": 10,
        "remaining": 9,
        "used": 1
      }
    },
    "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91"
  }
  ```

  ```json HTTP 202 replay_running theme={null}
  {
    "data": {
      "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91",
      "operation": "voices_save_preview",
      "createdAt": 1792144800000,
      "updatedAt": 1792144820000,
      "status": "running",
      "pollAfterSeconds": 5
    },
    "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91"
  }
  ```

  ```json HTTP 200 replay_completed_without_result theme={null}
  {
    "data": {
      "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91",
      "operation": "voices_save_preview",
      "createdAt": 1792144800000,
      "updatedAt": 1792144820000,
      "status": "completed",
      "resultStored": false,
      "message": "The action completed. Retrieve its output from the corresponding history or resource action."
    },
    "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91"
  }
  ```
</ResponseExample>

## 결과 필드

| 필드 | 설명 |
| - | - |
| `data.voicePreviews` | 이전 요청의 후보를 포함하여 이 계정에서 현재 활성화된 모든 미저장 미리보기입니다. |
| `data.voicePreviews[].id` | voices\_preview\_get과 voices\_save\_preview에서 사용하는 미리보기 UUID v4입니다. |
| `data.voicePreviews[].kind` | clone 또는 design입니다. |
| `data.voicePreviews[].name` | 미리보기 표시 이름입니다. 디자인된 후보는 요청한 이름 뒤에 번호를 붙입니다. |
| `data.voicePreviews[].previewUrl` | 재생 가능한 임시 오디오 URL입니다. 미리보기가 활성 상태인 동안 voices\_preview\_get으로 새 URL을 요청하세요. |
| `data.voicePreviews[].expiresAt` | 미리보기 만료 시각을 나타내는 밀리초 단위 Unix 타임스탬프입니다. |
| `data.voicePreviews[].durationSeconds` | 미리보기 오디오의 길이로, 초 단위입니다. |
| `data.creationUsage.cloneTrial` | limit, used 및 remaining이 포함된 평생 복제 미리보기 체험 허용량입니다. |
| `data.creationUsage.design` | 일일 음성 디자인 허용량으로, limit, used, remaining 및 Unix 밀리초 단위의 resetsAt이 포함됩니다. |
| `data.creationUsage.previewExpiresAfterHours` | 저장되지 않은 미리보기의 유지 시간(시간 단위)이며, 현재 24입니다. |
| `data.voice.id` | 재사용 가능한 TTS 선택 ID clone:\<UUID>입니다. tts\_generate에는 전체 값을 사용하고, voices\_delete에는 UUID만 사용하세요. |
| `data.voice.name` | 저장된 음성의 표시 이름입니다. |
| `data.voice.accent` | 저장된 사용자 지정 음성은 original을 사용합니다. |
| `data.voice.source` | 저장된 사용자 지정 음성은 clone을 사용합니다. |
| `data.voice.previewUrl` | 저장된 음성의 임시 오디오 미리보기 URL입니다. |
| `data.cloneUsage.canClone` | 현재 플랜에서 사용자 지정 TTS 음성 저장을 허용하는지 여부입니다. 사용 가능한 슬롯은 remaining을 확인하세요. |
| `data.cloneUsage.limit` | 저장된 사용자 지정 TTS 음성 슬롯의 요금제 한도입니다. |
| `data.cloneUsage.used` | 생성 예약분을 포함하여 사용 중인 TTS 음성 슬롯입니다. |
| `data.cloneUsage.remaining` | 사용 가능한 TTS 음성 저장 슬롯입니다. |

## 이 요청 복구 및 폴링

연결이 끊어진 후에도 동일한 식별자를 사용하세요. resultStored가 false이면 해당 기록, 결과 또는 다운로드 액션을 통해 출력을 조회하세요. 진행 상황을 확인하기 위해 두 번째 생성을 시작하지 마세요.

```bash theme={null}
curl --request POST 'https://api.voicecheap.ai/v1/actions/action_request_get' \
  --header "x-api-key: $VOICECHEAP_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{"requestId":"284a861a-4da8-4d67-ad48-8123e76c8e91","operation":"voices_save_preview"}'
```

```json HTTP 200 theme={null}
{
  "data": {
    "requestId": "284a861a-4da8-4d67-ad48-8123e76c8e91",
    "operation": "voices_save_preview",
    "status": "completed",
    "createdAt": 1792144800000,
    "updatedAt": 1792144820000,
    "resultStored": true,
    "result": {
      "voicePreviews": [],
      "creationUsage": {
        "cloneTrial": {
          "limit": 1,
          "used": 1,
          "remaining": 0
        },
        "design": {
          "limit": 5,
          "used": 0,
          "remaining": 5,
          "resetsAt": 1792195200000
        },
        "previewExpiresAfterHours": 24
      },
      "voice": {
        "accent": "original",
        "id": "clone:44444444-4444-4444-8444-444444444444",
        "name": "Studio narrator",
        "previewUrl": "https://files.example.com/saved-narrator.mp3",
        "source": "clone"
      },
      "cloneUsage": {
        "canClone": true,
        "limit": 10,
        "remaining": 9,
        "used": 1
      }
    }
  }
}
```

## 작업 참고 사항

* 이 변경 작업에 UUID v4 requestId를 사용하고 재시도 시 동일한 인수와 함께 재사용하세요. 장시간 실행되는 액션은 먼저 running 요청을 반환합니다. 이 예시는 action\_request\_get에서 얻을 수 있는 완료된 핸들러 결과입니다.
* previewId는 voices\_preview\_clone 또는 voices\_design이 반환한 소유한 후보의 UUID입니다. name은 앞뒤 공백 제거 후 1–80자입니다.
* 저장하려면 유료 요금제, 사용 가능한 TTS 음성 저장 슬롯 및 만료되지 않은 ready 상태의 미리보기가 필요합니다. 이 액션은 복제 및 디자인된 후보에 사용할 수 있습니다.
* 재사용 가능한 선택 ID는 clone:\<previewId>입니다. 저장된 후보는 voicePreviews에서 사라지고 voices\_list의 저장된 음성에 포함됩니다.
* 이미 저장된 동일한 후보를 반복해서 저장하면 저장된 음성을 재사용합니다. 반복 저장 시 원래 저장된 이름을 유지합니다.
* 새로 조회한 음성 목록에 저장된 음성이 없으면 voice가 생략될 수 있습니다. 현재 저장된 음성은 voices\_list로 조회하세요.
* 음성 합성 기록과 사용자 지정 음성은 인증된 계정에 속합니다. 팀 결제를 사용해도 다른 멤버의 음성 합성 결과나 음성에 접근할 수 없습니다.

## 사용량

성공한 호출마다 월간 API/MCP 호출 1회가 차감됩니다. 미디어 크레딧과 해당 작업의 생성 허용량도 적용됩니다.

## 오류

| HTTP 상태 | 코드 | 의미 또는 복구 방법 |
| - | - | - |
| 400 | `CREDENTIAL_IN_QUERY` | 자격 증명이 access\_token 또는 api\_key 쿼리 매개변수로 제공되었습니다. 자격 증명은 헤더로 보내세요. |
| 400 | `IDEMPOTENCY_CONFLICT` | Idempotency-Key 헤더와 본문의 requestId 필드가 일치해야 합니다. |
| 400 | `INVALID_REQUEST_ID` | UUID v4인 requestId 또는 Idempotency-Key를 제공하세요. |
| 400 | `VALIDATION_ERROR` | 입력이 액션 스키마와 일치하지 않습니다. 필드 오류는 details를 확인하세요. |
| 401 | `API_KEY_EXPIRED` | API 키가 만료되었습니다. |
| 401 | `INVALID_API_KEY` | 유효하고 만료되지 않은 API 키를 제공하세요. |
| 401 | `INVALID_CREDENTIAL` | Authorization 헤더의 형식이 잘못되었거나 x-api-key와 충돌합니다. |
| 401 | `MISSING_CREDENTIAL` | 사용 가능한 헤더 자격 증명이 전달되지 않았거나 4096자를 초과했습니다. |
| 401 | `USER_NOT_FOUND` | 이 자격 증명에 연결된 계정이 더 이상 존재하지 않습니다. |
| 403 | `ACCOUNT_UNAVAILABLE` | 작업을 수행하는 계정 또는 결제 계정을 사용할 수 없거나 해당 계정이 삭제 중입니다. |
| 403 | `AMBIGUOUS_TEAM_API_KEY` | 이 자격 증명의 사용자가 둘 이상의 팀에 속해 있어 청구 컨텍스트가 모호합니다. |
| 403 | `INSUFFICIENT_SCOPE` | 필요한 모든 권한을 부여하거나 다른 자격 증명을 사용하세요. |
| 403 | `INTEGRATION_PLAN_UNSUPPORTED` | 계정의 통합 한도를 확인할 수 없습니다. |
| 403 | `SUBSCRIPTION_REQUIRED` | 활성 유료 구독을 사용하세요. |
| 403 | `TEAM_OWNER_NOT_FOUND` | 실제로 적용되는 팀 소유자 계정을 찾을 수 없습니다. |
| 403 | `VOICE_CLONING_REQUIRES_PAID_PLAN` | 현재 요금제에서는 사용자 지정 TTS 음성을 저장하고 재사용할 수 없습니다. |
| 404 | `USER_NOT_FOUND` | 인증된 계정이 더 이상 존재하지 않습니다. |
| 404 | `VOICE_PREVIEW_NOT_FOUND` | 미리보기가 없거나 다른 계정에 속합니다. |
| 409 | `CONCURRENT_ACTION_LIMIT_REACHED` | 이 계정에서는 이미 10개의 연동 액션이 실행 중입니다. 새 액션을 시작하기 전에 기존 요청을 폴링하세요. |
| 409 | `IDEMPOTENCY_CONFLICT` | 동일한 작업과 인수에 대해서만 식별자를 재사용하세요. |
| 409 | `VOICE_CLONE_LIMIT_REACHED` | 저장된 사용자 지정 TTS 음성 슬롯이 모두 사용 중입니다. |
| 409 | `VOICE_CREATION_IN_PROGRESS` | 이 후보를 아직 저장 중이거나 계정 삭제로 인해 새 음성 작업이 차단되었습니다. |
| 410 | `VOICE_PREVIEW_EXPIRED` | 후보가 만료되었거나 ready 상태가 아니거나 더 이상 사용 가능한 미리보기 오디오가 없습니다. |
| 429 | `INTEGRATION_MONTHLY_LIMIT_REACHED` | 월간 한도가 초기화될 때까지 기다리거나 요금제를 변경하세요. |
| 429 | `INTEGRATION_RATE_LIMIT_REACHED` | Retry-After가 있으면 지정된 시간만큼 기다리고 백오프를 적용하세요. |
| 500 | `INTERNAL_ERROR` | 변경 작업을 재시도하기로 결정하기 전에 저장된 요청 상태와 리소스 기록을 확인하세요. |
| 502 | `INTERNAL_ERROR` | 액션을 완료할 수 없거나 결과를 확인할 수 없습니다. 다른 생성을 시작하기 전에 요청 상태와 리소스 이력을 확인하세요. |
| 503 | `ACTION_RESULT_UNAVAILABLE` | 액션은 실행되었지만 결과를 저장할 수 없습니다. 새 생성을 시작하기 전에 저장된 요청 상태와 리소스 기록을 확인하세요. |
| 503 | `SERVER_RESTARTING` | 서버가 다시 시작 중입니다. 잠시 후 재시도하고, 변경 작업의 경우 동일한 요청 식별자를 유지하세요. |

```json theme={null}
{
  "code": "INSUFFICIENT_SCOPE",
  "message": "This credential does not permit the requested action.",
  "status": 403,
  "details": {
    "requiredScopes": ["voices:write"]
  }
}
```


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