API 參考文件

API 參考文件

以程式方式生成 AI 影片。提交提示詞、輪詢完成狀態,並取得直接影片連結 — 全部透過 REST 完成。

基礎 URL: https://yoh.app

登入前,程式碼範例將使用佔位符 sk_live_YOUR_API_KEY。

快速開始

透過三個 API 呼叫建立您的第一部影片:生成 → 輪詢 → 下載。

1. 生成影片
curl -X POST https://yoh.app/api/v1/stories/generate \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "prompt": "A dragon teaches a young knight how to fly", "aspectRatio": "16:9" }'
回應 — 工作已排入佇列
{
  "jobId": "cmnk8wu2q0001q3vpmqv93nmy",
  "status": "queued",
  "pollUrl": "/api/v1/stories/jobs/cmnk8wu2q0001q3vpmqv93nmy"
}
2. 輪詢直到完成
curl https://yoh.app/api/v1/stories/jobs/cmnk8wu2q0001q3vpmqv93nmy \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
回應 — 工作已完成
{
  "status": "completed", "progress": 100,
  "storyId": "cmnk8xxnx000004lbd7siief7",
  "videoUrl": "https://cdn.example.com/video/clip.mp4",
  "error": null
}

就這樣。videoUrl 是可直接下載、串流或分享的公開存取 URL。

身份驗證

所有 API 請求都必須在 Authorization 標頭中使用 Bearer 方案包含您的 API 金鑰。

Authorization: Bearer sk_live_YOUR_API_KEY

API 金鑰以 sk_live_ 開頭。您可以從 API Keys page 建立和管理金鑰。 /api-keys

請妥善保管您的 API 金鑰。不要在客戶端程式碼或公開儲存庫中暴露它們。如果金鑰外洩,請立即從 API 金鑰頁面撤銷它。

範圍與點數

每個 API 金鑰被授予一個或多個控制其可存取端點的範圍。故事文字生成每個工作消耗 55 點數;若範圍還包含片段影片或最終轉檔(包括預設的 full 範圍),這些費用會另外預先扣除。如果工作在所有重試後失敗,點數將被退還。

範圍授予存取權限
stories:generatePOST /api/v1/stories/generate, POST /stories/:id/assets, POST /stories/:id/storyboard, POST /stories/:id/videos, POST /stories/:id/render, POST /stories/:id/clips/:clipId/image, POST /stories/:id/clips/:clipId/video — the clip edits: POST/PATCH /stories/:id/clips, PATCH/DELETE /stories/:id/clips/:clipId — and the story-asset edits: PATCH /stories/:id/characters|scenes|items/:assetId. Also implies assets:write and series:write, so keys issued before those scopes existed keep working.
stories:readGET /api/v1/stories, GET /api/v1/stories/:id, GET /api/v1/stories/jobs/:id, GET /api/v1/credits, POST /api/v1/stories/estimate, POST /api/v1/stories/analyze-prompt. Implies series:read and assets:read.
assets:readGET .../characters, .../scenes, .../items, .../clips, GET /api/v1/series/:id/assets
assets:writePOST /api/v1/series/:id/assets, PATCH/DELETE /api/v1/series/:id/assets/:assetId, POST /api/v1/uploads
series:readGET /api/v1/series, GET /api/v1/series/:id
series:writePOST /api/v1/series, PATCH /api/v1/series/:id
social:publishPOST /api/v1/social/publish, GET /api/v1/social/publish/:postId

生成影片

排入影片生成工作

POST/api/v1/stories/generatescope: stories:generate

立即提交提示詞並返回 jobId。影片以非同步方式生成。輪詢 GET /api/v1/stories/jobs/:jobId 以追蹤進度。

請求本體

