Docs

SoulDub Video Translation Agent API Reference

Runtime reference for agents, backend services, and scripts that automate SoulDub video translation.

Audience: agents, backend services, and scripts that automate SoulDub video translation.

This document is a runtime reference for the skill. It does not replace scripts/video_translation.py. Normal translation runs should use the script first; read this document only when debugging API calls, authentication, idempotency, rate limits, result downloads, or error codes.

1. Agent Quick Reference

  • Current API base URL: https://xxx.com/api/v1.
  • External API paths are under /api/v1/....
  • All /api/v1/... endpoints require an Agent API key. Prefer Authorization: Bearer ${AGENT_API_KEY}.
  • Binary video uploads use PUT {presignedUrl} directly against the storage presigned URL and do not include an API key.
  • Media gateway URLs are downloaded directly and do not include an API key.
  • Subtitle download endpoints are still Agent API endpoints and require an API key.
  • Task creation and retranslation must include a stable Idempotency-Key.
  • Do not write API keys, upload presigned URLs, temporary media download URLs, or signature parameters to logs, state files, test reports, or prompts.
  • For 429 responses, wait according to Retry-After. Also wait for concurrency limits; do not retry aggressively.
  • When a temporary URL expires, request a fresh URL from the result endpoint.

2. Call Flow

OrderStageEndpoint
1Initiate uploadPOST /video-uploads/initiate
2Presign part URLPOST /video-uploads/presign-part
3Upload video bytesPUT {presignedUrl}
4Complete uploadPOST /video-uploads/complete
5Create translation taskPOST /video-translations
6Poll task statusGET /video-translations/{taskId}
7List resultsGET /video-translations/{taskId}/results
8Get one media URLGET /video-translations/{taskId}/results/files/{variant}
9Download subtitlesGET /video-translations/{taskId}/results/subtitles/{variant}

Optional retranslation:

POST /video-translations/{existingTaskId}/retranslate

3. Authentication and Common Conventions

The Agent API supports two authentication headers. Prefer the first:

Authorization: Bearer ${AGENT_API_KEY}
X-API-Key: ${AGENT_API_KEY}

Common JSON request headers:

Content-Type: application/json
Accept: application/json
Authorization: Bearer ${AGENT_API_KEY}
X-Request-Id: agent-run-20260704-0001

X-Request-Id is optional. If provided, it must be 8..64 characters and may contain only A-Z a-z 0-9 _ . : -. The server returns the final request id in response headers.

Successful responses use a JSON envelope:

{
  "code": 0,
  "message": "ok",
  "data": {}
}

Error responses also use a JSON envelope:

{
  "code": -1,
  "message": "INVALID_REQUEST",
  "data": null,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Validation failed.",
    "requestId": "req_abc123",
    "details": {}
  }
}

When debugging, inspect in this order:

  1. HTTP status
  2. error.code
  3. error.details
  4. X-Request-Id response header

Rate-limit headers may appear on all Agent API responses:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1782300000

Rate-limited response:

HTTP/1.1 429 Too Many Requests
Retry-After: 17

When RATE_LIMITED is returned, wait according to Retry-After before retrying.

4. Video Upload

4.1 Initiate Upload

POST /video-uploads/initiate

Request:

{
  "filename": "demo.mp4",
  "contentType": "video/mp4",
  "fileSizeBytes": 342556
}
FieldRequiredNotes
filenameYesMust be a .mp4 filename
contentTypeYesMust be exactly video/mp4
fileSizeBytesYesMust be greater than 0 and within the account file-size limit

Save fileId, uploadId, key, and bucket from the response. Later complete-upload and task-creation calls need them.

4.2 Presign Part URL

POST /video-uploads/presign-part

Request:

{
  "uploadId": "opaque-r2-upload-id",
  "key": "storage-key-from-initiate",
  "partNumber": 1,
  "expiresInSeconds": 900
}
FieldRequiredNotes
uploadIdYesReturned by upload initiation
keyYesReturned by upload initiation
partNumberYes1..10000
expiresInSecondsNoURL TTL; the server clamps it to 60..86400 seconds

Use the returned presignedUrl only for the current upload. Do not persist it to logs or state files.

4.3 Upload Video Bytes

PUT {presignedUrl}
Content-Type: video/mp4

