Home > Dev > Docs
Home / Pricing / Docs / Apps

Videostew Developer Documentation

Request Format Note When making POST requests, we recommend sending the JSON body directly with the Content-Type: application/json header. If using application/x-www-form-urlencoded, serialize the parameters as JSON and send them in the payload field. Example: payload={"apiKey":"...","secret":"..."}

Automation API

An API for automatically generating and exporting videos without the editing interface. To prevent token exposure, call this API from the backend only.

POST https://videostew.com/api/automations

Automates both project creation and export in a single call. The final result is delivered via POST to the webhookUrl or the Export Webhook configured in your app settings. Projects created this way are automatically deleted one week after export.

Authentication Headers

Header Required Description
Authorization Token copied from Account Settings > API Token. Format: Bearer Qjky6mFiMkRn...
x-nonce A unique value per request (used to distinguish duplicate requests)

Body Parameters

Parameter Required Type Description
injector object Data injection/transformation spec. See Injector section below
dictionary object TTS/STT text substitution rules. See dictionary section below
baseProjectId string projectId of the project to use as a template. Must be in the same space or be public
spaceId int Target workspace ID. 0 means the default workspace
type string Export file format. mp4 | jpg
webhookUrl string URL to receive the result upon completion
webhookType string Webhook delivery method. form | json
webhookHeader string Custom webhook header. Format: Header-Name: value. Example: x-api-key: abc123

Request Example

curl -X POST "https://videostew.com/api/automations" \
  -H "Authorization: Bearer {{bearerToken}}" \
  -H "x-nonce: $RANDOM" \
  -H "Content-Type: application/json" \
  -d '{
    "baseProjectId": "88282f69vmqbqr9",
    "injector": {
      "wizard": {
        "source": "url",
        "sourceContent": "https://doo39oi115k60.cloudfront.net/wizard-url-example/",
      }
    }
  }'

Response Example

{
  "status": "done",
  "result": {
    "projectId": "abc123..."
  }
}

Checking the result: If you visit https://videostew.com/v/{projectId} and see the message "Exporting…", rendering is in progress. You can check the completed video at the same URL in about 5 minutes. To receive the completion event in code, specify a webhookUrl.

Webhook Response (on export completion)

{
  "status": "done",
  "projectId": "abc123...",
  "size": "1080x1920",
  "type": "mp4",
  "link": "https://cdn.videostew.com/projects/..."
}

Injector Detailed Options

Two data injection methods are available: wizard and data. They are mutually exclusive — only one can be used at a time.

injector.wizard

Pass text or a URL and AI will analyze it to automatically create a video. Detailed control is limited, but this is the simplest way to generate a video.

Parameter Description
source Source type. text | url
sourceContent Value corresponding to source. Plain text or HTML if text; a URL string if url
title Project title
language Project language. Example: ko, en, ja
opts.replace Slide range for AI to replace. body = replace body slides only, keeping intro/outro from the template as-is (suitable for fixed animations and endings used repeatedly). headbody = AI auto-generates subheadings and replaces subheading + body, keeping intro/outro. all = replace everything including intro/outro
opts.visual Visual source method. stock-mixed/stock-video/stock-image = automatically matches contextually relevant resources from the Videostew stock library (mixed/video only/image only respectively). ai-image = generate images with AI. none = no visuals (suitable when the source URL is already image-rich)
opts.autoLineBreak When set to y, text is split line by line so that line-by-line text animations work as intended. Most templates use this animation by default, so y is recommended
opts.visualStyle Visual style. illust = digital art, photo = photorealistic, flat-vector = vector, editorial-cartoon = editorial cartoon, editorial-illust = newspaper illustration, silhouette = silhouette. Or enter a direct prompt such as "Draw in ink wash painting style" to specify the style you want
adjust.duration Target length. keep = preserve original, 30s | 1m | 3m | 10m | 20m
adjust.structure Structure. keep = original, body = body only, headbody = subheading + content format
adjust.style Script style. keep = preserve original, colloquial = conversational like a podcast, news = formal and objective like a news broadcast, info = clear and concise style optimized for information delivery, insight = in-depth style emphasizing insights and analysis, essay = narrative and reflective essay tone, casual = friendly and relaxed everyday conversational style. Or enter a direct prompt such as "Speak to the viewer in a friendly tone with a touch of humor" to specify the style you want