欄位型別狀態說明
promptstring必填故事描述,最多 5000 個字元。
aspectRatiostring選填16:9、9:16、1:1、21:9 之一。預設:16:9。
renderVideoboolean選填false — 返回第一個 AI 片段(快速,約 10 分鐘)。true — 透過 Remotion 將所有片段渲染成一個合成 MP4(約 12–15 分鐘)。
scopestring選填story (text only, recommended), assets (+ character/scene/item images), storyboard (+ per-clip images), videos (+ clip videos), render (+ final MP4), or full (all phases). Default: full — which charges video + render credits up front. An unrecognized value falls back to full.
titlestring選填故事的顯示名稱。
contentTypestring選填故事格式分類。可選 cinematic_story、news_analysis、persona_channel、ad_spot。預設:cinematic_story。
localestring選填語言代碼,例如 en、zh-TW。預設:en。
seriesIdstring選填附加到現有影集以保持一致的角色視覺效果。
assetsarray選填The cast and props for this story — reference assets already in your series library, or attach one-off assets inline. See the table below.
continuityModestring選填With a seriesId: how much of the previous episodes this one carries. episodic_continuous (default) picks up open threads, finale resolves them, one_off_episode keeps series plot out entirely.
isFinaleboolean選填Implied by continuityMode: "finale"; you rarely need both.
allowMultiStoryboolean選填Let one request split into several episodes when the prompt clearly carries more than one. Default false — N episodes means N charges, so it is opt-in.
seriesContinuityContextobject選填Escape hatch. Supplying it replaces the continuity we assemble from your series (open threads plus the last three episode recaps), so an incomplete object produces a weaker episode than sending nothing. Prefer continuityMode.

assets[] 選填

This array ATTACHES assets to one generation — it is not where an asset is defined. It carries only what is read inline; the full asset shape (character type and role, voice, scene lighting and mood, item material and condition) lives in the series library, which accepts all of it and which the pipeline reads directly. Reference the library by id rather than restating an asset here. Each entry either references an asset in your series library by seriesAssetId (upload it once via POST /api/v1/series/:seriesId/assets and reuse it forever), or defines a one-off asset inline with type + name. The two forms mix freely in one array. Whatever you attach is described to the writer, and its image is reused instead of a new one being generated — so a character keeps their face and a product keeps its packaging.

欄位型別說明
seriesAssetIdstringReference an existing library asset. Everything else is optional when this is set.
typestringcharacter · scene · item · reference. Required for an inline asset.
namestringRequired for an inline asset. Also how the writer refers to it — name it in your prompt for it to appear.
descriptionstringOverrides the library description for this story only.
imageUrlstringImage for an inline asset. Re-hosted on our storage.
itemTypestringItems only: prop · product · logo. A logo is composited flat, never given 3D depth.
isHeroProductbooleanad_spot: the advertised product — appears in every clip. Fills heroItemId for you.
isPrimaryBackgroundbooleannews_analysis / persona_channel: the set reused across every shot. Fills primaryBackgroundAssetId for you.
useAsReferenceFramebooleanUse this image as the first frame / reference for image-to-video on clips featuring the asset.
genderstringCharacters only: surfaced to the writer.
agestringCharacters only: surfaced to the writer.
personalityTraitsstring[]Characters only: surfaced to the writer.
timeOfDaystringScenes only, e.g. dusk.
weatherstringScenes only, e.g. rain.
persistbooleanAccrue an inline asset into the series library so later stories can reference it by id. Default false.

Characters, scenes and items only enter the story when the prompt names them — attaching a whole series library does not drag its entire cast into every episode. The two pin flags are the exception: a pinned hero product or primary background is guaranteed regardless of whether the script says its name.

globalConfig 選填

Optional nested object controlling duration, output format, and model selection. Every field has a default; pass only what you want to override. The model fields (videoModel, imageModel, modelStrategy) may also be passed at the top level as a convenience. Unknown model ids degrade to auto rather than failing the request.

This is a superset of the studio's own controls, not a mirror of them. resolution, qualityTier, modelStrategy and seed are API-only — the studio expresses the same choices through its quality picker. Two fields are advisory: they are shown to the writer as context but nothing enforces them, and they are marked below.

