เอกสารอ้างอิง API
สร้างวิดีโอ AI ผ่านโปรแกรม ส่งพรอมป์ ตรวจสอบสถานะ แล้วรับ URL วิดีโอโดยตรง — ทั้งหมดผ่าน REST
Base URL: https://yoh.appตัวอย่างโค้ดจะใช้ตัวยึดตำแหน่ง sk_live_YOUR_API_KEY จนกว่าคุณจะลงชื่อเข้าใช้
เริ่มต้นใช้งาน
สร้างวิดีโอแรกของคุณด้วยการเรียก API เพียง 3 ครั้ง: สร้าง → ตรวจสอบสถานะ → ดาวน์โหลด
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"
}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 ทั้งหมดต้องแนบ API key ของคุณในส่วนหัว Authorization โดยใช้รูปแบบ Bearer
Authorization: Bearer sk_live_YOUR_API_KEYAPI key จะขึ้นต้นด้วย sk_live_ คุณสามารถสร้างและจัดการ key ได้จากAPI Keys page /api-keys
เก็บ API key ของคุณเป็นความลับ อย่าเปิดเผยไว้ในโค้ดฝั่งไคลเอนต์หรือ repository สาธารณะ หาก key ถูกเปิดเผย ให้เพิกถอนทันทีจากหน้า API Keys
ขอบเขตสิทธิ์ & เครดิต
API key แต่ละอันจะได้รับสิทธิ์หนึ่งขอบเขตขึ้นไปที่ควบคุมว่า endpoint ใดที่เข้าถึงได้ การสร้างข้อความเรื่องราวมีค่าใช้จ่าย 55 เครดิตต่องาน ส่วนขอบเขตที่สร้างวิดีโอคลิปหรือการเรนเดอร์สุดท้ายด้วย (รวมถึงขอบเขตเริ่มต้นคือ full) จะคิดค่าใช้จ่ายล่วงหน้าเพิ่มเติม เครดิตจะถูกคืนหากงานล้มเหลวหลังจากลองใหม่ครบทุกครั้งแล้ว
| ขอบเขตสิทธิ์ | ให้สิทธิ์เข้าถึง |
|---|---|
| stories:generate | POST /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:read | GET /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:read | GET .../characters, .../scenes, .../items, .../clips, GET /api/v1/series/:id/assets |
| assets:write | POST /api/v1/series/:id/assets, PATCH/DELETE /api/v1/series/:id/assets/:assetId, POST /api/v1/uploads |
| series:read | GET /api/v1/series, GET /api/v1/series/:id |
| series:write | POST /api/v1/series, PATCH /api/v1/series/:id |
| social:publish | POST /api/v1/social/publish, GET /api/v1/social/publish/:postId |
สร้างวิดีโอ
เพิ่มงานสร้างวิดีโอเข้าคิว
/api/v1/stories/generatescope: stories:generateส่งพรอมป์และคืนค่า jobId ทันที วิดีโอจะถูกสร้างแบบอะซิงโครนัส ตรวจสอบสถานะได้ที่ GET /api/v1/stories/jobs/:jobId
เนื้อหาคำขอ
| ฟิลด์ | ประเภท | สถานะ | คำอธิบาย |
|---|---|---|---|
prompt | string | จำเป็น | คำอธิบายเรื่องราว สูงสุด 5000 ตัวอักษร |
aspectRatio | string | ไม่บังคับ | หนึ่งใน 16:9, 9:16, 1:1, 21:9 ค่าเริ่มต้น: 16:9 |
renderVideo | boolean | ไม่บังคับ | false — คืนค่าคลิป AI แรก (เร็ว ~10 นาที) true — เรนเดอร์คลิปทั้งหมดรวมเป็น MP4 เดียวผ่าน Remotion (~12–15 นาที) |
scope | string | ไม่บังคับ | 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. |
title | string | ไม่บังคับ | ชื่อที่แสดงของเรื่องราว |
contentType | string | ไม่บังคับ | หมวดหมู่รูปแบบเรื่องราว หนึ่งใน cinematic_story, news_analysis, persona_channel, ad_spot ค่าเริ่มต้น: cinematic_story |
locale | string | ไม่บังคับ | รหัสภาษา เช่น en, zh-TW ค่าเริ่มต้น: en |
seriesId | string | ไม่บังคับ | แนบเข้ากับซีรีส์ที่มีอยู่แล้วเพื่อให้ภาพตัวละครมีความสอดคล้องกัน |
assets | array | ไม่บังคับ | The cast and props for this story — reference assets already in your series library, or attach one-off assets inline. See the table below. |
continuityMode | string | ไม่บังคับ | 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. |
isFinale | boolean | ไม่บังคับ | Implied by continuityMode: "finale"; you rarely need both. |
allowMultiStory | boolean | ไม่บังคับ | 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. |
seriesContinuityContext | object | ไม่บังคับ | 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.
| ฟิลด์ | ประเภท | คำอธิบาย |
|---|---|---|
seriesAssetId | string | Reference an existing library asset. Everything else is optional when this is set. |
type | string | character · scene · item · reference. Required for an inline asset. |
name | string | Required for an inline asset. Also how the writer refers to it — name it in your prompt for it to appear. |
description | string | Overrides the library description for this story only. |
imageUrl | string | Image for an inline asset. Re-hosted on our storage. |
itemType | string | Items only: prop · product · logo. A logo is composited flat, never given 3D depth. |
isHeroProduct | boolean | ad_spot: the advertised product — appears in every clip. Fills heroItemId for you. |
isPrimaryBackground | boolean | news_analysis / persona_channel: the set reused across every shot. Fills primaryBackgroundAssetId for you. |
useAsReferenceFrame | boolean | Use this image as the first frame / reference for image-to-video on clips featuring the asset. |
gender | string | Characters only: surfaced to the writer. |
age | string | Characters only: surfaced to the writer. |
personalityTraits | string[] | Characters only: surfaced to the writer. |
timeOfDay | string | Scenes only, e.g. dusk. |
weather | string | Scenes only, e.g. rain. |
persist | boolean | Accrue 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.
| ฟิลด์ | ประเภท | Values | Default |
|---|---|---|---|
durationSec | number | 15–300 (total seconds) | 60 |
aspectRatio | string | 16:9 · 9:16 · 1:1 · 21:9 | 16:9 |
resolution | string | 480p · 720p · 1080p | 720p |
pace | string | slow · normal · fast | normal |
structure | string | auto · 3_act · 4_act · montage | auto |
qualityTier | string | draft · balanced · premium (premium selects the advanced model lane) | balanced |
stylePreset | string | preset slug, or auto | auto |
platformTarget | string | ADVISORY — shown to the writer as context; nothing enforces it. youtube · tiktok · instagram · x | youtube |
safetyMode | string | ADVISORY — shown to the writer as context; not a content filter. standard · strict | standard |
seed | string | any string — shared across clips for continuity (API-only) | — |
videoModel | string | a model id, or auto | auto |
imageModel | string | a model id, or auto | auto |
modelStrategy | string | auto · fixed · cost_optimized · quality_first · hero_premium | auto |
generationMode | string | DEPRECATED — ignored. Every clip uses unified reference mode. | auto |
locale | string | en · 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.
| contentType | Fields |
|---|---|
| cinematic_story | (no per-type fields — format craft is built in) |
| news_analysis | primaryBackgroundAssetId, primaryBackgroundName (pin a studio scene across anchor shots) |
| persona_channel | primaryBackgroundAssetId, primaryBackgroundName (pin the persona's set) |
| ad_spot | heroItemId, heroProductName, heroTagline, heroKeyBenefit (the advertised product) |
Other optional fields
| ฟิลด์ | ประเภท | คำอธิบาย |
|---|---|---|
storyQualityConfig | object | { enableStyleAdaptedCharacters?: boolean, directClipImageGeneration?: boolean } — quality-rollout toggles. Omit to use pipeline defaults. |
style | object | Style-consistency overrides: cinematicStyle, era, colorPalette, lightingStyle, filmGrain, lensCharacter, materialEmphasis, styleCategory, negativeHints, stylePreset. |
seriesContinuityContext | object | Series 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.
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.
/api/v1/stories/:storyId/assetsGenerate 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.
/api/v1/stories/:storyId/storyboardGenerate storyboard grid images for all clips. Skips clips that already have images.
/api/v1/stories/:storyId/videosGenerate video for each clip. Skips clips that already have a video URL.
/api/v1/stories/:storyId/renderStitch all clip videos + music + captions into a final MP4.
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.
/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.
/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.
estimatedCredits) | Scope: stories:generate# 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 replacedEditing 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.
/api/v1/stories/:storyId/clips/:clipIdEdit 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.
/api/v1/stories/:storyId/clipsInsert 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.
/api/v1/stories/:storyId/clipsReorder. Body: { "clips": [{ "id": "...", "order": 0 }, …] } — send every clip with a contiguous 0-based order.
/api/v1/stories/:storyId/clips/:clipIdCut a clip; the remaining orders close up. A story must keep at least one clip.
# 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 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ตรวจสอบสถานะงาน
ดูสถานะงาน
/api/v1/stories/jobs/:jobIdscope: stories:readตรวจสอบ endpoint นี้หลังจากเรียก POST /api/v1/stories/generate โดยทั่วไปงานจะใช้เวลา 8–15 นาที ขึ้นอยู่กับความยาวของเรื่องราวและว่าเปิดใช้ renderVideo หรือไม่ แนะนำให้ตรวจสอบทุก 15–30 วินาที
วงจรสถานะของงาน
queuedgenerating_storygenerating_assetsgenerating_videosrendering_videocompletedหรือfailedWhich 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.
ฟิลด์ในการตอบกลับ
| ฟิลด์ | ประเภท | คำอธิบาย |
|---|---|---|
jobId | string | รหัสเฉพาะของงาน |
status | string | สถานะปัจจุบัน ดูวงจรสถานะด้านบน |
progress | number | จำนวนเต็ม 0–100 แสดงเปอร์เซ็นต์ความคืบหน้า |
storyId | string|null | จะถูกตั้งค่าเมื่อการสร้างเรื่องราวเสร็จสมบูรณ์ ใช้เพื่อดึงข้อมูลแอสเซ็ต |
renderVideo | boolean | ระบุว่ามีการร้องขอ MP4 สุดท้ายแบบรวมคลิปหรือไม่ |
videoUrl | string|null | URL วิดีโอแบบสมบูรณ์เมื่อสถานะเป็น completed อาจเป็น MP4 แบบรวมคลิป (renderVideo: true) หรือวิดีโอ AI ของคลิปแรก (renderVideo: false) |
error | string|null | ข้อความแสดงข้อผิดพลาดที่อ่านเข้าใจง่าย จะถูกตั้งค่าเมื่อเกิดความล้มเหลว — และขณะกำลังลองงานใหม่ด้วย ซึ่งสถานะจะกลับไปเป็น queued เป็น null เสมอเมื่อสำเร็จ |
createdAt / startedAt / completedAt | string (ISO) | ประทับเวลา |
เรื่องราว
รายการเรื่องราว
/api/v1/storiesscope: stories:readคืนค่ารายการเรื่องราวของคุณแบบแบ่งหน้า
พารามิเตอร์การค้นหา
| พารามิเตอร์ | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|
page | 1 | หมายเลขหน้า (≥ 1) |
limit | 20 | ผลลัพธ์ต่อหน้า (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 }
}ดูเรื่องราวหนึ่งรายการ
/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 ที่ได้จากงานนั้น endpoint แอสเซ็ตทั้งหมดต้องใช้ขอบเขตสิทธิ์ 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
ตัวละคร
รายการตัวละคร
/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
}]
}คลิป & วิดีโอ
รายการคลิป
/api/v1/stories/:storyId/clipsscope: assets:readแต่ละคลิปคือส่วนของฉากที่มีวิดีโอสร้างโดย AI ภาพนิ่ง และเสียงเป็นของตัวเอง ฟิลด์ URL ทั้งหมดเป็น 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 ของงานจะชี้ไปยังวิดีโอของคลิปแรก ใช้ endpoint นี้เพื่อรับ URL วิดีโอของคลิปแต่ละรายการ
คลังแอสเซ็ตของซีรีส์
อัปโหลดครั้งเดียว ใช้ซ้ำได้ตลอด
/api/v1/series/:seriesId/assetsscope: assets:writeเป็นคลังเดียวกับที่แผงแอสเซ็ตในสตูดิโอเขียนลงไป อัปโหลดตัวละคร ฉากหลัง พร็อพ หรือภาพอ้างอิงสไตล์ แล้วเก็บ id ที่ได้กลับมา จากนั้นตอนสร้างงานให้ส่ง id นั้นเป็น seriesAssetId ในอาร์เรย์ assets โดยไม่ต้องสร้างลิงก์รูปใหม่ทุกครั้ง รูปที่คุณส่งมาจะถูกนำไปโฮสต์บนพื้นที่จัดเก็บของเรา ดังนั้น id จะยังใช้งานได้แม้ URL ต้นทางของคุณจะหมดอายุ
| ฟิลด์ | ประเภท | คำอธิบาย |
|---|---|---|
type | string | character, scene, item หรือ reference |
name | string | ชื่อที่แสดง และเป็นคีย์ที่ใช้จับคู่ในการสร้างงานครั้งถัดไป |
imageUrl | string | URL สาธารณะของรูปภาพ หรือจะส่ง imageBase64 แทนก็ได้ |
itemType | string | เฉพาะพร็อพ: prop, product หรือ logo โดย logo จะถูกวางแบบแบน ไม่ใส่มิติ 3 มิติ |
isHeroProduct | boolean | เฉพาะพร็อพ: สินค้าหลักของโฆษณานี้ จะปรากฏในทุกคลิป |
isPrimaryBackground | boolean | เฉพาะฉาก: สตูดิโอ/ฉากที่ใช้ซ้ำในทุกช็อตของผู้ประกาศ |
- ส่งแอสเซ็ตเดียว หรือใช้อาร์เรย์ "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:
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", ... } }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.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.
แอสเซ็ตของซีรีส์
ลงทะเบียนภาพตัวละครสำหรับซีรีส์
/api/v1/series-assets/ensure-character-imagescope: assets:writePOST /api/v1/series-assets/ensure-character-image ยังใช้งานได้และไม่มีการเปลี่ยนแปลง แต่รองรับเฉพาะตัวละคร แนะนำให้ใช้เอนด์พอยต์นี้แทน
อัปโหลดและบันทึกภาพอ้างอิงตัวละครสำหรับซีรีส์ เพื่อให้ AI ใช้ใบหน้า/ลุคที่สอดคล้องกันในทุกเรื่องราวของซีรีส์นั้น การดำเนินการนี้เป็นแบบ idempotent — เรียกซ้ำหลายครั้งด้วยชื่อตัวละครเดียวกันได้อย่างปลอดภัย
| ฟิลด์ | ประเภท | คำอธิบาย |
|---|---|---|
seriesId | string | รหัสของซีรีส์ที่จะแนบตัวละครเข้าไป |
characterName | string | ชื่อของตัวละคร (ใช้เป็นคีย์เฉพาะภายในซีรีส์) |
imageUrl | string | URL ที่เข้าถึงได้แบบสาธารณะของภาพอ้างอิงตัวละคร |
จัดการ API Key
endpoint การจัดการ API key ใช้ session authentication (คุกกี้ของผู้ใช้ที่ล็อกอินอยู่) ไม่ใช่ API key ใช้ API Keys page สำหรับขั้นตอนการทำงานผ่านเบราว์เซอร์ หรือเรียก endpoint เหล่านี้ผ่านโปรแกรมจากแบ็กเอนด์ของคุณด้วยเซสชันที่ถูกต้อง /api-keys
รายการ API key
/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 key
/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 key
/api/v1/api-keys/:idscope: session{ "id": "key_abc123", "revoked": true }ข้อผิดพลาด
ข้อผิดพลาดทั้งหมดจะคืนค่าในรูปแบบ JSON ที่สอดคล้องกัน โดยมีอ็อบเจ็กต์ error ที่มี code ที่เครื่องอ่านได้
{ "error": { "code": "insufficient_credits", "message": "...", "status": 402 } }| HTTP | code | ความหมาย |
|---|---|---|
| 401 | unauthorized | ไม่มี API key หรือ API key ไม่ถูกต้อง |
| 403 | forbidden | API key ไม่มีขอบเขตสิทธิ์ที่จำเป็น |
| 404 | not_found | ไม่พบทรัพยากรที่ร้องขอ |
| 400 | bad_request | เนื้อหาคำขอหรือพารามิเตอร์ไม่ถูกต้อง |
| 402 | insufficient_credits | เครดิตไม่เพียงพอสำหรับดำเนินการนี้ |
| 429 | rate_limited | คำขอมากเกินไป ตรวจสอบส่วนหัว Retry-After |
| 500 | internal_error | เกิดข้อผิดพลาดของเซิร์ฟเวอร์ที่ไม่คาดคิด |
ขีดจำกัดอัตราการเรียก: endpoint การสร้างอนุญาต 5 คำขอต่อนาทีต่อ API key การอ่านข้อมูลและการแก้ไขคลิป (ซึ่งไม่ใช้เครดิต) อนุญาต 60 คำขอ เมื่อคุณถึงขีดจำกัด การตอบกลับจะมีส่วนหัว Retry-After ระบุจำนวนวินาทีที่ต้องรอ งานที่ล้มเหลว (หลังจากลองใหม่ครบทุกครั้ง) จะคืนเครดิตที่ถูกหักไปโดยอัตโนมัติ
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
/api/v1/social/publishscope: social:publishReturns
202with{ postId, status: "publishing" }and uploads in the background — poll the status endpoint below. Passwait: trueto block until the upload finishes instead (can take minutes).socialAccountIdstringstoryIdstringvideoUrlstringplatformstringcaptionstringyoutubeobjectdurationSecnumberaspectRatiostringfileSizeBytesnumberwaitbooleanPoll publish status
/api/v1/social/publish/:postIdscope: social:publishLifecycle:
publishing→published|failed.