요청 형식 주의사항 POST 요청 시
Content-Type: application/json헤더와 함께 JSON body를 그대로 전달하는 방식을 권장합니다.application/x-www-form-urlencoded를 사용할 경우에는 파라미터를 JSON으로 직렬화한 뒤payload필드에 담아 전송하십시오. 예:payload={"apiKey":"...","secret":"..."}이 문서 예시의 JSON에 붙은
//주석은 설명용입니다. 표준 JSON은 주석을 허용하지 않으므로, 실제 요청 본문에는 주석을 넣지 마세요. (주석이 포함된 블록은jsonc로 표기했습니다.)
이 문서의 마크다운 원문: https://videostew.com/ko/dev/docs.md (영문: https://videostew.com/en/dev/docs.md) — AI 도구에 문서 전체를 넘길 때 이 주소를 쓰면 됩니다.
이 API에는 버전 접두사가 없습니다. 베이스 URL은
https://videostew.com/api입니다./api/v1/...,/api/v2/...같은 경로는 존재하지 않습니다.리소스명은 항상 복수형입니다.
/api/project가 아니라/api/projects,/api/export가 아니라/api/exports입니다.아래 표에 없는 경로는 공개 API가 아닙니다. 경로를 추측해서 호출하지 마세요. 유효한 토큰으로 인증에 성공한 요청이 존재하지 않는
/api/경로를 부르면 안내용404JSON이 돌아오며, 이 때문에 IP가 차단되지는 않습니다. 인증은 경로보다 먼저 검사하므로 토큰이 없거나 유효하지 않으면 경로와 무관하게401이 돌아옵니다 — 이때는 경로를 바꿔 재시도하지 말고 토큰부터 확인하세요. 또한 인증 없이 존재하지 않는 경로를 1분에 30회 이상 호출하면 IP가 자동으로 차단됩니다. 차단되면 이후 모든 요청이 사람 인증(CAPTCHA) 페이지와 함께 HTTP 405를 반환하며, 정상적인 API 토큰을 보내도 마찬가지입니다. 차단된 경우 https://videostew.com/info/release-ip 에서 직접 해제할 수 있습니다.
모든 /api/ 요청에는 Authorization: Bearer <API 토큰> 과 x-nonce 헤더가 필요합니다. API 토큰은 계정 설정 > API 토큰에서 발급합니다.
| 메서드 | 경로 | 설명 |
|---|---|---|
| POST | /api/automations |
프로젝트 생성과 추출을 한 번에 자동화 (Automation API) |
| GET | /api/spaces |
워크스페이스 목록 조회 |
| GET | /api/projects |
프로젝트 목록 조회 |
| GET | /api/projects/:projectId |
단일 프로젝트 조회 |
| GET | /api/exports |
추출 목록 조회 |
| GET | /api/exports/:projectId |
특정 프로젝트의 추출 조회 (추출 상태 폴링용) |
| GET | /api/exports/:projectId/:exportId |
단일 추출 조회 |
| POST | /api/exports/:projectId |
기존 프로젝트의 추출 요청 |
각 엔드포인트의 파라미터와 호출 예시는 기타 API 섹션을 참고하십시오.
SDK를 사용하는 경우에만 필요한 별도 엔드포인트입니다(인증 방식이 다릅니다. SDK 섹션 참고).
| 메서드 | 경로 | 설명 |
|---|---|---|
| POST | /auth/access-token |
SDK 액세스 토큰 발급 |
| POST | /auth/revoke-access-token |
SDK 액세스 토큰 만료 |
/api/ 응답은 항상 JSON이며, 본문의 status가 성공 여부를 나타냅니다(done 또는 fail).
| 상태코드 | 의미 | 조치 |
|---|---|---|
200 + "status":"done" |
성공 | — |
200 + "status":"fail" |
인증은 통과했지만 요청 처리에 실패 | message의 사유 확인(권한 없음, 필수 파라미터 누락 등) |
| 401 | 토큰이 없거나 유효하지 않음 | hint 참고. 경로 문제가 아니므로 경로를 바꿔 재시도하지 마세요 |
| 404 | 존재하지 않는 엔드포인트 | 위 엔드포인트 요약의 경로를 사용하세요 |
| 403 / 405 / 429 | 애플리케이션이 아니라 앞단 보안 계층의 응답 | 아래 설명 참고 |
// 401 - 토큰 없음
{
"status": "fail",
"message": "invalid access token",
"hint": "No API token was supplied. Send it as the header \"Authorization: Bearer <token>\" ...",
"docs": "https://videostew.com/ko/dev/docs"
}
// 404 - 존재하지 않는 엔드포인트
{
"status": "fail",
"message": "Unknown API endpoint. This API has no version prefix (there is no /api/v1/...) and resource names are always plural. See the endpoint list in the docs.",
"docs": "https://videostew.com/ko/dev/docs"
}
x-nonce는 요청마다 유니크해야 합니다. 이미 사용한 값을 60초 안에 다시 보내면 요청이 실행되지 않고 200 + "status":"fail"에 duplicate request (nonce already used) 메시지가 돌아옵니다.
본문이 JSON이 아니라 HTML이라면 애플리케이션이 아니라 앞단 보안 계층이 돌려준 응답입니다. 토큰과 무관하므로 인증을 다시 확인해도 해결되지 않습니다.
Retry-After 헤더(초)만큼 기다린 뒤 재시도하십시오.편집 화면 없이 동영상을 자동으로 생성·추출하는 API입니다. 토큰 노출 방지를 위해 반드시 백엔드에서만 호출하십시오.
프로젝트 생성과 추출을 동시에 자동화합니다. 완료된 최종 결과는 webhookUrl 또는 앱 설정의 Export Webhook으로 POST 전달됩니다. 이 방법으로 생성된 프로젝트는 추출 이후 일주일 후 자동으로 삭제됩니다.
| 헤더 | 필수 | 설명 |
|---|---|---|
| Authorization | ✓ | 계정 설정 > API 토큰에서 복사한 토큰. Bearer Qjky6mFiMkRn... 형식 |
| x-nonce | ✓ | 각 요청에 대한 유니크한 값 (중복 요청 구분용) |
| 파라미터 | 필수 | 타입 | 설명 |
|---|---|---|---|
| injector | ✓ | object | 데이터 주입/변형 명세. 아래 Injector 섹션 참고 |
| dictionary | object | TTS/STT 텍스트 치환 규칙. 아래 dictionary 섹션 참고 | |
| baseProjectId | string | 특정 프로젝트를 템플릿으로 사용할 경우의 projectId. 같은 스페이스이거나 public 상태여야 함 | |
| spaceId | int | 대상 워크스페이스 고유번호. 0이면 기본 워크스페이스 | |
| type | string | 추출 파일 형식. mp4 | jpg |
|
| webhookUrl | string | 완료 시 결과를 전달할 URL. 콜백을 받을 서버가 없어 폴링만 사용할 경우 생략 가능(응답의 pollUrl로 대체) |
|
| webhookType | string | webhook 전달 방식. form | json. 폴링만 쓸 경우 불필요 |
|
| webhookHeader | string | webhook 커스텀 헤더. 헤더명: 값 형식. 예: x-api-key: abc123. 폴링만 쓸 경우 불필요 |
curl -X POST "https://videostew.com/api/automations" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-nonce: $RANDOM" \
-H "Content-Type: application/json" \
-d '{
"baseProjectId": "88282f69vmqbqr9",
"injector": {
"wizard": {
"source": "url",
"sourceContent": "https://doo39oi115k60.cloudfront.net/wizard-url-example/"
}
}
}'
{
"status": "done",
"result": {
"projectId": "abc123...",
"pollUrl": "https://videostew.com/api/exports/abc123...",
"pollIntervalSeconds": 180
}
}
처리 결과 확인: 렌더링은 요청 시점에 즉시 끝나지 않으며, 최종 완료 여부는 아래 두 가지 방법 중 하나로 확인해야 합니다. 두 방법은 배타적이지 않으므로 필요하면 함께 사용해도 됩니다.
- Webhook (콜백을 받을 서버가 있는 경우) —
webhookUrl을 지정하면 완료/실패 시 해당 URL로 결과가 POST 전달됩니다.
- 성공과 실패가 같은 URL로 오므로 반드시
status를 확인하십시오.done이면link에 결과 URL이,fail이면message에 사유가 담겨 옵니다.- 영상 생성(위자드) 단계에서 실패한 경우와 렌더링 단계에서 실패한 경우 모두 전달됩니다. 즉 webhook을 쓰면 실패를 놓치지 않으므로 폴링을 함께 할 필요가 없습니다.
- 요청에
webhookUrl을 지정하지 않았더라도 앱 설정의 Export Webhook이 등록되어 있으면 그 URL로 전달됩니다.- 폴링 (콜백을 받을 서버가 없는 경우, 예: AI/LLM이 직접 호출하는 자동화) — 요청 호스트가 없어 webhook을 받을 수 없다면, 응답에 포함된
pollUrl(=GET /api/exports/{projectId}, 아래 Exports API 섹션 참고)을 주기적으로 호출해status를 확인하세요.
- 권장 폴링 주기: 3분(180초) 이상. 응답의
pollIntervalSeconds값을 그대로 사용하면 됩니다. 렌더링은 통상 수 분이 걸리므로 이보다 자주 조회해도 결과가 더 빨리 나오지 않으며, 서버 부하만 늘어납니다.status값:generate/request= 아직 처리 중,done= 완료(link필드에 결과 URL),fail= 실패.- Automation으로 만든 프로젝트는 접수 직후부터 항상 1건이 응답됩니다. 따라서
rows[0].status만 확인하면 되고, 빈 배열을 따로 처리할 필요가 없습니다.- 사람이 직접 확인할 때는
https://videostew.com/v/{projectId}접속으로도 진행 상황("추출중입니다" → 완성된 영상)을 볼 수 있지만, 이는 사람용 화면이며 자동화(코드) 연동에는pollUrl또는webhookUrl을 사용하십시오.
{
"status": "done",
"projectId": "abc123...",
"size": "1080x1920",
"type": "mp4",
"link": "https://cdn.videostew.com/projects/...",
"message": "",
"editUrl": "https://videostew.com/e/abc123...?api_key=...&token=..."
}
{
"status": "fail",
"projectId": "abc123...",
"size": "",
"type": "mp4",
"link": "",
"message": "내용을 입력해주세요",
"editUrl": "https://videostew.com/e/abc123...?api_key=...&token=..."
}
message에 실패 사유가 들어옵니다. 영상 생성(위자드) 단계에서 실패한 경우와 렌더링 단계에서 실패한 경우 모두 전달됩니다.size·link는 빈 문자열입니다.webhookUrl을 지정했다면 폴링은 필요하지 않습니다. 성공·실패 모두 webhook으로 전달됩니다.GET /api/exports/{projectId} 호출 시){
"page": 1,
"rows": [
{
"id": "e-19",
"apiKey": "...",
"spaceId": 0,
"projectId": "abc123...",
"status": "done",
"size": "1080x1920",
"scenario": "",
"type": "mp4",
"createdAt": "2026-07-09 12:00:00",
"updatedAt": "2026-07-09 12:04:30",
"message": "",
"link": "https://cdn.videostew.com/projects/..."
}
]
}
status가done이 될 때까지pollIntervalSeconds(기본 3분) 간격으로 재조회하고,done이 되면link로 결과를 받아오면 됩니다.
status가 fail이면 message 필드에 실패 사유가 함께 내려옵니다.
{
"page": 1,
"rows": [
{
"id": "e-20",
"projectId": "abc123...",
"status": "fail",
"message": "내용을 입력해주세요",
"type": "mp4",
"createdAt": "2026-07-28 17:37:28",
"link": ""
}
]
}
link는 빈 문자열입니다.fail은 재시도해도 같은 결과인 경우가 많습니다. 특히 원문이 비어 있거나 URL에서 본문을 추출하지 못한 경우처럼 입력에 기인한 실패는, message를 확인해 요청 자체를 고쳐야 합니다.generate, 이후 done 또는 fail로 바뀝니다. rows[0].status만 보고 판단하면 됩니다. (편집 화면에서 만든 일반 프로젝트를 추출 이력 없이 조회하면 빈 배열이 올 수 있습니다.)message는 항상 포함되며, fail이 아닐 때는 빈 문자열입니다.프로젝트 내용 구성시 오류(frontend), 크레딧 부족 안내). 내부 오류의 상세는 전달되지 않습니다.데이터를 주입하는 방법으로 wizard, data를 제공하며, 각각은 배타적이므로 한 번에 한가지 방식만 사용할 수 있습니다.
텍스트 또는 URL을 넘기면 AI가 분석해 동영상을 자동으로 만들어줍니다. 세부적인 제어는 어렵지만, 가장 간단하게 동영상을 생성할 수 있는 방법입니다.
| 파라미터 | 설명 |
|---|---|
| source | 소스 타입. text | url |
| sourceContent | source에 대응하는 값. text면 HTML/plain text, url이면 URL 문자열 |
| title | 프로젝트 제목 |
| language | 프로젝트 언어. 예: ko, en, ja |
| opts.replace | AI가 교체할 슬라이드 범위. body=본문만 교체, 인트로/아웃트로는 템플릿 그대로 유지(반복 사용하는 고정 애니메이션·엔딩에 적합). headbody=AI가 소제목을 자동 생성하며 소제목+본문 교체, 인트로/아웃트로 유지. all=인트로/아웃트로 포함 전체 교체 |
| opts.visual | 비주얼 소스 방식. stock-mixed/stock-video/stock-image=비디오스튜 스톡 라이브러리에서 맥락에 맞는 리소스를 자동 매칭(각각 혼합/비디오만/이미지만). ai-image=AI로 이미지 생성. none=비주얼 없음(원본 URL에 이미지가 풍부할 때 적합) |
| opts.autoLineBreak | y로 설정하면 텍스트를 줄 단위로 분리해, 한 줄씩 등장하는 텍스트 애니메이션이 의도대로 동작합니다. 대부분의 템플릿이 이 애니메이션을 기본으로 사용하므로 y 권장 |
| opts.visualStyle | 비주얼 스타일. illust=디지털아트, photo=실사, flat-vector=벡터, editorial-cartoon=만평, editorial-illust=신문 일러스트, silhouette=실루엣. 또는 "수묵화 스타일로 그려줘" 처럼 직접 프롬프트를 입력해 원하는 스타일을 지정 |
| opts.narrationVoices | 대화형 콘텐츠(팟캐스트 등)에서 화자별 AI 목소리 지정. { "neutral": "libraryId", "화자명": "libraryId", ... } 형태의 객체. neutral은 화자 없는 슬라이드에 적용되는 기본 목소리. 화자명은 콘텐츠의 화자명: 패턴과 정확히 일치해야 함. 단일 TTS는 이 필드 대신 베이스 프로젝트의 나레이션 설정을 따름 |
| adjust.duration | 목표 분량. keep=원본 유지, 30s | 1m | 3m | 10m | 20m |
| adjust.structure | 구조. keep=원본, body=본문만, headbody=소제목+내용 형식 |
| adjust.style | 스크립트 스타일. keep=원본 유지, colloquial=팟캐스트 같은 대화형, news=뉴스 방송처럼 공식적·객관적, info=정보 전달에 최적화된 명확·간결한 스타일, insight=인사이트와 분석을 강조하는 심층적 스타일, essay=서술적·사색적 에세이 톤, casual=친근하고 편안한 일상 대화 스타일. 또는 "시청자에게 말 걸듯 친근하게, 유머도 살짝 섞어줘" 처럼 직접 프롬프트를 입력해 원하는 스타일을 지정 |
adjust의 모든 키가keep이면 원본 글을 AI로 다듬는 과정 없이 원문 그대로 사용합니다.
{
"wizard": {
"source": "url",
"sourceContent": "https://doo39oi115k60.cloudfront.net/wizard-url-example/",
"language": "ko",
"opts": {
"replace": "all",
"visual": "ai-image",
"visualStyle": "illust",
"autoLineBreak": "y"
},
"adjust": {
"duration": "1m",
"structure": "headbody",
"style": "news"
}
}
}
대화형(팟캐스트) 예시 - 화자별 AI 목소리 지정
콘텐츠에 진행자:, 게스트: 같은 화자 패턴이 포함된 경우, narrationVoices로 각 화자에 다른 목소리를 지정할 수 있습니다. 화자명은 콘텐츠의 패턴과 정확히 일치해야 합니다.
{
"wizard": {
"source": "text",
"sourceContent": "<p>진행자: 안녕하세요, 오늘의 주제는...</p><p>게스트: 네, 반갑습니다...</p>",
"language": "ko",
"opts": {
"replace": "all",
"narrationVoices": {
"neutral": "voice-library-id-default",
"진행자": "voice-library-id-host",
"게스트": "voice-library-id-guest"
}
}
}
}
neutral: 화자 패턴이 없는 슬라이드(인트로·아웃트로 등)에 적용되는 기본 목소리입니다.
프로젝트/슬라이드/요소 구조를 직접 지정합니다. 영상을 어떻게 만들 것인지 모든 것을 직접 구성할 때 사용합니다. 기존 요소와 일치하면 대체, 없으면 신규 생성합니다. 변경이 필요한 키만 지정하면 되며, 지정하지 않은 값은 기존 프로젝트의 값을 유지합니다.
라벨 ≠ API 키: 요소에 붙인 라벨이
img여도 이미지 주입 키는 항상image입니다. 라벨(label)은 '어느 슬라이드/요소'인지 고르는 용도이고, 주입 키(textimagevideo)와 좌표·스타일 키는 고정입니다.
data 키
최상위에 지정한 bgm은 slides 배열 전체에 기본값으로 적용됩니다. 각 슬라이드별 지정이 있으면 그 값을 더 우선시합니다.
| 키 | 설명 |
|---|---|
| title | 프로젝트 제목 |
| language | 프로젝트 언어. 예: ko, en |
| bgm | 프로젝트 BGM 파일 URL |
| slides | 슬라이드 배열 |
data.slides[] 키
| 키 | 설명 |
|---|---|
| label | 슬라이드 식별 라벨(기본 베이스 프로젝트내의 슬라이드 라벨기준으로 매칭). 같은 label을 여러 번 지정하면 해당 템플릿 슬라이드를 복제해 슬라이드 수를 늘릴 수 있습니다. 예: main 라벨을 3번 쓰면 main 슬라이드가 3개 생성. 라벨값은 편집화면에서 하단의 슬라이드 패널에서 확인할 수 있습니다. |
| narration | 슬라이드 나레이션. 텍스트를 입력하면 템플릿에 설정된 AI 목소리로 읽어줍니다. 직접 녹음한 오디오를 쓰려면 공개 URL을 입력하세요 |
| sound | 슬라이드 효과음 URL |
| bgm | 이 슬라이드에만 적용할 BGM URL. 최상위 bgm보다 우선 |
| text | 텍스트 요소 내용. 단순 문자열이면 슬라이드의 첫 번째 텍스트 요소에 적용. 슬라이드에 텍스트 요소가 여러 개 있을 때 각 요소를 정확히 타겟팅하려면 { "label": "요소라벨", "content": "내용" } 객체 형태로 배열 지정(라벨은 템플릿에서 설정한 요소 라벨 기준). 빈 문자열이면 요소 삭제. 이 간단 모드에서는 슬라이드 최상위 text/image/video 키만 씁니다. 요소 단위(위치·애니메이션·색 등) 제어가 필요하면 아래 상세 설정 의 elements 배열을 사용하세요. |
| image | 이미지 요소 libraryId. 배열로 여러 개 지정 가능 |
| video | 비디오 요소 libraryId. 배열로 여러 개 지정 가능 |
참고: URL을 지정하는 모든 필드(
image,video,sound,bgm,narration등)는 공개(public) 접근 가능한 주소여야 하며, 크롬 브라우저 기준의 표준 파일 형식이어야 합니다.
예시 1 - 텍스트·이미지 치환 + TTS 나레이션
{
"data": {
"language": "ko",
"bgm": "https://example.com/bgm.mp3", // 모든 슬라이드에 공통 적용
"slides": [
{
"label": "title", // 베이스 프로젝트의 슬라이드 라벨로 매칭
"text": "오늘의 뉴스 제목", // 템플릿의 텍스트 요소를 이 내용으로 교체
"image": "https://doo39oi115k60.cloudfront.net/wizard-url-example/01.webp",
"narration": "오늘의 뉴스를 전해드립니다." // 문자열이면 TTS로 생성(베이스 프로젝트의 나레이션 속성 따름)
},
{
"label": "body",
"text": "본문 내용입니다.",
"image": "https://doo39oi115k60.cloudfront.net/wizard-url-example/02.webp"
},
{
"label": "outro",
"text": "" // 빈 문자열이면 해당 텍스트 요소 삭제
}
]
}
}
예시 2 - 라벨로 요소 타겟팅
템플릿에 텍스트 요소가 여러 개 있을 때, { label, content } 형태로 어떤 요소에 무엇을 넣을지 정확히 지정할 수 있습니다. 단, 이 방식은 Videostew 편집 화면에서 각 텍스트 요소에 미리 라벨을 붙여두어야 동작합니다. 라벨이 없는 요소는 타겟팅할 수 없습니다. 예를 들어 상단 요소에 title, 하단 AI 보이스용 요소에 voicetext라고 라벨을 붙여둔 템플릿이라면 아래처럼 각각 제어할 수 있습니다.
{
"data": {
"slides": [
{
"label": "main",
"text": [
{ "label": "title", "content": "이거 진짜 될까?" }, // 화면 상단 타이틀 요소
{ "label": "voicetext", "content": "이거 진짜 될까" } // AI 보이스가 읽을 자막 요소
]
}
]
}
}
예시 3 - 녹음 파일 나레이션 + 슬라이드별 비디오
{
"data": {
"slides": [
{
"label": "intro",
"narration": "https://doo39oi115k60.cloudfront.net/wizard-url-example/narration1.mp3", // 직접 녹음/합성한 오디오 파일
"video": "https://doo39oi115k60.cloudfront.net/wizard-url-example/video1.mp4"
},
{
"label": "body",
"narration": "https://doo39oi115k60.cloudfront.net/wizard-url-example/narration2.mp3",
"video": "https://doo39oi115k60.cloudfront.net/wizard-url-example/video2.mp4",
"bgm": "https://example.com/this-slide-only.mp3" // 이 슬라이드만 다른 BGM
}
]
}
}
injector.data의 확장 모드입니다. 위 간단 모드(text/image/video 문자열)로 표현하기 힘든 세부 제어(요소 위치, 같은 종류 요소 여러 개, 등장 애니메이션·제자리효과 등)가 필요할 때 elements 배열을 사용합니다. 아래는 사용 가능한 키의 전체 목록이며, 실제 요청에는 필요한 것만 포함하세요.
baseProjectId필수: injection은 호출자가 소유한 프로젝트/템플릿을 베이스로 합니다.baseProjectId를 생략하면 요소가 반영되지 않으니 자신의 프로젝트 ID를 지정하세요. 베이스에 없는 새 슬라이드 레이아웃을elements만으로 생성할 수는 없습니다.
color 포맷:
[[R, G, B, A], degree](R/G/B는 0-255, A는 0-1 불투명도, degree는 그라데이션 각도, 단색이면0).
미디어 교체는
libraryId에 공개 URL: 이미지/비디오 요소의libraryId에 public URL을 넣으면 인제스트되어 교체됩니다.custom.source나 요소의image키로는 교체되지 않습니다.
요소 등장 애니메이션 trans (요소 최상위 필드, 슬라이드 전환 trans와 별개. 형식 그룹__방향):
| 그룹 | 방향 |
|---|---|
slide flip_push flip_pull cube |
left right up down |
stretch |
left right up down horizontal vertical |
flip |
horizontal_left horizontal_right vertical_up vertical_down |
reveal |
left right up down horizontal_open vertical_open |
fade |
normal left right up down |
zoom zoomspin |
in out |
circle |
open |
clock |
asc desc |
blink |
(방향 없음) |
요소 제자리효과 idle: pendulum pulse twitch updown leftright shake blink clockwise counterclockwise. 세기는 idleConf: { "speed": 수치, "distance": 수치 }.
슬라이드 전환과 요소 애니메이션은 값 체계가 다릅니다. 슬라이드 전환(
slides[].trans.type)은 하이픈 방향(fadecube-ltrreveal-rtl등), 요소 등장 애니메이션(elements[].trans.type)은 위 표의 언더바 방향(reveal__right등)을 씁니다.
필드 표
슬라이드 레벨:
| 키 | 타입 | 설명 |
|---|---|---|
label |
string | 베이스 슬라이드 라벨로 매칭(간단 모드와 동일) |
trans |
object | 슬라이드 전환효과 { type, durationTime } |
duration |
object | 슬라이드 재생시간 기준 { base, time }(아래) |
narration |
object | 나레이션(아래) |
glb |
object | 슬라이드 전역(BGM/효과음/배경/블러) |
elements |
array | 요소 배열. 라벨 일치 시 대체, 없으면 생성, content:""면 삭제 |
요소 elements[] 공통:
| 키 | 타입 | 설명 |
|---|---|---|
type |
string | text | image | video | shape |
label |
string | 요소 라벨(대체 대상 선택) |
content |
string | 텍스트 내용(type:text). ""면 요소 삭제 |
libraryId |
string | 미디어 소스. 공개 URL이면 인제스트되어 교체 |
rect |
object | 위치·크기(아래) |
custom |
object | 스타일·등장효과(아래) |
trans |
object | 요소 등장 애니메이션 { type, txtRange }(위 표) |
idle / idleConf |
string / object | 제자리효과(위 목록) |
rect:
| 키 | 설명 |
|---|---|
top left width height |
위치·크기(px) |
opacity |
요소 불투명도 0-1 |
innerType |
미디어 표시 cover(꽉 채움·기본) | contain(맞춤) | fill(원본비율) |
innerPosition |
포커스 위치, CSS position 형식(예: 50% 30%) |
custom (텍스트):
| 키 | 설명 |
|---|---|
fontSize |
글자 크기(px) |
color / raColor |
글자색 / 가독성배경 색 (color 포맷) |
align |
가로정렬 left | center | right |
valign |
세로정렬 top | middle | bottom |
lineHeight |
행간 |
raType |
가독성 배경 "" | block | inline(기본) | outline | shadow |
enabledTts |
true면 이 텍스트를 AI 보이스가 읽음 |
splitLine |
텍스트 등장효과 "" | 1l | 2l | p |
splitLineType |
replace(교체) | add(누적) |
custom (비디오):
| 키 | 설명 |
|---|---|
trimStart trimEnd |
트림(초) |
speed volume |
속도 / 볼륨 |
loop |
반복(0=안함, 9999=무한) |
narration:
| 키 | 설명 |
|---|---|
type |
voice(오디오 파일) | tts(텍스트 합성) |
libraryId |
type:voice일 때 오디오 URL |
content |
type:tts일 때 읽을 텍스트 |
volume speed trimStart trimEnd |
볼륨 / 속도 / 트림 |
duration:
| 키 | 설명 |
|---|---|
base |
슬라이드 재생시간 기준. narration=나레이션·오디오 길이에 맞춰 자동으로 늘어남(음성이 잘리지 않음) | video=삽입한 비디오 클립 길이 | manual=time에 지정한 고정 초 |
time |
base:manual일 때 고정 재생시간(초). 0.1~60 |
오디오가 중간에 잘린다면 해당 슬라이드의
duration.base가manual(고정 초) 또는video로 설정된 것입니다. 슬라이드는 오디오 길이에 맞춰 자동으로 늘어나지 않고 고정 길이만큼만 재생되므로, 나레이션·오디오가 그보다 길면 초과분이 잘립니다. 오디오 길이에 맞춰 슬라이드가 늘어나게 하려면"duration": { "base": "narration" }을 함께 지정하세요.
glb:
| 키 | 설명 |
|---|---|
bgmLibraryId bgmVolume bgmSpeed |
슬라이드 BGM URL / 볼륨 / 속도 |
soundLibraryId soundVolume |
효과음 URL / 볼륨 |
soundEvery |
true면 연속 슬라이드에서 반복 재생 |
soundFit |
true면 슬라이드 길이에 맞춰 페이드 |
backgroundColor |
배경색 (color 포맷) |
bgBlr bgBlrB |
배경 블러 on/off / 강도 0-1 |
{
"data": {
"title": "프로젝트 제목",
"language": "ko",
"slides": [
{
"trans": {
"type": "fade", // auto | fade | flip-ltr | flip-ttb | cube-ltr | cube-rtl | reveal-ltr | reveal-rtl
"durationTime": 0.3
},
"duration": {
"base": "narration", // narration(음성 길이 따라 늘어남·잘림 방지) | video(클립 길이) | manual(고정 초)
"time": 2 // base가 manual일 때 고정 재생시간(초)
},
"narration": {
"type": "voice", // voice(오디오 파일) | tts(텍스트 합성)
"libraryId": "https://doo39oi115k60.cloudfront.net/wizard-url-example/narration1.mp3", // type이 voice일 때
"content": "", // type이 tts일 때 읽을 텍스트
"volume": 1,
"speed": 1.1,
"trimStart": 0,
"trimEnd": 0
},
"glb": {
"bgmLibraryId": "https://example.com/bgm.mp3",
"bgmVolume": 0.9,
"bgmSpeed": 1,
"soundLibraryId": "https://example.com/sfx.mp3",
"soundVolume": 1,
"soundEvery": true,
"soundFit": false,
"backgroundColor": [[0, 0, 0, 1], 0],
"bgBlr": false,
"bgBlrB": 0.4
},
"elements": [
{
"type": "text",
"content": "텍스트 내용",
"rect": {
"top": 800, "left": 40, "width": 1000, "height": 200,
"opacity": 1
},
"custom": {
"fontSize": 48,
"color": [[255, 255, 255, 1], 0],
"align": "center", // left | center | right
"valign": "middle", // top | middle | bottom
"lineHeight": 1.2,
"raType": "shadow", // "" | block | inline | outline | shadow (기본 inline)
"raColor": [[0, 0, 0, 1], 0],
"enabledTts": true,
"splitLine": "1l", // "" 전체 | 1l 한 줄씩 | 2l 두 줄씩 | p 문단별
"splitLineType": "replace" // replace 교체 | add 누적
},
"trans": { "type": "reveal__right", "txtRange": "line" } // 요소 등장 애니메이션(위 표)
},
{
"type": "image",
"libraryId": "https://doo39oi115k60.cloudfront.net/wizard-url-example/01.webp",
"rect": {
"top": 0, "left": 0, "width": 1080, "height": 1920,
"opacity": 1,
"innerType": "cover", // cover | contain | fill
"innerPosition": "50% 30%" // CSS position 형식, 이미지 포커스 위치
},
"idle": "updown", // 제자리효과(위 목록)
"idleConf": { "speed": 1.0, "distance": 1.0 }
},
{
"type": "video",
"libraryId": "https://doo39oi115k60.cloudfront.net/wizard-url-example/video1.mp4",
"rect": {
"top": 0, "left": 0, "width": 1080, "height": 1080,
"innerType": "cover",
"innerPosition": "50% 50%"
},
"custom": {
"trimStart": 0,
"trimEnd": 10,
"speed": 1,
"volume": 1,
"loop": 9999 // 0 = 반복 안함, 9999 = 무한 반복
}
},
{
"type": "shape",
"libraryId": "shape-library-id",
"rect": {
"top": 0, "left": 0, "width": 200, "height": 200
},
"custom": {
"color": [[255, 100, 0, 1], 0]
}
}
]
}
]
}
}
TTS/STT 시 적용할 텍스트 치환 규칙입니다. injector와 함께 바디 최상위에 전달합니다. 스페이스 딕셔너리와 병합되며 여기서 지정한 값이 우선합니다. 영역당 최대 100개 항목.
| 키 | 설명 |
|---|---|
tts |
AI 보이스가 텍스트를 읽을 때 적용. 잘못 발음되는 단어를 올바른 발음으로 강제 지정 |
stt |
음성을 텍스트로 변환(자막 생성)할 때 적용. STT가 잘못 인식하는 단어를 올바른 표기로 강제 교정 |
trans |
번역 후처리 시 적용 |
{
"injector": {
"wizard": { "source": "url", "sourceContent": "https://doo39oi115k60.cloudfront.net/wizard-url-example/" }
},
"dictionary": {
"tts": [
{ "i": "3D", "o": "쓰리디" }, // "삼디"로 읽히는 걸 방지
{ "i": "AI", "o": "에이아이" } // "아이"로 읽히는 걸 방지
],
"stt": [
{ "i": "video studio", "o": "videostew" }, // STT가 잘못 인식한 표기를 교정
{ "i": "비디오 스튜", "o": "비디오스튜" } // 띄어쓰기 오인식 교정
]
}
}
curl -X GET "https://videostew.com/api/spaces" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-nonce: $RANDOM"
| 파라미터 | 타입 | 설명 |
|---|---|---|
| spaceId | int | 대상 워크스페이스 고유번호. 0이면 기본 워크스페이스 |
| projectIds | string | 콤마로 구분된 프로젝트 ID 목록 |
| folderId | int | 폴더 고유번호. 기본값(루트) 1000000 |
| status | string | public | enabled | secret. trash는 삭제된 상태 |
| limit | int | 페이지당 항목수 |
| page | int | 페이지 번호 |
| title | string | 제목 검색 |
| tags | string | 태그 검색 |
curl -X GET "https://videostew.com/api/projects?spaceId=0&limit=20&page=1" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-nonce: $RANDOM"
curl -X GET "https://videostew.com/api/projects/PROJECT_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-nonce: $RANDOM"
쿼리 파라미터: includeSlides=true - 응답 result에 slides 배열이 포함됩니다(각 슬라이드: slideId glb duration narration trans elements note 등, 주입 구조와 대칭). 주입한 값의 실제 반영을 확인할 때 유용합니다. 대용량이므로 필요할 때만 사용하세요.
특정 프로젝트의 추출만 조회하려면
projectId를 쿼리 파라미터가 아니라 **경로(path segment)**로 전달해야 합니다:GET /api/exports/{projectId}(아래 예시 참고).
| 파라미터 | 타입 | 설명 |
|---|---|---|
| spaceId | int | 대상 워크스페이스 고유번호. 0이면 기본 워크스페이스 |
| projectIds | string | 콤마로 구분된 프로젝트 ID 목록 |
| type | string | mp4 | jpg |
| limit | int | 페이지당 항목수 |
| page | int | 페이지 번호 |
curl -X GET "https://videostew.com/api/exports/PROJECT_ID?limit=20" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-nonce: $RANDOM"
curl -X GET "https://videostew.com/api/exports/PROJECT_ID/EXPORT_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-nonce: $RANDOM"
| 파라미터 | 타입 | 설명 |
|---|---|---|
| type | string | mp4 | jpg |
| webhookUrl | string | 완료 시 결과를 받을 URL |
| webhookType | string | form | json |
| webhookHeader | string | webhook 커스텀 헤더. 헤더명: 값 형식. 예: x-api-key: abc123 |
curl -X POST "https://videostew.com/api/exports/PROJECT_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-nonce: $RANDOM" \
-H "Content-Type: application/json" \
-d '{"type":"mp4","webhookUrl":"https://your-server.com/webhook"}'
응답:
{
"status": "done",
"result": {
"id": "e-19",
"projectId": "...",
"status": "generate",
"type": "mp4"
}
}
Webhook 응답 (추출 완료 시):
{
"status": "done",
"projectId": "...",
"size": "1280x720",
"type": "mp4",
"link": "https://cdn.videostew.com/projects/...",
"message": "",
"editUrl": "https://videostew.com/e/...?api_key=...&token=..."
}
실패 시에는 status가 fail, link가 빈 문자열이 되고 message에 실패 사유가 들어옵니다.
자사의 웹사이트에서 자사의 고객들에게 비디오스튜의 편집화면만 제공할 때 사용합니다.
SDK를 사용할 때는 각 사용자마다 개별 액세스 토큰을 발급해야 합니다. 토큰은 마지막 사용 이후 1개월 간 유효하며, 재사용 시 자동 연장됩니다. 401 Token is invalid 응답이 오면 새 토큰을 발급해 fallback 처리하세요.
!!
secret키 노출 방지를 위해 반드시 백엔드에서만 요청하십시오.
| 파라미터 | 필수 | 타입 | 설명 |
|---|---|---|---|
| apiKey | ✓ | string | 발급받은 앱의 ApiKey |
| secret | ✓ | string | 발급받은 앱의 시크릿 키 |
| uniqueId | ✓ | any | 자사 플랫폼에서 사용하는 유저의 고유번호 |
| language | string | uniqueId 신규 생성 시 언어 강제 지정. 예: ko, en |
curl -X POST "https://videostew.com/auth/access-token" \
-H "Content-Type: application/json" \
-d '{
"apiKey": "YOUR_API_KEY",
"secret": "YOUR_API_SECRET",
"uniqueId": "user_123"
}'
응답:
{
"status": "done",
"apiKey": "abc1de2fg3...",
"token": "Qjky6mFiMkRnOmExYj..."
}
로그아웃 등 모든 활동 종료 시 해당 유저의 토큰을 강제 만료합니다. 토큰은 1회성이 아니므로 강제 삭제 시 사용 중인 다른 탭에서 문제가 발생할 수 있습니다. 사용자가 탈퇴 등으로 확실히 그만두는 시점에서 호출하시면 됩니다.
curl -X POST "https://videostew.com/auth/revoke-access-token" \
-H "Content-Type: application/json" \
-d '{
"apiKey": "YOUR_API_KEY",
"secret": "YOUR_API_SECRET",
"uniqueId": "user_123"
}'
버튼 클릭 등의 이벤트에서 편집기를 모달로 엽니다.
<script src="https://cdn.videostew.com/web/sdk/sdk-latest.js"></script>
<script>
videostewSDK.open({
token: "Qjky6mFiMkRnOmEx...", // /auth/access-token 응답의 token 값
projectId: "...", // 수정할 프로젝트 ID (없으면 신규 생성)
// baseProjectId: "...", // 특정 프로젝트를 템플릿으로 사용(신규 생성시)
// language: "ko", // 프로젝트 및 UI 언어 설정
// injector: { ... }, // 데이터 주입/변형 명세
// dictionary: { ... }, // TTS/STT 텍스트 치환 규칙
callbackFunc: function(result) {
// 사용자가 [Done]을 눌렀을 때 호출
console.log(result.projectId);
// result: { projectId, thumbnailUrl, spaceId, width, height, title, status, slideCount, savedAt }
},
eventFunc: function(projectId, eventName) {
// 편집 화면 내 이벤트 발생 시 호출
// eventName: openPreview, saveProject, openDownload,
// requestExportPng, requestExportMp4, exportedPng, exportedMp4,
// downloadPng, downloadMp4, openProjectInfo, updateProjectInfo
},
errorFunc: function(errorMessage) {
// 토큰 만료 시 호출 → 새 토큰 발급 후 재시도 처리
// errorMessage: "Token is invalid"
}
});
</script>
| 옵션 | 필수 | 설명 |
|---|---|---|
| token | ✓ | /auth/access-token 응답의 token 값. 예: Qjky6mFiMkRn... |
| apiKey | 레거시 방식(raw token) 사용 시에만 필요. 신규 방식은 생략 | |
| projectId | 수정할 프로젝트 ID. 없으면 신규 생성 | |
| baseProjectId | 템플릿 프로젝트 ID. 같은 스페이스이거나 public 상태여야 함 | |
| language | 프로젝트 및 UI 언어. 미지정 시 기본 en |
|
| injector | 데이터 주입/변형 명세. Injector 섹션 참고 | |
| dictionary | TTS/STT 텍스트 치환 규칙. dictionary 섹션 참고 | |
| callbackFunc | function(result) - [Done] 버튼 클릭 시 호출, 앱 설정에 Done Webhook이 지정된 경우 이 설정은 무시됩니다. |
|
| eventFunc | function(projectId, eventName) - 편집 화면 내 이벤트 발생 시 호출 |
|
| errorFunc | function(errorMessage) - 토큰 만료 시 호출 |
편집 화면 상태를 저장합니다. 유저가 [Save] 버튼을 누른 것과 동일하므로 특별한 경우가 아니면 호출할 필요가 없습니다.
videostewSDK.save();
편집 내용을 저장하고 callbackFunc 또는 Done Webhook으로 결과를 전달합니다.
videostewSDK.done();
편집기를 닫습니다. 저장되지 않은 내용이 있으면 확인 팝업이 표시됩니다.
videostewSDK.cancel();
// videostewSDK.cancel(true); // 강제 닫기