欄位型別ValuesDefault
durationSecnumber15–300 (total seconds)60
aspectRatiostring16:9 · 9:16 · 1:1 · 21:916:9
resolutionstring480p · 720p · 1080p720p
pacestringslow · normal · fastnormal
structurestringauto · 3_act · 4_act · montageauto
qualityTierstringdraft · balanced · premium (premium selects the advanced model lane)balanced
stylePresetstringpreset slug, or autoauto
platformTargetstringADVISORY — shown to the writer as context; nothing enforces it. youtube · tiktok · instagram · xyoutube
safetyModestringADVISORY — shown to the writer as context; not a content filter. standard · strictstandard
seedstringany string — shared across clips for continuity (API-only)
videoModelstringa model id, or autoauto
imageModelstringa model id, or autoauto
modelStrategystringauto · fixed · cost_optimized · quality_first · hero_premiumauto
generationModestringDEPRECATED — ignored. Every clip uses unified reference mode.auto
localestringen · zh-TW · ja · ko · th · id (also accepted top-level)en

contentTypeConfig 選填

Optional per-contentType creative controls. Fields are validated against the selected content type; omitted fields fall back to sensible defaults. Pass the object matching your contentType. The two pin ids below are usually easier to set as isHeroProduct / isPrimaryBackground on an assets[] entry — an id you write here wins if you send both.

contentTypeFields
cinematic_story(no per-type fields — format craft is built in)
news_analysisprimaryBackgroundAssetId, primaryBackgroundName (pin a studio scene across anchor shots)
persona_channelprimaryBackgroundAssetId, primaryBackgroundName (pin the persona's set)
ad_spotheroItemId, heroProductName, heroTagline, heroKeyBenefit (the advertised product)

Other optional fields

欄位型別說明
storyQualityConfigobject{ enableStyleAdaptedCharacters?: boolean, directClipImageGeneration?: boolean } — quality-rollout toggles. Omit to use pipeline defaults.
styleobjectStyle-consistency overrides: cinematicStyle, era, colorPalette, lightingStyle, filmGrain, lensCharacter, materialEmphasis, styleCategory, negativeHints, stylePreset.
seriesContinuityContextobjectSeries continuity context for episodic generation. Used together with seriesId.

Fields that aren't part of the selected contentType's schema are silently ignored — cinematic_story takes no per-type fields at all, so pass contentTypeConfig only for ad_spot, news_analysis, or persona_channel.

Full control example
curl -X POST $BASE/api/v1/stories/generate \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A 30s spot for a titanium water bottle that keeps ice for 24h",
    "contentType": "ad_spot",
    "scope": "full",
    "renderVideo": true,
    "globalConfig": {
      "durationSec": 30,
      "resolution": "1080p",
      "modelStrategy": "hero_premium"
    },
    "contentTypeConfig": {
      "heroProductName": "Northpeak Flask",
      "heroTagline": "Ice, 24 hours later.",
      "heroKeyBenefit": "Vacuum-sealed titanium keeps ice frozen for a full day"
    }
  }'

回應 202 Accepted

{ "jobId": "cmnk8wu2q0001q3vpmqv93nmy", "status": "queued", "pollUrl": "/api/v1/stories/jobs/..." }

Step-by-Step Endpoints

After generating a story with scope: "story", use these endpoints to generate storyboard images, clip videos, and the final rendered MP4 as separate steps.

POST/api/v1/stories/:storyId/assets

Generate the cast and world: character portraits + 2×2 character sheets, plus scene and item images. Run this before /storyboard for the best face consistency. Skips assets that already have images.

Credits: charged per generated image | Scope: stories:generate
POST/api/v1/stories/:storyId/storyboard

Generate storyboard grid images for all clips. Skips clips that already have images.

Credits: 20 minimum, then ~3 per clip beyond that | Scope: stories:generate
POST/api/v1/stories/:storyId/videos

Generate video for each clip. Skips clips that already have a video URL.

Credits: ~96 per 8s clip (default model; scales with duration) | Scope: stories:generate
POST/api/v1/stories/:storyId/render

Stitch all clip videos + music + captions into a final MP4.