If all keys in adjust are set to keep, the original text is used as-is without any AI refinement.

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

injector.data

Directly specifies the project/slide/element structure. Use this when you want to fully control how the video is built. If a match with an existing element is found it is replaced; otherwise a new one is created. Specify only the keys that need to change — unspecified values retain their values from the existing project.

data keys

The bgm specified at the top level is applied as the default across the entire slides array. If a value is specified for an individual slide, that value takes precedence.

Key Description
title Project title
language Project language. Example: ko, en
bgm Project BGM file URL
slides Array of slides

data.slides[] keys

Key Description
label Slide identification label (matched against the slide labels in the base project). Specifying the same label multiple times duplicates that template slide to increase the slide count. Example: using the main label 3 times creates 3 main slides. Label values can be found in the slide panel at the bottom of the editing screen.
narration Slide narration. Enter text and it will be read in the AI voice configured in the template. To use a self-recorded audio file, enter a public URL
sound Slide sound effect URL
bgm BGM URL applied to this slide only. Takes precedence over the top-level bgm
text Text element content. A plain string is applied to the first text element in the slide. When a slide has multiple text elements and you need to target each precisely, specify an array of { "label": "element-label", "content": "content" } objects (label is based on the element label set in the template). An empty string deletes the element. The elements array approach is not supported.
image Image element libraryId. Multiple values can be specified as an array
video Video element libraryId. Multiple values can be specified as an array

Note: All fields that accept a URL (image, video, sound, bgm, narration, etc.) must be publicly accessible addresses in a standard file format supported by Chrome.

Example 1 — Text/image replacement + TTS narration

{
  "data": {
    "language": "ko",
    "bgm": "https://example.com/bgm.mp3",  // applied to all slides
    "slides": [
      {
        "label": "title",                   // matched by slide label in the base project
        "text": "오늘의 뉴스 제목",           // replaces the template's text element with this content
        "image": "https://doo39oi115k60.cloudfront.net/wizard-url-example/01.webp",
        "narration": "오늘의 뉴스를 전해드립니다."  // a string value generates TTS (follows the narration settings of the base project)
      },
      {
        "label": "body",
        "text": "본문 내용입니다.",
        "image": "https://doo39oi115k60.cloudfront.net/wizard-url-example/02.webp"
      },
      {
        "label": "outro",
        "text": ""                          // an empty string deletes that text element
      }
    ]
  }
}

Example 2 — Targeting elements by label

When a template has multiple text elements, you can use the { label, content } form to precisely specify which element receives what content. However, this approach requires that each text element in the Videostew editing screen has been pre-labeled. Elements without a label cannot be targeted. For example, if a template has the top element labeled title and the bottom AI voice element labeled voicetext, you can control each independently as shown below.

{
  "data": {
    "slides": [
      {
        "label": "main",
        "text": [
          { "label": "title", "content": "이거 진짜 될까?" },       // top title element on screen
          { "label": "voicetext", "content": "이거 진짜 될까" }     // subtitle element read by AI voice
        ]
      }
    ]
  }
}

Example 3 — Recorded narration + per-slide video

{
  "data": {
    "slides": [
      {
        "label": "intro",
        "narration": "https://doo39oi115k60.cloudfront.net/wizard-url-example/narration1.mp3", // self-recorded/synthesized audio file
        "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"  // different BGM for this slide only
      }
    ]
  }
}
Advanced Settings

Use this when you need fine-grained control that cannot be expressed with simple key mapping (e.g., position or multiple items). The following is a complete list of available keys — include only what you need in your actual request.

color format: [[R, G, B, A], degree] — R/G/B are 0–255, A is 0–1 (opacity), degree is the gradient angle (0 for solid color).