The body is the video bytes. This request is not an Agent API request:

  • Do not include Authorization.
  • Do not include X-API-Key.
  • Do not log presignedUrl.
  • If the URL expires, call presign-part again.
  • A successful PUT usually returns 200. The client does not need to send ETag back to the Agent API.

4.4 Complete Upload

POST /video-uploads/complete

Request:

{
  "uploadId": "opaque-r2-upload-id",
  "key": "storage-key-from-initiate"
}

Confirm data.success == true, data.status == "completed", and data.fileId matches the fileId returned by upload initiation.

5. Create Translation Task

POST /video-translations
Idempotency-Key: ${UNIQUE_IDEMPOTENCY_KEY}

Request:

{
  "fileId": "uploaded-file-id",
  "videoDurationSeconds": 6,
  "checksumSha256": "optional-file-sha256",
  "sourceLanguage": "auto",
  "targetLanguage": "en",
  "speakerCount": 1,
  "allowDynamicDuration": true
}
FieldRequiredNotes
fileIdYesCompleted upload fileId
videoDurationSecondsYes1..3600 seconds
checksumSha256NoSource file SHA-256 for tracking and validation
sourceLanguageYesSource language; auto is allowed
targetLanguageYesTarget language; auto is not allowed
speakerCountNo0..15; default 0 means auto-detect
allowDynamicDurationNotrue allows dynamic duration synchronization

Supported languages are defined by references/languages.json and server-side validation.

Save taskId, fileId, creditsConsumed, and the Idempotency-Key used for this request.

Credits are rounded up by minute:

durationInMinutes = max(1, ceil(videoDurationSeconds / 60))
creditsConsumed = durationInMinutes * credit.points_per_minute

The current default is credit.points_per_minute=3; the actual value is determined by server configuration.

CaseResult
First successful requestCreates task, charges credits, returns taskId
Same key + same body againReturns the same response without charging again
Same key + different body409 IDEMPOTENCY_KEY_REUSED
Same key request still processing409 IDEMPOTENCY_REQUEST_IN_PROGRESS

If a network timeout leaves uncertainty about whether the server received the request, retry the exact same body with the same Idempotency-Key.

6. Query Task Progress

GET /video-translations/{taskId}
FieldNotes
taskIdTask ID
statuspending, processing, completed, failed, cancelled
progressTask progress, usually 0..100
errorMessageFailure reason; usually empty for success or in-progress tasks
statusTerminalHandling
pendingNoKeep polling
processingNoKeep polling
completedYesFetch results
failedYesStop polling and report errorMessage
cancelledYesStop polling

Recommended polling strategy:

  1. Query for the first time 2 seconds after task creation.
  2. Back off while incomplete: 2s -> 5s -> 10s -> 20s -> 30s.
  3. Recommended maximum interval is 30 seconds.
  4. For HTTP 429, wait according to Retry-After.
  5. For HTTP 5xx, use exponential backoff with an overall timeout.

Stop polling only when the API returns completed, failed, or cancelled.

7. Fetch Results

7.1 List Results

GET /video-translations/{taskId}/results?expiresIn=600

The task must be completed. expiresIn is optional. Media download URL TTL is clamped by the server to 60..3600 seconds and defaults to 3600.

typevariantNotes
videovideoFinal composed MP4 video
audiovocalSeparated vocal WAV
audiobackgroundSeparated background WAV
audiofullComplete synthesized vocal audio WAV
subtitlesourceSource-language SRT subtitles
subtitletranslatedTarget-language SRT subtitles
subtitlebilingualBilingual SRT subtitles

Handling rules:

  • URLs with access=media_gateway are downloaded directly without an API key.
  • Subtitle URLs with access=api and requiresAuthorization=true need the API key.
  • If a media URL expires, call this endpoint or the single-file endpoint again to get a fresh URL.

7.2 Get One Media File URL

GET /video-translations/{taskId}/results/files/{variant}?expiresIn=600
variantContent
videoFinal composed MP4 video
vocalVocal WAV
backgroundBackground WAV
fullComplete synthesized vocal audio WAV

The response url is a temporary media gateway URL:

  • Do not include the API key.
  • Do not persist it long term.
  • If download fails near expiresAt, call this endpoint again to refresh the URL.
  • The media gateway supports Range GET for resumable large-file downloads.