Credits: 10 | Scope: stories:generate

One Clip at a Time

The batch endpoints above only fill in what's missing/storyboard skips clips that already have an image and /videos skips clips that already have a video. To re-roll a shot you don't like, target it directly. These are the same operations as the editor's per-clip regenerate buttons, and they overwrite whatever that clip already has.

POST/api/v1/stories/:storyId/clips/:clipId/image

(Re)generate the storyboard image for ONE clip. Optional body: prompt (art direction for this shot) and imageModel.

Credits: one image (no pipeline base) | Scope: stories:generate
POST/api/v1/stories/:storyId/clips/:clipId/video

(Re)generate the video for ONE clip. Optional body: videoModel. There is no mode to pick — every clip is produced in the single unified reference mode, where a multi-reference model receives one categorized bundle: the storyboard sketch and clip image, character sheets, item, background and style images, the previous clip's tail as a video reference, and the character's voice sample as an audio reference.

Credits: that clip's duration on the story's lane (returned as estimatedCredits) | Scope: stories:generate
Re-roll one shot
# Regenerate clip 3's image, then its video — the rest of the story is untouched
curl -X POST $BASE/api/v1/stories/STORY_ID/clips/CLIP_ID/image \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
# Poll jobId until completed

curl -X POST $BASE/api/v1/stories/STORY_ID/clips/CLIP_ID/video \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"videoModel":"bytedance/seedance-2.0"}'
# Poll jobId until completed → the clip's videoUrl is replaced

Editing the Story

Direct the story instead of only re-rolling it: rewrite a shot, retime it, add a beat, reorder, or cut one. These are immediate (no job to poll) and cost no credits. A clip's description is the prompt the generation pipelines read — edit it, then call that clip's /image or /video to re-shoot it with the new direction.

PATCH/api/v1/stories/:storyId/clips/:clipId

Edit a clip. Send only what you want to change: title, description, duration, generationDuration, resolution, transitionStyle, sceneId, characterIds, itemIds, dialogue, audioFx.

Media columns (imageUrl, videoUrl, generation history) are deliberately not writable — those belong to the pipelines. Use the clip's /image and /video endpoints to change them.

Credits: 0 | Scope: stories:generate
POST/api/v1/stories/:storyId/clips

Insert a clip. title and description required; place it with afterClipId or beforeClipId (omit both to append). Following clips shift down automatically. The new clip has no image or video yet.

Credits: 0 | Scope: stories:generate
PATCH/api/v1/stories/:storyId/clips

Reorder. Body: { "clips": [{ "id": "...", "order": 0 }, …] } — send every clip with a contiguous 0-based order.

Credits: 0 | Scope: stories:generate
DELETE/api/v1/stories/:storyId/clips/:clipId

Cut a clip; the remaining orders close up. A story must keep at least one clip.

Credits: 0 | Scope: stories:generate
Rewrite a shot, then re-shoot it
# 1. Rewrite the direction + retime the shot (immediate, free)
curl -X PATCH $BASE/api/v1/stories/STORY_ID/clips/CLIP_ID \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "The keeper climbs the stairs as the lamp flickers behind him.",
    "duration": 7
  }'

# 2. Re-shoot it with the new direction
curl -X POST $BASE/api/v1/stories/STORY_ID/clips/CLIP_ID/image \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
curl -X POST $BASE/api/v1/stories/STORY_ID/clips/CLIP_ID/video \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
Step-by-step example
# Step 1: Generate story text (~1-2 min)
curl -X POST $BASE/api/v1/stories/generate \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"A samurai cat guards a cherry blossom tree","contentType":"cinematic_story","scope":"story"}'
# Poll jobId until completed → get storyId

# Step 2: Generate storyboard images (~3-5 min)
curl -X POST $BASE/api/v1/stories/STORY_ID/storyboard \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
# Poll until completed

# Step 3: Generate clip videos (~1-3 min per clip)
curl -X POST $BASE/api/v1/stories/STORY_ID/videos \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
# Poll until completed