{
  "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
        },
        "narration": {
          "type": "voice",      // voice
          "libraryId": "https://doo39oi115k60.cloudfront.net/wizard-url-example/narration1.mp3",
          "content": "", // TTS 텍스트 (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": "v_bottom",       // top | middle | bottom | v_top | v_bottom
              "lineHeight": 1.2,
              "raType": "block",          // notset | block | inline | outline | textShadow
              "raColor": [[0, 0, 0, 1], 0],
              "enabledTts": true,
              "splitLine": "1l",          // "" | 1l | 2l | p
              "splitLineType": "replace"  // replace | add
            }
          },
          {
            "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 format, image focus position
            }
          },
          {
            "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 = no loop, 9999 = infinite loop
            }
          },
          {
            "type": "shape",
            "libraryId": "shape-library-id",
            "rect": {
              "top": 0, "left": 0, "width": 200, "height": 200
            },
            "custom": {
              "color": [[255, 100, 0, 1], 0]
            }
          }
        ]
      }
    ]
  }
}

dictionary

Text substitution rules applied during TTS/STT. Passed at the top level of the body alongside injector. Merged with the space dictionary, with values specified here taking precedence. Maximum 100 entries per section.

Key Description
tts Applied when AI voice reads text. Force-sets the correct pronunciation for words that are mispronounced
stt Applied when converting speech to text (subtitle generation). Force-corrects words that STT misrecognizes
trans Applied during translation post-processing
{
  "injector": {
    "wizard": { "source": "url", "sourceContent": "https://doo39oi115k60.cloudfront.net/wizard-url-example/" }
  },
  "dictionary": {
    "tts": [
      { "i": "3D", "o": "쓰리디" },       // prevents it from being read as "삼디"
      { "i": "AI", "o": "에이아이" }       // prevents it from being read as "아이"
    ],
    "stt": [
      { "i": "video studio", "o": "videostew" },   // corrects STT misrecognition
      { "i": "비디오 스튜", "o": "비디오스튜" }     // corrects spacing misrecognition
    ]
  }
}

Other APIs

GET https://videostew.com/api/spaces — List Workspaces

curl -X GET "https://videostew.com/api/spaces" \
  -H "Authorization: Bearer {{bearerToken}}" \
  -H "x-nonce: $RANDOM"

GET https://videostew.com/api/projects — List Projects

Parameter Type Description
spaceId int Target workspace ID. 0 means the default workspace
projectIds string Comma-separated list of project IDs
folderId int Folder ID. Default (root) is 1000000
status string public | enabled | secret. trash means deleted
limit int Items per page
page int Page number
title string Search by title
tags string Search by tags
curl -X GET "https://videostew.com/api/projects?spaceId=0&limit=20&page=1" \
  -H "Authorization: Bearer {{bearerToken}}" \
  -H "x-nonce: $RANDOM"

GET https://videostew.com/api/projects/:projectId — Get Single Project

curl -X GET "https://videostew.com/api/projects/PROJECT_ID" \
  -H "Authorization: Bearer {{bearerToken}}" \
  -H "x-nonce: $RANDOM"

Query parameter: includeSlides=true (includes slide details; response may be large)

GET https://videostew.com/api/exports — List Exports

Parameter Type Description
spaceId int Target workspace ID. 0 means the default workspace
projectIds string Comma-separated list of project IDs
projectId string Retrieve exports for a specific project only
type string mp4 | jpg
limit int Items per page
page int Page number
curl -X GET "https://videostew.com/api/exports?projectId=PROJECT_ID&limit=20" \
  -H "Authorization: Bearer {{bearerToken}}" \
  -H "x-nonce: $RANDOM"

GET https://videostew.com/api/exports/:projectId/:exportId — Get Single Export

curl -X GET "https://videostew.com/api/exports/PROJECT_ID/EXPORT_ID" \
  -H "Authorization: Bearer {{bearerToken}}" \
  -H "x-nonce: $RANDOM"

POST https://videostew.com/api/exports/:projectId — Request Export

Parameter Type Description
type string mp4 | jpg
webhookUrl string URL to receive the result upon completion
webhookType string form | json
webhookHeader string Custom webhook header. Format: Header-Name: value. Example: x-api-key: abc123
curl -X POST "https://videostew.com/api/exports/PROJECT_ID" \
  -H "Authorization: Bearer {{bearerToken}}" \
  -H "x-nonce: $RANDOM" \
  -H "Content-Type: application/json" \
  -d '{"type":"mp4","webhookUrl":"https://your-server.com/webhook"}'

Response:

{
  "status": "done",
  "result": {
    "id": "e-19",
    "projectId": "...",
    "status": "generate",
    "type": "mp4"
  }
}

Webhook response (on export completion):

