문서
SoulDub 동영상 번역 Agent API 레퍼런스
SoulDub 동영상 번역을 자동화하는 Agent, 백엔드 서비스, 스크립트를 위한 실행 API 레퍼런스입니다.
대상 독자: SoulDub 동영상 번역을 자동화하는 Agent, 백엔드 서비스 및 스크립트입니다.
이 문서는 Skill의 실행 시점 API 레퍼런스이며 scripts/video_translation.py를 대체하지 않습니다. 일반 번역은 먼저 스크립트를 사용하고, API 호출, 인증, 멱등성, 속도 제한, 결과 다운로드 또는 오류 코드를 디버깅할 때 이 문서를 확인하세요.
1. Agent 빠른 참고
- 현재 API 기본 URL:
https://xxx.com/api/v1 - 외부 API 경로는
/api/v1/...아래에 있습니다. - 모든
/api/v1/...엔드포인트에는 Agent API key가 필요합니다.Authorization: Bearer ${AGENT_API_KEY}사용을 권장합니다. - 바이너리 동영상 업로드는 저장소의 presigned URL에
PUT {presignedUrl}으로 직접 전송하며 API key를 포함하지 않습니다. - 미디어 게이트웨이 URL은 API key 없이 직접 다운로드합니다.
- 자막 다운로드 엔드포인트는 Agent API이므로 API key가 필요합니다.
- 작업 생성과 재번역에는 안정적인
Idempotency-Key가 필요합니다. - API key, 업로드 presigned URL, 임시 미디어 다운로드 URL 또는 서명 파라미터를 로그, 상태 파일, 테스트 보고서 또는 프롬프트에 기록하지 마세요.
- 429 응답은
Retry-After에 따라 기다립니다. 동시 실행 제한에도 기다려야 하며 공격적으로 재시도하지 마세요. - 임시 URL이 만료되면 결과 엔드포인트에서 새 URL을 요청하세요.
2. 호출 흐름
| 순서 | 단계 | 엔드포인트 |
|---|---|---|
| 1 | 업로드 시작 | POST /video-uploads/initiate |
| 2 | 파트 URL 서명 | POST /video-uploads/presign-part |
| 3 | 동영상 바이트 업로드 | PUT {presignedUrl} |
| 4 | 업로드 완료 | POST /video-uploads/complete |
| 5 | 번역 작업 생성 | POST /video-translations |
| 6 | 작업 상태 조회 | GET /video-translations/{taskId} |
| 7 | 결과 목록 조회 | GET /video-translations/{taskId}/results |
| 8 | 미디어 URL 하나 가져오기 | GET /video-translations/{taskId}/results/files/{variant} |
| 9 | 자막 다운로드 | GET /video-translations/{taskId}/results/subtitles/{variant} |
선택적 재번역:
POST /video-translations/{existingTaskId}/retranslate3. 인증 및 공통 규칙
Agent API는 두 가지 인증 헤더를 지원합니다. 첫 번째 헤더를 권장합니다.
Authorization: Bearer ${AGENT_API_KEY}X-API-Key: ${AGENT_API_KEY}일반적인 JSON 요청 헤더:
Content-Type: application/json
Accept: application/json
Authorization: Bearer ${AGENT_API_KEY}
X-Request-Id: agent-run-20260704-0001X-Request-Id는 선택 사항입니다. 지정하는 경우 길이는 8..64자이고 A-Z a-z 0-9 _ . : -만 사용할 수 있습니다. 서버는 응답 헤더에 최종 request id를 반환합니다.
성공 응답은 다음 JSON envelope을 사용합니다.
{
"code": 0,
"message": "ok",
"data": {}
}오류 응답도 JSON envelope을 사용합니다.
{
"code": -1,
"message": "INVALID_REQUEST",
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "Validation failed.",
"requestId": "req_abc123",
"details": {}
}
}디버깅할 때는 다음 순서로 확인하세요.
- HTTP 상태 코드
error.codeerror.details- 응답 헤더의
X-Request-Id
모든 Agent API 응답에는 다음 속도 제한 헤더가 포함될 수 있습니다.
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1782300000속도 제한 응답:
HTTP/1.1 429 Too Many Requests
Retry-After: 17RATE_LIMITED가 반환되면 Retry-After만큼 기다린 후 다시 시도하세요.
4. 동영상 업로드
4.1 업로드 시작
POST /video-uploads/initiate요청:
{
"filename": "demo.mp4",
"contentType": "video/mp4",
"fileSizeBytes": 342556
}| 필드 | 필수 | 설명 |
|---|---|---|
filename | 예 | .mp4 파일 이름이어야 합니다. |
contentType | 예 | 정확히 video/mp4여야 합니다. |
fileSizeBytes | 예 | 0보다 크고 계정 파일 크기 제한 이하여야 합니다. |
응답에서 fileId, uploadId, key, bucket을 저장하세요. 업로드 완료와 작업 생성 호출에 필요합니다.
4.2 파트 presigned URL 생성
POST /video-uploads/presign-part요청:
{
"uploadId": "opaque-r2-upload-id",
"key": "storage-key-from-initiate",
"partNumber": 1,
"expiresInSeconds": 900
}| 필드 | 필수 | 설명 |
|---|---|---|
uploadId | 예 | 업로드 시작 응답에서 반환됩니다. |
key | 예 | 업로드 시작 응답에서 반환됩니다. |
partNumber | 예 | 1..10000 범위입니다. |
expiresInSeconds | 아니요 | URL TTL이며 서버가 60..86400초로 제한합니다. |
반환된 presignedUrl은 현재 업로드에만 사용하세요. 로그나 상태 파일에 저장하지 마세요.
4.3 동영상 바이트 업로드
PUT {presignedUrl}
Content-Type: video/mp4요청 본문은 동영상 바이트입니다. 이 요청은 Agent API 요청이 아닙니다.
Authorization을 포함하지 마세요.X-API-Key를 포함하지 마세요.presignedUrl을 로그에 남기지 마세요.- URL이 만료되면
presign-part를 다시 호출하세요. - 성공적인 PUT은 보통 200을 반환하며, 클라이언트가 ETag를 Agent API로 보낼 필요는 없습니다.
4.4 업로드 완료
POST /video-uploads/complete요청:
{
"uploadId": "opaque-r2-upload-id",
"key": "storage-key-from-initiate"
}data.success == true, data.status == "completed"인지 확인하고 data.fileId가 업로드 시작 응답의 fileId와 같은지 확인하세요.
5. 번역 작업 생성
POST /video-translations
Idempotency-Key: ${UNIQUE_IDEMPOTENCY_KEY}요청:
{
"fileId": "uploaded-file-id",
"videoDurationSeconds": 6,
"checksumSha256": "optional-file-sha256",
"sourceLanguage": "auto",
"targetLanguage": "en",
"speakerCount": 1,
"allowDynamicDuration": true
}| 필드 | 필수 | 설명 |
|---|---|---|
fileId | 예 | 완료된 업로드의 fileId입니다. |
videoDurationSeconds | 예 | 1..3600초입니다. |
checksumSha256 | 아니요 | 추적 및 검증을 위한 원본 파일 SHA-256입니다. |
sourceLanguage | 예 | 원본 언어이며 auto를 사용할 수 있습니다. |
targetLanguage | 예 | 대상 언어이며 auto는 사용할 수 없습니다. |
speakerCount | 아니요 | 0..15; 기본값 0은 자동 감지입니다. |
allowDynamicDuration | 아니요 | true이면 동적 길이 동기화를 허용합니다. |
지원 언어는 references/languages.json과 서버 측 검증으로 정의됩니다.
taskId, fileId, creditsConsumed 및 요청에 사용한 Idempotency-Key를 저장하세요.
크레딧은 분 단위로 올림 계산합니다.
durationInMinutes = max(1, ceil(videoDurationSeconds / 60))
creditsConsumed = durationInMinutes * credit.points_per_minute현재 기본값은 credit.points_per_minute=3이며 실제 값은 서버 설정에 따라 달라집니다.
| 상황 | 결과 |
|---|---|
| 처음 성공한 요청 | 작업을 만들고 크레딧을 차감한 뒤 taskId를 반환합니다. |
| 같은 Key + 같은 본문 | 같은 응답을 반환하며 다시 차감하지 않습니다. |
| 같은 Key + 다른 본문 | 409 IDEMPOTENCY_KEY_REUSED |
| 같은 멱등 요청이 처리 중 | 409 IDEMPOTENCY_REQUEST_IN_PROGRESS |
네트워크 시간 초과로 서버가 요청을 받았는지 확실하지 않다면 동일한 본문과 동일한 Idempotency-Key로 재시도하세요.
6. 작업 진행률 조회
GET /video-translations/{taskId}| 필드 | 설명 |
|---|---|
taskId | 작업 ID |
status | pending, processing, completed, failed, cancelled |
progress | 일반적으로 0..100인 작업 진행률 |
errorMessage | 실패 이유. 성공 또는 처리 중에는 보통 비어 있습니다. |
status | 종료 상태 | 처리 |
|---|---|---|
pending | 아니요 | 계속 조회합니다. |
processing | 아니요 | 계속 조회합니다. |
completed | 예 | 결과를 가져옵니다. |
failed | 예 | 조회를 중지하고 errorMessage를 보고합니다. |
cancelled | 예 | 조회를 중지합니다. |
권장 조회 전략:
- 작업 생성 2초 후 처음 조회합니다.
- 완료되지 않았다면
2s -> 5s -> 10s -> 20s -> 30s로 간격을 늘립니다. - 권장 최대 간격은 30초입니다.
- HTTP 429는
Retry-After에 따라 기다립니다. - HTTP 5xx는 전체 시간 제한과 지수 백오프를 사용합니다.
API가 completed, failed 또는 cancelled를 반환할 때만 조회를 중지하세요.
7. 결과 가져오기
7.1 결과 목록 조회
GET /video-translations/{taskId}/results?expiresIn=600작업은 completed 상태여야 합니다. expiresIn은 선택 사항이며 미디어 다운로드 URL TTL은 서버에서 60..3600초로 제한되고 기본값은 3600초입니다.
type | variant | 설명 |
|---|---|---|
video | video | 최종 합성 MP4 동영상 |
audio | vocal | 분리된 음성 WAV |
audio | background | 분리된 배경음 WAV |
audio | full | 합성된 전체 음성 WAV |
subtitle | source | 원본 언어 SRT 자막 |
subtitle | translated | 대상 언어 SRT 자막 |
subtitle | bilingual | 이중 언어 SRT 자막 |
처리 규칙:
access=media_gatewayURL은 API key 없이 직접 다운로드합니다.access=api이고requiresAuthorization=true인 자막 URL은 API key가 필요합니다.- 미디어 URL이 만료되면 이 엔드포인트 또는 단일 파일 엔드포인트를 다시 호출하여 새 URL을 받으세요.
7.2 미디어 파일 URL 하나 가져오기
GET /video-translations/{taskId}/results/files/{variant}?expiresIn=600variant | 내용 |
|---|---|
video | 최종 합성 MP4 동영상 |
vocal | 음성 WAV |
background | 배경음 WAV |
full | 합성된 전체 음성 WAV |
응답의 url은 임시 미디어 게이트웨이 URL입니다.
- API key를 포함하지 마세요.
- 장기간 저장하지 마세요.
expiresAt에 가까워 다운로드가 실패하면 이 엔드포인트를 다시 호출하여 URL을 갱신하세요.- 미디어 게이트웨이는 Range GET을 지원하므로 대용량 파일을 이어받을 수 있습니다.
7.3 자막 다운로드
GET /video-translations/{taskId}/results/subtitles/{variant}variant | 내용 |
|---|---|
source | 원본 언어 자막 |
translated | 대상 언어 자막 |
bilingual | 이중 언어 자막 |
응답은 JSON이 아닌 일반 SRT 텍스트입니다.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8?download=1을 추가하면 첨부 파일 형태로 다운로드할 수 있습니다. 자막 엔드포인트도 Agent API이므로 API key가 필요합니다.
8. 재번역
POST /video-translations/{existingTaskId}/retranslate
Idempotency-Key: ${UNIQUE_IDEMPOTENCY_KEY}허용되는 기존 작업 상태:
completedfailedcancelled
요청 본문은 비워도 됩니다. 본문을 보내는 경우 현재 다음 옵션만 지원합니다.
{
"allowDynamicDuration": true
}재번역은 새 작업을 만들고 크레딧을 차감하며 동시 실행 제한을 적용받습니다. 반드시 Idempotency-Key를 사용하세요.
9. 복구 가능한 상태 머신
최소한 다음 상태를 저장하세요.
{
"apiBase": "https://xxx.com/api/v1",
"fileId": "string",
"uploadId": "string",
"key": "string",
"taskId": "string",
"idempotencyKey": "string",
"sourceLanguage": "auto",
"targetLanguage": "en",
"status": "pending|processing|completed|failed|cancelled"
}다음 값은 저장하지 마세요.
- 평문 Agent API key
- 업로드 presigned URL
- 임시 미디어 다운로드 URL
- 임시 서명 쿼리 파라미터, 서명 또는 인증 정보
권장 상태 머신:
NEW
-> UPLOAD_INITIATED
-> PART_PRESIGNED
-> PART_UPLOADED
-> UPLOAD_COMPLETED
-> TASK_CREATED
-> TASK_PROCESSING
-> TASK_COMPLETED
-> RESULT_DOWNLOADED복구 규칙:
uploadId와key가 있으면 완료되지 않은 파트의 URL을 다시 생성합니다.- 작업 생성 요청이 서버에 도달했을 수 있으면 동일한 본문과
Idempotency-Key로 다시 전송합니다. taskId가 있으면GET /video-translations/{taskId}부터 다시 시작합니다.- 다운로드 URL이 만료되면
results/files/{variant}또는results를 다시 요청합니다.
10. 오류 코드 빠른 참고
error.code | HTTP | 의미 | 자동 재시도 |
|---|---|---|---|
UNAUTHORIZED | 401 | API key가 없습니다. | 아니요 |
API_KEY_INVALID | 401 | API key가 올바르지 않습니다. | 아니요 |
API_KEY_EXPIRED | 401 | API key가 만료되었습니다. | 아니요 |
API_KEY_REVOKED | 401 | API key가 폐기되었습니다. | 아니요 |
RATE_LIMITED | 429 | 요청이 제한되었습니다. | 예, Retry-After만큼 대기 |
CONCURRENCY_LIMIT_REACHED | 429 | 계정의 동시 실행 제한에 도달했습니다. | 예, 작업 완료까지 대기 |
INSUFFICIENT_CREDITS | 402 | 크레딧이 부족합니다. | 아니요 |
INVALID_REQUEST | 400 | 요청 본문, 필드 또는 variant가 올바르지 않습니다. | 아니요 |
MISSING_IDEMPOTENCY_KEY | 400 | 멱등성 키가 없거나 올바르지 않습니다. | 아니요 |
INVALID_FILE_SIZE | 400 | 파일 크기가 0 이하입니다. | 아니요 |
FILE_TOO_LARGE | 400 | 파일이 너무 큽니다. | 아니요 |
INVALID_CONTENT_TYPE | 400 | MP4 파일이 아닙니다. | 아니요 |
INVALID_LANGUAGE | 400 | 지원하지 않는 언어입니다. | 아니요 |
INVALID_SPEAKER_COUNT | 400 | 화자 수가 올바르지 않습니다. | 아니요 |
INVALID_DURATION | 400 | 동영상 길이가 올바르지 않습니다. | 아니요 |
IDEMPOTENCY_KEY_REUSED | 409 | 다른 본문으로 같은 키를 재사용했습니다. | 아니요 |
IDEMPOTENCY_REQUEST_IN_PROGRESS | 409 | 같은 멱등 요청이 처리 중입니다. | 예, 같은 본문과 키로 나중에 재시도 |
UPLOAD_NOT_FOUND | 404 / 409 | 업로드 세션이 없거나 만료되었거나 허용되지 않는 상태입니다. | 상황에 따라 다름 |
UPLOAD_NOT_COMPLETED | 409 | 파일 업로드가 완료되지 않았습니다. | 아니요, 먼저 업로드 완료 |
TASK_NOT_FOUND | 404 | 작업이 없거나 권한이 없습니다. | 아니요 |
TASK_NOT_COMPLETED | 409 | 작업이 아직 완료되지 않았습니다. | 예, 계속 조회 |
LANGUAGE_PAIR_TASK_EXISTS | 409 | 같은 파일과 언어 조합의 작업이 이미 있습니다. | 아니요, details.existingTaskId 확인 |
FINAL_VIDEO_EXPIRED | 409 | 최종 동영상 결과가 만료되었습니다. | 아니요, 다시 처리하거나 복구 |
FINAL_AUDIO_EXPIRED | 409 | 최종 오디오 결과가 만료되었습니다. | 아니요, 다시 처리하거나 복구 |
RESULT_NOT_READY | 404 | 결과 파일이 아직 준비되지 않았습니다. | 예, 잠시 기다린 후 재시도 |
SUBTITLE_NOT_FOUND | 404 | 자막이 없거나 비어 있습니다. | 아니요, 완료 직후의 짧은 재시도 제외 |
| 모든 5xx | 500 / 503 | 서비스가 일시적으로 사용할 수 없습니다. | 예, 상한이 있는 지수 백오프 |
11. 외부 호출 참고
- 호출자는 이 문서에 적힌 Base URL, Agent API key 및 엔드포인트 경로만 있으면 됩니다.
- 업로드 presigned URL이나 임시 미디어 다운로드 URL을 캐시하거나 저장하지 마세요.
- API key, 업로드 URL 또는 미디어 다운로드 URL을 로그나 대화 컨텍스트에 기록하지 마세요.
- 다운로드 URL이 만료되면 결과 엔드포인트를 다시 호출하여 새 URL을 받으세요.