# Step 4: Render final MP4 (~1-2 min)
curl -X POST $BASE/api/v1/stories/STORY_ID/render \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
# Poll until completed → videoUrl

輪詢工作狀態

取得工作狀態

GET/api/v1/stories/jobs/:jobIdscope: stories:read

呼叫 POST /api/v1/stories/generate 後輪詢此端點。工作通常需要 8–15 分鐘,取決於故事長度以及是否啟用了 renderVideo。建議每 15–30 秒 輪詢一次。

工作狀態生命週期

queuedgenerating_storygenerating_assetsgenerating_videosrendering_videocompletedfailed

Which statuses you see depends on the scope you requested — a story job goes straight from generating_story to completed. A transient failure sends the job back to queued with error set to a retry message; that is not terminal, so treat only completed and failed as final states.

回應欄位

欄位型別說明
jobIdstring唯一工作識別碼。
statusstring目前狀態。參見上方的生命週期。
progressnumber0–100 整數,表示完成百分比。
storyIdstring|null故事生成完成後設定。用於取得資產。
renderVideoboolean是否請求合成最終 MP4。
videoUrlstring|null狀態完成時的絕對影片 URL。可以是合成 MP4(renderVideo: true)或第一個片段的 AI 影片(renderVideo: false)。
errorstring|null人類可讀的錯誤訊息。失敗時會設定;工作重試時也會設定(此時狀態回到 queued)。成功時始終為 null。
createdAt / startedAt / completedAtstring (ISO)時間戳記。

故事

列出故事

GET/api/v1/storiesscope: stories:read

返回您的故事分頁列表。

查詢參數

參數預設值說明
page1頁碼(≥ 1)。
limit20每頁結果數(1–100)。 Capped at 100.
status按故事狀態過濾。

This is the only endpoint that returns a pagination envelope alongside data:

{
  "data": [ { "id": "cmnk9cibl0004q3vpijkqfwl4", "title": "...", "status": "completed" } ],
  "pagination": { "page": 1, "limit": 20, "total": 137 }
}

取得故事

GET/api/v1/stories/:storyIdscope: stories:read

返回單個故事的完整元數據,包括其角色、場景、物品和片段的數量。

{
  "id": "cmnk9cibl0004q3vpijkqfwl4",
  "title": "A robot chef cooks in a futuristic kitchen",
  "status": "completed", "aspectRatio": "9:16", "locale": "en",
  "counts": { "characters": 3, "scenes": 4, "items": 6, "clips": 6 },
  "createdAt": "2026-04-04T11:37:04.662Z"
}

資產概覽

工作完成後,您可以使用工作返回的 storyId 取得為故事生成的所有資產。所有資產端點都需要 assets:read 範圍。

GET /api/v1/stories/:id/characters

角色 — 名稱、描述、角色、imageUrl、語音信息

GET /api/v1/stories/:id/scenes

場景 — 描述、時間、天氣、光線、imageUrl

GET /api/v1/stories/:id/items

道具/物品 — 描述、重要性、imageUrl

GET /api/v1/stories/:id/clips

片段 — 旁白、時長、videoUrl、imageUrl、audioUrl

角色

列出角色

GET/api/v1/stories/:storyId/charactersscope: assets:read
{
  "data": [{
    "id": "char_abc123", "name": "Chef Axon", "role": "protagonist",
    "description": "A robot chef with a warm personality",
    "imageUrl": "https://cdn.example.com/characters/chef-axon.jpg",
    "voiceName": "Neutral-EN", "order": 0
  }]
}

片段與影片

列出片段

GET/api/v1/stories/:storyId/clipsscope: assets:read

每個片段都是一個有自己 AI 生成影片、靜態圖片和音訊的場景片段。所有 URL 欄位都是絕對路徑且可公開存取。

{
  "data": [{
    "id": "clip_001", "order": 0, "narration": "In the year 2157...",
    "duration": 8,
    "videoUrl": "https://cdn.example.com/generated/video/clip1.mp4",
    "imageUrl": "https://cdn.example.com/generated/image/clip1.jpg",
    "audioUrl": null, "status": "completed"
  }]
}