7.3 Download Subtitles

GET /video-translations/{taskId}/results/subtitles/{variant}
variantContent
sourceSource-language subtitles
translatedTarget-language subtitles
bilingualBilingual subtitles

The response is plain SRT text, not JSON:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

Append ?download=1 to request attachment-style download. The subtitle endpoint is still an Agent API endpoint and requires an API key.

8. Retranslation

POST /video-translations/{existingTaskId}/retranslate
Idempotency-Key: ${UNIQUE_IDEMPOTENCY_KEY}

Allowed existing task statuses:

  • completed
  • failed
  • cancelled

The request body may be empty. If a body is provided, the API currently supports only:

{
  "allowDynamicDuration": true
}

Retranslation creates a new task, charges credits, is subject to concurrency limits, and must use an Idempotency-Key.

9. Recoverable State Machine Guidance

Persist at least:

{
  "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"
}

Do not persist:

  • Plaintext Agent API key
  • Upload presigned URL
  • Temporary media download URL
  • Temporary signature query parameters, signatures, or credentials

Recommended state machine:

NEW
  -> UPLOAD_INITIATED
  -> PART_PRESIGNED
  -> PART_UPLOADED
  -> UPLOAD_COMPLETED
  -> TASK_CREATED
  -> TASK_PROCESSING
  -> TASK_COMPLETED
  -> RESULT_DOWNLOADED

Recovery rules:

  • If uploadId and key exist, presign unfinished parts again.
  • If task creation may have reached the server, replay the same create body with the same Idempotency-Key.
  • If taskId exists, resume from GET /video-translations/{taskId}.
  • If a download URL expires, request results/files/{variant} or results again.

10. Error Code Quick Reference

error.codeHTTPMeaningAuto-retry
UNAUTHORIZED401Missing API keyNo
API_KEY_INVALID401Invalid API keyNo
API_KEY_EXPIRED401Expired API keyNo
API_KEY_REVOKED401Revoked API keyNo
RATE_LIMITED429Rate-limitedYes, wait for Retry-After
CONCURRENCY_LIMIT_REACHED429Account concurrency limit reachedYes, wait for a task to finish
INSUFFICIENT_CREDITS402Insufficient creditsNo
INVALID_REQUEST400Invalid request body, field, or variantNo
MISSING_IDEMPOTENCY_KEY400Missing or invalid idempotency keyNo
INVALID_FILE_SIZE400Non-positive file sizeNo
FILE_TOO_LARGE400File too largeNo
INVALID_CONTENT_TYPE400Not an MP4 fileNo
INVALID_LANGUAGE400Unsupported languageNo
INVALID_SPEAKER_COUNT400Invalid speaker countNo
INVALID_DURATION400Invalid video durationNo
IDEMPOTENCY_KEY_REUSED409Same key reused with a different bodyNo, upper layer must decide
IDEMPOTENCY_REQUEST_IN_PROGRESS409Same idempotent request is still processingYes, retry same body and key later
UPLOAD_NOT_FOUND404 / 409Upload session missing, expired, or not in an allowed stateDepends
UPLOAD_NOT_COMPLETED409File upload is not completeNo, complete upload first
TASK_NOT_FOUND404Task missing or unauthorizedNo
TASK_NOT_COMPLETED409Task is not completeYes, keep polling
LANGUAGE_PAIR_TASK_EXISTS409Existing task for the same file and language pairNo, inspect details.existingTaskId
FINAL_VIDEO_EXPIRED409Final video output expiredNo, reprocess or recover
FINAL_AUDIO_EXPIRED409Final audio output expiredNo, reprocess or recover
RESULT_NOT_READY404Result file not ready yetYes, wait briefly and retry
SUBTITLE_NOT_FOUND404Subtitle missing or emptyNo, except for brief retry just after completion
Any 5xx500 / 503Service temporarily unavailableYes, exponential backoff with a cap

11. External Call Notes

  • Callers only need the Base URL, Agent API key, and endpoint paths listed in this document.
  • Do not cache or persist upload presigned URLs or temporary media download URLs.
  • Do not write API keys, upload presigned URLs, or media download URLs to logs or conversation context.
  • When a download URL expires, call the result endpoint again to get a fresh URL.