{
  "status": "done",
  "projectId": "...",
  "size": "1280x720",
  "type": "mp4",
  "link": "https://cdn.videostew.com/projects/..."
}

SDK

SDK curl 예시에 인증정보를 자동으로 채우려면 앱 관리 페이지에서 앱을 먼저 발급하세요.

Use this when you want to provide only the Videostew editing interface to your customers on your own website.

Authentication (Issuing / Expiring Access Tokens)

When using the SDK, you must issue a separate access token for each user. A token is valid for 1 month after its last use and is automatically renewed on reuse. If you receive a 401 Token is invalid response, issue a new token and handle the fallback.

!! To prevent secret key exposure, make this request from the backend only.

POST https://videostew.com/auth/access-token — Issue Access Token

Parameter Required Type Description
apiKey string The ApiKey of the issued app
secret string The secret key of the issued app
uniqueId any The unique identifier of the user in your platform
language string Force-set language when a new uniqueId is created. Example: ko, en
curl -X POST "https://videostew.com/auth/access-token" \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "{{apiKey}}",
    "secret": "{{secret}}",
    "uniqueId": "user_123"
  }'

Response:

{
  "status": "done",
  "apiKey": "abc1de2fg3...",
  "token": "Qjky6mFiMkRnOmExYj..."
}

POST https://videostew.com/auth/revoke-access-token — Expire Access Token (Optional)

Force-expires a user's token when all activity ends (e.g., logout). Since tokens are not single-use, force-deleting one may cause issues in other open tabs. Call this when you are certain the user has stopped using the service (e.g., account deletion).

curl -X POST "https://videostew.com/auth/revoke-access-token" \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "{{apiKey}}",
    "secret": "{{secret}}",
    "uniqueId": "user_123"
  }'

Launching the Editor

videostewSDK.open(options)

Opens the editor as a modal, typically from a button click or similar event.

<script src="https://cdn.videostew.com/web/sdk/sdk-latest.js"></script>
<script>
	videostewSDK.open({
		token: "Qjky6mFiMkRnOmEx...", // token value from the /auth/access-token response
		projectId: "...",     // ID of the project to edit (creates a new one if omitted)
		// baseProjectId: "...",  // use a specific project as a template (on new creation)
		// language: "ko",        // project and UI language setting
		// injector: { ... },     // data injection/transformation spec
		// dictionary: { ... },   // TTS/STT text substitution rules
		callbackFunc: function(result) {
			// called when the user clicks [Done]
			console.log(result.projectId);
			// result: { projectId, thumbnailUrl, spaceId, width, height, title, status, slideCount, savedAt }
		},
		eventFunc: function(projectId, eventName) {
			// called when an event occurs in the editing screen
			// eventName: openPreview, saveProject, openDownload,
			//            requestExportPng, requestExportMp4, exportedPng, exportedMp4,
			//            downloadPng, downloadMp4, openProjectInfo, updateProjectInfo
		},
		errorFunc: function(errorMessage) {
			// called on token expiry → issue a new token and retry
			// errorMessage: "Token is invalid"
		}
	});
</script>
Option Required Description
token The token value from the /auth/access-token response. Example: Qjky6mFiMkRn...
apiKey Required only when using the legacy method (raw token). Omit for the new method
projectId ID of the project to edit. Creates a new project if omitted
baseProjectId Template project ID. Must be in the same space or be public
language Project and UI language. Defaults to en if not specified
injector Data injection/transformation spec. See Injector section
dictionary TTS/STT text substitution rules. See dictionary section
callbackFunc function(result) — called when the [Done] button is clicked. Ignored if a Done Webhook is configured in the app settings.
eventFunc function(projectId, eventName) — called when an event occurs in the editing screen
errorFunc function(errorMessage) — called on token expiry

videostewSDK.save()

Saves the current state of the editing screen. This is equivalent to the user pressing the [Save] button, so there is generally no need to call it explicitly.

videostewSDK.save();

videostewSDK.done()

Saves the edited content and delivers the result to callbackFunc or the Done Webhook.

videostewSDK.done();

videostewSDK.cancel(force?)

Closes the editor. A confirmation popup is shown if there are unsaved changes.

videostewSDK.cancel();
// videostewSDK.cancel(true); // force close
[Stop]