當 renderVideo 為 false 時,工作的 videoUrl 指向第一個片段的影片。使用此端點取得每個單獨片段的影片 URL。

系列素材庫

上傳一次,永久引用

POST/api/v1/series/:seriesId/assetsscope: assets:write

與工作室素材面板寫入的是同一個素材庫。上傳角色、背景、道具或風格參考後保留回傳的 id,之後生成時把該 id 當作 assets 陣列中的 seriesAssetId 引用即可,不需要每次重新產生圖片連結。你提供的圖片會重新託管在我們的儲存空間,因此即使你自己的上傳網址過期,該 id 仍然有效。

欄位型別說明
typestringcharacter、scene、item 或 reference。
namestring顯示名稱,也是後續生成用來比對的鍵值。
imageUrlstring圖片的公開網址;也可改傳 imageBase64。
itemTypestring僅限道具:prop、product 或 logo。logo 會以平面方式合成,不會加上 3D 立體效果。
isHeroProductboolean僅限道具:這支廣告主打的商品,會出現在每一個鏡頭。
isPrimaryBackgroundboolean僅限場景:每個主播鏡頭都會重複使用的攝影棚/場景。
  • 可傳入單一素材,或用 "assets" 陣列一次寫入最多 100 筆。
  • 預設以名稱完全相同來合併,因此重複上傳同一份素材目錄是冪等的。可加上 ?mergeStrategy=fuzzy 容許改名比對,或用 insert 一律新增。
  • 在道具上設定 isHeroProduct(ad_spot)或在場景上設定 isPrimaryBackground(news_analysis / persona_channel)即可釘選。生成時系統會自動解析,你不需要手動填寫 heroItemId。
範圍授予存取權限
GET/api/v1/series
POST/api/v1/series
GET/api/v1/series/:seriesId
PATCH/api/v1/series/:seriesId
GET/api/v1/series/:seriesId/assets?type=character
POST/api/v1/series/:seriesId/assets
PATCH/api/v1/series/:seriesId/assets/:assetId
DELETE/api/v1/series/:seriesId/assets/:assetId
POST/api/v1/uploads
PATCH/api/v1/stories/:storyId/characters/:assetId
PATCH/api/v1/stories/:storyId/scenes/:assetId
PATCH/api/v1/stories/:storyId/items/:assetId
POST/api/v1/stories/estimate
GET/api/v1/credits

End to end — put a brand's logo in an ad, and keep the id for every ad after it:

1 — create the show (once)
curl -X POST https://yoh.app/api/v1/series \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Acme brand films","styleGuide":{"cinematicStyle":"clean product cinematography"}}'
# → { "series": { "id": "series_abc", ... } }
2 — upload the logo (once) and keep the id
curl -X POST https://yoh.app/api/v1/series/series_abc/assets \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "type": "item",
        "name": "Acme Logo",
        "itemType": "logo",
        "description": "Wordmark, orange on white",
        "imageUrl": "https://your-admin.example.com/tmp/logo.png"
      }'
# → { "asset": { "id": "series-item-...", "imageUrl": "https://...blob..." } }
# That imageUrl is ours now — your temporary link can expire.
3 — generate an ad that features it (every time)
curl -X POST https://yoh.app/api/v1/stories/generate \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "prompt": "A 30-second spot for the Acme cordless drill",
        "contentType": "ad_spot",
        "seriesId": "series_abc",
        "assets": [
          { "seriesAssetId": "series-item-...", "isHeroProduct": true }
        ]
      }'
# → { "jobId": "...", "pollUrl": "/api/v1/stories/jobs/..." }

Step 2 happens once per asset, not once per video. Step 3 repeats forever with the same id — and because the asset is pinned, the logo is guaranteed in every clip even if the script never says its name. To check the cost before committing, POST the same body to /api/v1/stories/estimate.

影集資產

為影集註冊角色圖片

POST/api/v1/series-assets/ensure-character-imagescope: assets:write

