API リファレンス

API リファレンス

プログラムで AI 動画を生成します。プロンプトを送信し、完了をポーリングして、直接動画 URL を受け取ります — すべて REST で完結。

ベース URL: https://yoh.app

サインインするまで、コード例はプレースホルダー sk_live_YOUR_API_KEY を使用します。

クイックスタート

3 回の 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 リクエストは、Bearer スキームを使用して Authorization ヘッダーに API キーを含める必要があります。

Authorization: Bearer sk_live_YOUR_API_KEY

API キーは sk_live_ で始まります。API Keys page からキーを作成・管理できます。 /api-keys

API キーは秘密にしてください。クライアント側のコードや公開リポジトリに公開しないでください。キーが漏洩した場合は、すぐに API キーページから取り消してください。

スコープとクレジット

各 API キーには、アクセス可能なエンドポイントを制御する 1 つ以上のスコープが付与されます。ストーリーのテキスト生成はジョブあたり 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 を使用してすべてのクリップを 1 つの合成 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 を呼び出した後にこのエンドポイントをポーリングします。ストーリーの長さや renderVideo が有効かどうかによって、ジョブには通常 8–15 分かかります。15–30 秒 ごとのポーリングを推奨します。

ジョブステータスのライフサイクル

queuedgenerating_storygenerating_assetsgenerating_videosrendering_videocompletedまたはfailed

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現在のステータス。上記のライフサイクルを参照。
progressnumber完了率を示す 0–100 の整数。
storyIdstring|nullストーリー生成完了後に設定。アセットの取得に使用。
renderVideoboolean合成最終 MP4 が要求されたかどうか。
videoUrlstring|nullステータスが completed の場合の絶対動画 URL。合成 MP4(renderVideo: true)または最初のクリップの AI 動画(renderVideo: false)。
errorstring|null人間が読めるエラーメッセージ。失敗時に設定されるほか、リトライ中(ステータスが queued に戻る)にも設定されます。成功時は常に null。
createdAt / startedAt / completedAtstring (ISO)タイムスタンプ。

ストーリー

ストーリーの一覧

GET/api/v1/storiesscope: stories:read

ストーリーのページネーションリストを返します。

クエリパラメータ

パラメータデフォルト説明
page1ページ番号(≥ 1)。
limit201 ページあたりの結果数(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 を保存しておけば、生成時に assets 配列の seriesAssetId としてその id を渡すだけで参照できます。動画ごとに画像リンクを作り直す必要はありません。渡された画像は当社ストレージに再ホストされるため、元のアップロード URL が失効しても id は使い続けられます。

フィールド説明
typestringcharacter、scene、item、reference のいずれか。
namestring表示名。以降の生成で照合に使われるキーでもあります。
imageUrlstring画像の公開 URL。代わりに imageBase64 も指定できます。
itemTypestring小道具のみ: prop、product、logo。logo は平面として合成され、立体的な奥行きは付きません。
isHeroProductboolean小道具のみ: この広告の主役となる商品。全クリップに登場します。
isPrimaryBackgroundbooleanシーンのみ: すべてのアンカーショットで使い回すスタジオ/セット。
  • 素材を 1 件送るか、"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 キー管理エンドポイントは API キーではなく session authentication(ログイン済みユーザーの Cookie)を使用します。ブラウザベースのワークフローには API Keys page を使用するか、有効なセッションでバックエンドからプログラムでこれらのエンドポイントを呼び出してください。 /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 }

エラー

すべてのエラーは、機械可読な code を含む error オブジェクトを持つ一貫した JSON 形式を返します。

{ "error": { "code": "insufficient_credits", "message": "...", "status": 402 } }
HTTPコード意味
401unauthorizedAPI キーがないか無効です。
403forbiddenAPI キーに必要なスコープがありません。
404not_found要求されたリソースが存在しません。
400bad_request無効なリクエストボディまたはパラメータ。
402insufficient_creditsこの操作を実行するためのクレジットが不足しています。
429rate_limitedリクエストが多すぎます。Retry-After ヘッダーを確認してください。
500internal_error予期しないサーバーエラー。

レート制限: API キーごとに、生成エンドポイントは 1 分あたり 5 リクエストまで。読み取りとクレジットを消費しないクリップ編集は 60 リクエストまでです。制限に達した場合、レスポンスには待機する秒数を含む Retry-After ヘッダーが含まれます。失敗したジョブ(全リトライ後)は差し引かれたクレジットを自動的に返還します。