업로드하고 제출한 뒤 웹훅을 기다리세요.
MediaRuntime은 비동기로 동작합니다. 제출에 성공하면 즉시 QUEUED를 반환하고, 완성된 출력은 이후 서명된 웹훅으로 전달됩니다.
# 1. Ask MediaRuntime for a short-lived upload URL.
UPLOAD=$(curl -sS -X POST "https://mediaruntime.com/v1/upload-url" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"filename":"launch-trailer.mp4","content_type":"video/mp4"}')
UPLOAD_URL=$(printf '%s' "$UPLOAD" | jq -r .upload_url)
FILE_URI=$(printf '%s' "$UPLOAD" | jq -r .file_uri)
UPLOAD_CONTENT_TYPE=$(printf '%s' "$UPLOAD" | jq -r '.upload_headers["Content-Type"]')
UPLOAD_AUTH=$(printf '%s' "$UPLOAD" | jq -r '.upload_headers.Authorization // empty')
# 2. Upload the bytes. Send every returned upload header.
UPLOAD_ARGS=(-H "Content-Type: $UPLOAD_CONTENT_TYPE")
if [[ -n "$UPLOAD_AUTH" ]]; then
UPLOAD_ARGS+=(-H "Authorization: $UPLOAD_AUTH")
fi
curl -sS -X PUT "$UPLOAD_URL" \
"${UPLOAD_ARGS[@]}" \
--upload-file ./launch-trailer.mp4
# 3. Queue an asynchronous job using the returned file_uri.
curl -sS -X POST "https://mediaruntime.com/v1/transcode" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg file_uri "$FILE_URI" '{
file_url: $file_uri,
metadata: { asset_id: "asset_0426", media_type: "video" },
outputs: [{
type: "mp4",
preset: "mp4_720p_h264_aac",
poster_time_sec: 2
}]
}')"{
"job_id": "job_1320c28b72104811b075a26a99496cf6",
"status": "QUEUED",
"tier": "standard",
"msg": "accepted"
}API 키는 서버에만 보관하세요.
계정 → API 키에서 키를 생성하세요. 원본 키는 한 번만 표시되며 시크릿 매니저에 보관해야 합니다. 브라우저 코드, 모바일 바이너리, 로그, 소스 관리에는 절대 두지 마세요.
헤더
모든 /v1 요청에 X-API-Key를 보내세요.
보관
MEDIARUNTIME_API_KEY를 서버 측 시크릿 매니저에 저장하세요.
교체
새 키를 만들어 배포하고 트래픽을 확인한 뒤 이전 키를 폐기하세요.
metadata가 연동 작업을 대신하게 하세요.
wMedia가 사용하는 프로덕션 패턴은 의도적으로 단순합니다. 입력과 출력 레시피, 그리고 최종 이벤트를 자체 데이터베이스 레코드와 다시 연결할 수 있을 만큼의 metadata를 제출하면 됩니다.
{
"file_url": "gs://bucket/path/source.mp4",
"metadata": {
"producer": "my-api",
"entity_id": "video_01J8Y4",
"owner_id": "user_482",
"media_type": "video",
"trace_id": "req_f839"
},
"moderation": {
"enabled": true,
"mode": "report",
"checks": ["sexual", "violence", "dangerous"]
},
"watermark": { "enabled": true },
"outputs": [
{
"type": "mp4",
"preset": "mp4_720p_h264_aac",
"path_suffix": "web",
"poster_time_sec": 2,
"gif_preview": {
"enabled": true,
"width": 320,
"fps": 8,
"start_time": 2,
"duration": 2.5
}
},
{
"type": "image",
"preset": "image_multi_v1",
"path_suffix": "covers",
"images": [
{ "width": 1280, "height": 720, "mode": "fit", "format": "webp", "quality": 84 },
{ "width": 480, "height": 480, "mode": "cover", "format": "webp", "quality": 80 }
]
}
]
}| 필드 | 타입 | 설명 |
|---|---|---|
| file_url | string | gs:// 또는 공개 HTTPS 입력 하나. inputs와 함께 사용할 수 없습니다. |
| inputs | array | 1~25개 입력의 배치 팬아웃. 각 항목은 input_id와 metadata를 가질 수 있습니다. |
| outputs | array | 1~10개의 출력 레시피. 각 항목에 type이 필요하며 preset 지정을 강력히 권장합니다. |
| metadata | object | 최대 32KiB의 JSON. 저장된 뒤 meta.request_metadata로 그대로 반환됩니다. |
| moderation | object | Premium 시각 미디어 검사: sexual, violence, dangerous. |
| watermark | object | Premium 시각 미디어 오버레이. 계정에 PNG 로고가 미리 등록되어 있어야 합니다. |
배치 팬아웃
모든 입력에 동일한 출력이 필요할 때 배치를 사용하세요. 입력별 metadata는 각 하위 작업에 병합되며, 상위 작업이 배치의 기준이 됩니다.
{
"inputs": [
{
"file_url": "https://cdn.example.com/a.mp4",
"input_id": "asset-a",
"metadata": { "position": 0 }
},
{
"file_url": "https://cdn.example.com/b.mp4",
"input_id": "asset-b",
"metadata": { "position": 1 }
}
],
"metadata": { "batch_id": "import_2026_08_09" },
"outputs": [{ "type": "mp4", "preset": "mp4_720p_h264_aac" }]
}엔진이 생성할 결과물을 선택하세요.
type과 preset이 하나의 실행 가능한 레시피를 이룹니다. 아래에 표시된 type을 정확히 사용하세요. preset 이름만으로는 출력 type이 바뀌지 않으며, 짝이 맞지 않으면 거부되거나 잘못 라우팅될 수 있습니다.
{
"file_url": "https://cdn.example.com/landscape-interview.mp4",
"outputs": [{
"type": "social",
"preset": "social_vertical_blur"
}]
}social_vertical_blur는 1080×1920 H.264/AAC MP4를 만듭니다. 원본을 잘라내지 않고 맞춰 넣고, 흐리게 처리한 복사본이 9:16 캔버스의 배경을 채웁니다. Reels, TikTok, Shorts에 적합합니다. 세로 출력 높이가 1920픽셀이므로 Premium 레시피입니다.비디오 파일 및 포스터
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| mp4_720p_h264_aac (type: mp4) | 비디오 | 웹 재생을 위해 faststart가 적용된 720p H.264/AAC MP4입니다. | Standard |
| mp4_ladder_v1 (type: mp4) | 비디오 | 1080p, 720p, 480p의 MP4 렌디션 세 개와 각 렌디션의 포스터를 생성합니다. | Standard |
| transmux_mp4_fast (type: mp4) | 호환 가능한 오디오/비디오 | 재인코딩 없이 기존 스트림을 faststart MP4로 복사합니다. 복사가 실패하고 폴백이 활성화되어 있으면 720p H.264 프리셋으로 재인코딩합니다. | Standard |
| poster_frame_v1 (type: mp4) | 비디오 | poster_time_sec 시점에서 캡처한 720p JPG 한 장입니다. 요청 type은 여전히 mp4입니다. | Standard |
| mp4_hevc_1080p (type: mp4) | 비디오 | Apple 재생을 위한 hvc1 태그가 적용된 1080p HEVC/H.265 + AAC MP4입니다. | Premium |
| mp4_av1_smart (type: mp4) | 비디오 | 압축 효율에 최적화된 1080p AV1 + Opus MP4이며, 인코딩에 CPU를 많이 사용합니다. | Premium |
| mov_prores_422 (type: mp4) | 비디오 | ProRes 422 HQ + PCM MOV 편집 마스터입니다. 원본 해상도를 유지하며 용량이 큰 중간 파일을 생성합니다. | Premium |
소셜 영상
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| social_vertical_blur (type: social) | 비디오 | 1080×1920 H.264/AAC MP4입니다. Reels, TikTok, Shorts용으로 흐린 9:16 배경 위에 원본을 맞춰 배치합니다. | Premium |
애니메이션 GIF
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| gif_hq (type: gif) | 비디오 | 가로 480px, 15fps 애니메이션 GIF이며 팔레트 생성을 사용해 색상 품질을 높입니다. | Standard |
프레임 추출
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| extract_frames_1 (type: frames) | 비디오 | 초당 1프레임으로 샘플링한 번호가 매겨진 JPG 프레임 시퀀스입니다. | Standard |
| extract_frames_5 (type: frames) | 비디오 | 초당 5프레임으로 샘플링한 번호가 매겨진 JPG 프레임 시퀀스입니다. | Standard |
스트리밍
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| hls_ladder_v1 (type: hls) | 비디오 | 1080p와 720p H.264/AAC 변형, 마스터 재생목록, 6초 세그먼트로 구성된 HLS VOD 패키지입니다. | Standard |
오디오
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| audio_copy_fast (type: audio) | 오디오 또는 오디오가 있는 비디오 | 재인코딩 없이 원본 오디오 스트림을 복사합니다. 복사가 실패하고 폴백이 활성화되어 있으면 128kbps AAC로 기록합니다. | Standard |
| audio_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | faststart가 적용된 M4A 파일의 128kbps AAC입니다. | Standard |
| audio_mp3_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 128kbps MP3 파일입니다. | Standard |
| audio_opus_96k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 음성 전달에 적합한 96kbps Opus 파일입니다. | Standard |
| audio_loudnorm_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | -16 LUFS를 목표로 정규화한 뒤 128kbps AAC로 기록합니다. | Standard |
| audio_trim_silence_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 앞뒤 무음을 제거한 뒤 128kbps AAC로 기록합니다. | Premium |
| audio_loudnorm_trim_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 경계의 무음을 잘라내고 -16 LUFS로 정규화한 뒤 128kbps AAC로 기록합니다. | Premium |
| audio_whisper_prep (type: audio) | 오디오 또는 오디오가 있는 비디오 | Whisper, ASR 등 음성 파이프라인을 위해 준비된 16kHz 모노 PCM WAV입니다. | Premium |
이미지 파생본
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| image_multi_v1 (type: image) | 이미지 | fit, fill, cover, contain 중 선택한 방식으로 요청한 images 배열을 생성합니다. 티어는 포맷, 크기, 개수, 스마트 크롭, 배경 제거 여부에 따라 달라집니다. | Standard 또는 Premium |
transmux_mp4_fast는 화질이 바뀌는 인코딩을 피합니다. 복사가 실패하고 폴백이 활성화되어 있으면 mp4_720p_h264_aac로 재인코딩하므로, 출력 특성을 예측 가능하게 유지하려면 해당 프리셋을 직접 사용하세요.한 번 업로드하고 제품에 필요한 포맷과 부가 결과물을 만드세요.
원본 확장자가 출력을 결정하지 않습니다. type과 preset이 실행할 레시피를 선택하므로, 업로드한 영상 하나로 재생용 영상, 오디오 전용 미디어, 자막, GIF 미리보기, 포스터, 프레임 시퀀스를 같은 비동기 작업에서 만들 수 있습니다.
| 원본 | 결과물 | 레시피 |
|---|---|---|
| JPG, PNG 또는 WebP | JPG, PNG, WebP 또는 AVIF 파생본 | image + image_multi_v1을 사용하고 images[].format, 크기, mode, quality를 지정합니다. |
| 비디오 | 웹 MP4, HLS, 소셜 영상 또는 편집 마스터 | 알맞은 mp4, hls 또는 social 프리셋을 선택합니다. |
| 비디오 | 애니메이션 GIF, 포스터 또는 JPG 프레임 시퀀스 | gif_hq, poster_frame_v1, extract_frames_1/5를 사용하거나 비디오 출력에 gif_preview를 추가합니다. |
| 비디오 또는 오디오 | M4A, MP3, Opus 또는 음성용 WAV | 해당하는 audio_* 프리셋을 선택합니다. 비디오 입력은 오디오 스트림이 추출됩니다. |
| 비디오 또는 음성 오디오 | SRT, WebVTT 또는 둘 다 | 오디오나 비디오 출력에 subtitles를 추가하고 srt, vtt 또는 both를 선택합니다. |
{
"file_url": "gs://value-returned-by-upload-url",
"metadata": {
"media_type": "image",
"asset_id": "product-photo-0426"
},
"outputs": [{
"type": "image",
"preset": "image_multi_v1",
"path_suffix": "converted",
"images": [
{ "width": 1600, "height": 1200, "mode": "fit", "format": "jpg", "quality": 88 },
{ "width": 1600, "height": 1200, "mode": "fit", "format": "webp", "quality": 82 }
]
}]
}{
"file_url": "gs://value-returned-by-upload-url",
"outputs": [
{ "type": "mp4", "preset": "mp4_720p_h264_aac", "path_suffix": "web-video" },
{ "type": "audio", "preset": "audio_mp3_128k", "path_suffix": "audio-only" }
]
}{
"file_url": "gs://value-returned-by-upload-url",
"metadata": { "media_type": "video", "asset_id": "interview-0426" },
"outputs": [{
"type": "audio",
"preset": "audio_aac_128k",
"path_suffix": "audio-and-transcript",
"subtitles": {
"enabled": true,
"languages": ["auto"],
"format": "both",
"model": "ggml-base.bin",
"translate_to_english": false
}
}]
}{
"file_url": "gs://value-returned-by-upload-url",
"metadata": { "media_type": "video", "asset_id": "trailer-0426" },
"outputs": [{
"type": "mp4",
"preset": "mp4_720p_h264_aac",
"path_suffix": "web",
"poster_time_sec": 4,
"poster_format": "jpg",
"gif_preview": {
"enabled": true,
"width": 480,
"fps": 10,
"start_time": 4,
"duration": 3
}
}]
}{
"file_url": "gs://value-returned-by-upload-url",
"metadata": { "media_type": "video", "asset_id": "stream-0426" },
"outputs": [{
"type": "hls",
"preset": "hls_ladder_v1",
"path_suffix": "stream"
}]
}{
"file_url": "gs://value-returned-by-upload-url",
"metadata": { "media_type": "video", "asset_id": "clip-0426" },
"outputs": [
{ "type": "gif", "preset": "gif_hq", "path_suffix": "animated-preview" },
{ "type": "frames", "preset": "extract_frames_1", "path_suffix": "sampled-frames" }
]
}hls_ladder_v1은 마스터 재생목록, 1080p와 720p H.264/AAC 변형 재생목록, 6초 미디어 세그먼트를 생성합니다. 보고된 마스터 재생목록 URL을 사용하거나 번들 전체를 함께 옮기세요.gif_hq는 480px, 15fps의 완전한 기본 GIF를 만듭니다. gif_preview는 다른 비디오 출력에 길이가 짧고 시간이 명시된 GIF를 추가합니다. 프레임 프리셋은 초당 1개 또는 5개의 번호가 매겨진 JPG 시퀀스를 반환합니다.알아두면 좋은 옵션
audio_aac_128k는 M4A를, audio_mp3_128k는 MP3를, audio_opus_96k는 Opus를, audio_whisper_prep는 16kHz 모노 WAV를 반환합니다. subtitles.format에는 srt, vtt 또는 both를 지정합니다. 포스터 이미지 한 장만 필요하면 type: mp4와 preset: poster_frame_v1, 원하는 poster_time_sec를 함께 보내세요.
프리셋으로 시작하고 꼭 필요한 것만 재정의하세요.
프리셋은 요청을 읽기 쉽게 유지하고 엔진에 안정적인 기준을 제공합니다. 렌디션, 자막, 미리보기, 코덱, 비트레이트 옵션은 제품에 필요할 때만 명시적으로 추가하세요.
{
"file_url": "https://cdn.example.com/source.jpg",
"outputs": [{
"type": "image",
"preset": "image_multi_v1",
"images": [
{ "width": 1200, "height": 630, "mode": "cover", "format": "webp", "quality": 84 },
{ "width": 320, "height": 320, "mode": "cover", "format": "webp", "quality": 78 }
]
}]
}{
"file_url": "https://cdn.example.com/interview.wav",
"outputs": [{
"type": "audio",
"preset": "audio_aac_128k",
"subtitles": {
"enabled": true,
"languages": ["auto"],
"format": "both",
"model": "ggml-base.bin"
}
}]
}{ "watermark": { "enabled": true } }만 보내면 MediaRuntime이 서버에 저장된 로고를 사용합니다.샘플링 기반 시각 안전성 보고서를 작업에 첨부하세요.
콘텐츠 검토는 이미지 또는 비디오 입력 하나에 대해 트랜스코딩 전에 실행됩니다. 현재 프로덕션 계약은 보고서 전용이며, 보고서는 증거와 판정을 기록할 뿐 트랜스코딩을 차단하지 않습니다.
{
"file_url": "https://cdn.example.com/upload.mp4",
"metadata": { "media_type": "video", "asset_id": "asset_0426" },
"moderation": {
"enabled": true,
"mode": "report",
"checks": ["sexual", "violence", "dangerous"]
},
"outputs": [{
"type": "mp4",
"preset": "mp4_720p_h264_aac"
}]
}{
"moderation": {
"requested": {
"enabled": true,
"mode": "report",
"checks": ["sexual", "violence", "dangerous"],
"media_type": "video",
"phase": "phase1_video_report"
},
"result": {
"ok": true,
"media_type": "video",
"verdict": "review",
"flagged_checks": ["violence"],
"scores": {
"violence": { "yes": 0.82, "no": 0.18 }
},
"decisions": {
"violence": { "decision": "review", "raw_decision": "review" }
},
"evidence": {
"frames_sampled": 8,
"frames_flagged": [{
"frame_index": 3,
"timestamp_sec": 20,
"verdict": "review",
"flagged_checks": ["violence"]
}]
},
"video": {
"frame_interval_sec": 10,
"max_frames": 24
}
}
},
"meta": {
"moderation_result": {
"url": "https://storage.googleapis.com/.../moderation_result.json"
}
},
"usage": {
"breakdown": { "moderation_units": 120 }
}
}| Contract | Value | Notes |
|---|---|---|
| Plan | Premium | The API returns 403 unless the account is Premium or auto-upgrade is allowed. |
| Mode | report | This is the only accepted mode today. Do not send block. |
| Checks | sexual, violence, dangerous | Send one to three checks. Omitting checks selects all three. |
| Inputs | One image or video | Audio-only inputs, batches, and Sandbox moderation are rejected. |
| Video sampling | Fixed interval, bounded frames | The service chooses the interval and cap; read the actual values from result.video and result.evidence. |
| Decision | allow, review, or block signal | Report mode never blocks execution. Use result.verdict, decisions, flagged_checks, and evidence in your own policy. |
| Artifact | meta/moderation_result.json | Included in the output ZIP and exposed through meta.moderation_result.url when available. |
| Billing | Separate moderation units | Estimated and settled with the job at usage.breakdown.moderation_units. |
review는 사람이 확인하는 대기열, 게시 보류 등 직접 제어하는 정책을 위한 신호로 다루세요. 보고서가 완료되었다는 것이 해당 미디어가 안전하거나 합법적이거나 정책을 준수한다는 보장은 아닙니다.이벤트를 신뢰하기 전에 원본 바이트를 검증하세요.
최종 이벤트는 최소 한 번 전달되며 순서는 보장되지 않습니다. HMAC-SHA256을 검증하고, 오래된 타임스탬프를 거부하고, event_id로 중복을 제거하고, 빠르게 응답한 뒤 무거운 작업은 큐로 넘기세요.
{
"event_id": "webhook_evt_job_1320c28b72104811b075a26a99496cf6",
"job_id": "job_1320c28b72104811b075a26a99496cf6",
"account_id": "acc_xxx",
"status": "COMPLETED",
"completedAt": "2026-08-09T02:41:23Z",
"billing": { "status": "PAID", "estimatedUnits": 31 },
"usage": { "units_total": 31, "breakdown": {} },
"delivery": {
"mode": "PULL",
"retentionDays": 7,
"expiresAt": "2026-08-16T02:41:23Z",
"bundle": {
"type": "zip",
"filename": "outputs.zip",
"download": {
"url": "https://mediaruntime.com/v1/jobs/job_1320c28b72104811b075a26a99496cf6/bundle?token=...",
"expiresAt": "2026-08-16T02:41:23Z"
}
}
},
"meta": {
"engine_result_url": "https://storage.googleapis.com/...",
"outputs_root_gs": "gs://.../jobs/acc_xxx/job_.../outputs",
"request_metadata": {
"producer": "my-api",
"entity_id": "video_01J8Y4",
"media_type": "video"
}
}
}delivery.bundle.download.url에서 전체 ZIP을 내려받거나, meta.engine_result_url을 조회해 개별 출력과 산출물 경로를 확인하세요.import crypto from "node:crypto";
import express from "express";
const app = express();
// Register this route before any express.json() middleware.
app.post("/webhooks/mediaruntime", express.raw({ type: "application/json" }), (req, res) => {
const eventId = req.get("X-Transcoder-Id") || "";
const signatureHeader = req.get("X-Transcoder-Signature") || "";
const parts = Object.fromEntries(
signatureHeader.split(",").map((part) => part.trim().split("="))
);
const timestamp = parts.t || "";
const received = parts.v1 || "";
const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!eventId || !timestamp || !received || ageSeconds > 300) {
return res.sendStatus(401);
}
const expected = crypto
.createHmac("sha256", process.env.MEDIARUNTIME_WEBHOOK_SECRET)
.update(timestamp + "." + eventId + ".")
.update(req.body) // raw Buffer; never JSON.stringify(req.body)
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(received, "utf8");
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
// Enqueue work and deduplicate on event.event_id in your database.
console.log(event.event_id, event.job_id, event.status);
return res.sendStatus(200);
});전달 규칙
- 서명 검증과 영속적인 큐 적재/중복 제거가 끝난 뒤에만 2xx를 반환하세요.
- event_id를 멱등성 키로 사용하세요.
- meta.request_metadata를 사용하면 별도의 조회 테이블 없이 엔터티를 찾을 수 있습니다.
- delivery.expiresAt 이전에 보관된 출력을 내려받으세요.
- FAILED 또는 REJECTED 이벤트에는 error.code/message가 있으며 사용할 수 있는 번들은 없습니다.
선불 충전, 사용한 만큼 결제, 실제 사용량 기준 정산.
카드를 등록하고 지갑을 충전하면 정기 구독 없이 작업을 제출할 수 있습니다. MediaRuntime은 실행 전에 예상 금액을 예약하고, 작업이 최종 상태에 도달하면 최종 금액을 정산합니다.
| 플랜 | 공개 요금 | 최소 충전액 | 기본 자동 충전 |
|---|---|---|---|
| Standard Pay-As-You-Go | $0.02/min | $20.00 | 잔액 $2.00 시점에 $20.00 |
| Premium Pay-As-You-Go | $0.05/min | $60.00 | 잔액 $5.00 시점에 $60.00 |
billing 및 usage 필드를 사용하세요.지갑 규칙
- 사용 가능 잔액은 지갑 잔액에서 실행 중인 작업에 예약된 금액을 뺀 값입니다.
- 사용 가능 잔액이 부족하면 실행 전에 HTTP 402를 반환합니다.
- 자동 충전은 선택 사항이며 등록된 카드가 필요합니다.
- 업그레이드가 허용되지 않은 상태에서 Premium 전용 요청을 보내면 403을 반환합니다.
전송 실패는 재시도하고, 잘못된 요청은 재시도하지 마세요.
대부분의 API 오류는 최상위 detail 필드를 사용합니다. 문자열일 수도 구조화된 객체일 수도 있으므로 상관관계 ID와 함께 전체 응답을 기록하되, API 키는 절대 기록하지 마세요.
| 상태 코드 | 의미 | 연동에서 해야 할 일 |
|---|---|---|
| 400 | 요청이 논리적으로 잘못되었거나 추정기가 거부했습니다. | 요청을 수정하세요. 그대로 재시도하지 마세요. |
| 401 | API 키가 유효하지 않거나 만료 또는 폐기되었습니다. | 키를 수정하거나 교체하세요. 무작정 재시도하지 마세요. |
| 402 | 계정, 지갑 또는 결제 사전 점검이 작업 비용을 감당할 수 없습니다. | 먼저 지갑을 충전하거나 결제 문제를 해결하세요. |
| 403 | 플랜, 역할 또는 기능 제한이 요청을 허용하지 않습니다. | 플랜이나 요청을 변경하세요. 그대로 재시도하지 마세요. |
| 413 | HTTP 요청 본문이 2MiB를 초과합니다. | 미디어는 별도로 업로드하고 URL만 보내세요. |
| 422 | JSON이 API 스키마와 일치하지 않습니다. | 지정된 필드를 수정하세요. |
| 429 | 계정 또는 키에 속도 제한이 적용되었습니다. | 지수 백오프와 지터를 적용해 재시도하세요. |
| 500/502/503 | 일시적인 플랫폼 의존성 오류이거나 레인이 중지되었습니다. | 백오프를 적용해 안전하게 재시도하고 상관관계 metadata를 유지하세요. |
{
"detail": "Insufficient wallet balance for this job"
}대부분의 연동에 필요한 최소한의 표면.
아래 서버 간 엔드포인트는 모두 X-API-Key를 사용합니다. 토큰이 포함된 번들 URL만 예외이며, 자체적으로 수명이 짧고 작업 범위로 제한된 자격 증명을 갖습니다.
| 메서드 | 경로 | 용도 |
|---|---|---|
| POST | /v1/upload-url | 입력 버킷에 15분간 유효한 업로드 대상을 생성합니다. |
| POST | /v1/transcode | 단일 입력 또는 배치 미디어 작업을 큐에 등록합니다. |
| GET | /v1/jobs/{job_id}/bundle?token=... | 작업 범위 토큰으로 번들을 받습니다. API 키가 필요하지 않습니다. |
| POST | /v1/jobs/{job_id}/retry-webhook | 본인 소유 작업의 최종 웹훅을 다시 전송합니다. |
| POST | /v1/account/watermark-logo/upload-url | 계정 PNG 로고용 업로드 대상을 생성합니다. |
| POST | /v1/account/watermark-logo/confirm | 로고와 배치 설정을 확정합니다. |