POST /api/v1/series-assets/ensure-character-image 仍可使用且行為不變,但它只支援角色。建議改用此端點。

為影集上傳並保存角色參考圖片,使 AI 在該影集所有故事中使用一致的面孔/外觀。此操作是 idempotent 的 — 使用相同角色名稱多次呼叫是安全的。

欄位型別說明
seriesIdstring要附加角色的影集 ID。
characterNamestring角色名稱(在影集中用作唯一鍵)。
imageUrlstring角色參考圖片的可公開存取 URL。

Publish to Social

Push a finished video straight to a connected account, so an automation can drive generate → render → publish with one credential. Connect the accounts in the app first; this endpoint publishes to them.

Publish a video

POST/api/v1/social/publishscope: social:publish

Returns 202 with { postId, status: "publishing" } and uploads in the background — poll the status endpoint below. Pass wait: true to block until the upload finishes instead (can take minutes).

欄位型別狀態說明
socialAccountIdstring必填A connected account owned by your key's user.
storyIdstring必填A story owned by your key's user.
videoUrlstring必填Public MP4 URL — the videoUrl from a completed job.
platformstring必填facebook · instagram · tiktok · threads · youtube
captionstring選填Post caption.
youtubeobject選填{ title, privacyStatus, categoryId, tags } — YouTube only.
durationSecnumber選填Used for pre-flight platform validation.
aspectRatiostring選填Used for pre-flight platform validation.
fileSizeBytesnumber選填Used for pre-flight platform validation.
waitboolean選填Block until the upload completes. Default false.
curl -X POST $BASE/api/v1/social/publish \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "socialAccountId": "acct_123",
    "storyId": "STORY_ID",
    "videoUrl": "https://cdn.example.com/video/final.mp4",
    "platform": "youtube",
    "caption": "Episode 1 is live",
    "youtube": { "title": "The Lighthouse — Ep 1", "privacyStatus": "public" }
  }'

Poll publish status

GET/api/v1/social/publish/:postIdscope: social:publish

Lifecycle: publishingpublished | failed.

管理 API 金鑰

API 金鑰管理端點使用 session authentication(已登入用戶的 Cookie),而非 API 金鑰。使用 API Keys page 進行瀏覽器工作流程,或從後端以有效 Session 以程式方式呼叫這些端點。 /api-keys

列出 API 金鑰

GET/api/v1/api-keysscope: session
{
  "data": [{
    "id": "key_abc123", "name": "Production",
    "keyPrefix": "sk_live_7WH3",
    "scopes": ["stories:generate", "stories:read", "assets:read"],
    "revokedAt": null, "createdAt": "2026-04-03T10:00:00.000Z"
  }]
}

建立 API 金鑰

POST/api/v1/api-keysscope: session
// Response — full key shown once
{
  "id": "key_abc123",
  "key": "sk_live_...",
  "name": "My integration",
  "keyPrefix": "sk_live_7WH3",
  "scopes": ["stories:generate", "stories:read", "assets:read"]
}

撤銷 API 金鑰

DELETE/api/v1/api-keys/:idscope: session
{ "id": "key_abc123", "revoked": true }

錯誤代碼

所有錯誤都返回一致的 JSON 結構,其中包含帶有機器可讀 code 的 error 物件。

{ "error": { "code": "insufficient_credits", "message": "...", "status": 402 } }
HTTP代碼含義
401unauthorized缺少或無效的 API 金鑰。
403forbiddenAPI 金鑰沒有所需的範圍。
404not_found請求的資源不存在。
400bad_request無效的請求本體或參數。
402insufficient_credits點數不足,無法執行此操作。
429rate_limited請求過多。請查看 Retry-After 標頭。
500internal_error意外的伺服器錯誤。

速率限制: 每個 API 金鑰的生成端點每分鐘限 5 次請求;讀取與不消耗點數的片段編輯每分鐘限 60 次。達到限制時,回應包含 Retry-After 標頭,其中包含等待的秒數。失敗的工作(所有重試後)自動退還已扣除的點數。