Nodaro ドキュメント
ドキュメントノードリファレンスモデルAI エージェント(MCP)開発者向けセルフホスティングリサーチ
REST API

スタジオプロダクション

REST で、スタジオプロダクションを読み書きします。アトミックな操作を適用し、静止画とクリップを生成して、完了したジョブを結果に反映し、書き出しを計画して、映画を共有またはコピーします。

Nodaro Cloud で利用できます

スタジオプロダクション API は、Nodaro Studio で作る映画を読み書きします。プロダクションは、設定に順番に並んだショットのリストを持つ、Nodaro のワークフローです。各ショットには、構図を決めた静止画、任意のアニメーションクリップ、そしてそれらを作ったプラン、ルック、キャスト、ボイスがあります。スクリプト、AI アシスタント、スタジオのエディターは、これらのルートを通じて、同時に同じプロダクションを操作します。

スタジオプロダクションは、Nodaro Cloud でのみ動作します。この機能を提供していないデプロイメントでは、すべてのルートが 404 を返すため、一覧のルートで 1 回だけ機能を検出してください。セルフホスティング環境では、シーン(Scene)、動画生成(Generate Video)、動画を結合(Combine Videos)などのノードを使って、映画をワークフローとして構築します。ルートはベアラートークンを必要とします。OAuth アプリのトークンには、読み取りに workflows:read、書き込みに workflows:write が必要です。認証を参照してください。

プロダクションにアクセスできない場合、どのルートも 403 ではなく 404 を返します。そのため、ID を使ってプロダクションの存在を探ることはできません。

スタジオでの呼び方と、ドキュメントのキー

スタジオとドキュメントでは、同じものを違う名前で呼びます。コードではドキュメントのキーを使い、人と話すときはスタジオでの呼び方を使います。

スタジオでの呼び方ドキュメントでの呼び方
映画プロダクション
シーン(たとえば「シーン 3」)shotId で指定する、shots[] のエントリー
シーンのフレームと、そのテイクショットの still の結果
シーンのモーションと、そのテイクショットの clip の結果
モーションの中のショットショットの beats[]

エンドポイント

メソッドパス説明
GET/v1/studio/productions/skill作成ガイド、カタログ、プランの JSON Schema、操作の語彙です。無料です。
GET/v1/studio/productions/capabilitiesこのデプロイメントが対応している、任意の操作です。
POST/v1/studio/productions/validateプランを検証します。無料で、何も保存しません。
GET/v1/studio/productions自分のプロダクションを、新しい順に一覧表示します。
POST/v1/studio/productionsプロダクションを作成します。プランから作成することもできます。
GET/v1/studio/productions/:idプロダクションを読み取ります。書き込みは行いません。
POST/v1/studio/productions/:id/ops操作のバッチを適用するか、プレビューします。
POST/v1/studio/productions/:id/reconcile完了したジョブを結果に反映します。
POST/v1/studio/productions/:id/importプランのショットをプロダクションに追加します。
POST/v1/studio/productions/:id/describeブリーフからプランを作成します。
POST/v1/studio/productions/:id/generateショットの静止画またはクリップを生成するか、見積もります。
POST/v1/studio/productions/:id/frameクリップからフレームを取り出します。
POST/v1/studio/productions/:id/voiceショットにセリフを読み上げて重ねます。
POST/v1/studio/productions/:id/revoiceショットのクリップに含まれる声を差し替えます。
POST/v1/studio/productions/:id/musicサウンドトラックを生成します。
GET/v1/studio/productions/:id/export-plan映画を組み立てる手順を、料金付きで示します。
POST/v1/studio/productions/:id/shareリンク共有を有効または無効にします。
POST/v1/studio/productions/:id/unshareリンク共有を無効にします。
POST/v1/studio/productions/:id/cloneプロダクションをコピーします。

削除のルートはありません。アーカイブは操作の 1 つで、元に戻せます。

プロダクションを読み取る

すべてのレスポンスは、{ "data": { "production": { … } } } の形でラップされます。SDK はこれを展開するので、プロダクションを返すメソッドは、プロダクションそのものを返します。プロダクションの最上位には、次が含まれます。

フィールド意味
id、name、version、updatedAt識別情報と、すべての書き込みで確認されるカウンターです。
thumbnailUrl、shared、archivedサムネイル、リンク共有が有効かどうか、アーカイブ済みかどうかです。
film、cast、folders、storyboard、music、musicPlan、cutsフィルムルック、ロール、タイムラインのフォルダー、ブリーフ、サウンドトラック、書き出したカットです。
trash{ count, items? }:削除されて、復元できるものです。
pending{ stills, clips, music, draft }:現在生成中のものです。
shots[]タイムライン順のショットで、それぞれに still、clip、startFrame、endFrame、plan、beats、look、voice などが含まれます。

GET /v1/studio/productions/:id は、detail=summary(デフォルト。件数と有効な URL)または detail=full(各ショットの履歴にあるすべての結果と、それを生んだコンテキスト)を受け付けます。shot_id を指定すると、1 つのショットだけを返します。生成のあとにもう一度読み取る、軽い方法です。GET /v1/studio/productions は、limit、cursor、includeArchived を受け付けます。

結果は積み重なっていきます。生成は置き換えを行いません。ショットの静止画は、それまでに得たすべての候補であり、クリップも同じです。静止画を削除しても、クリップには影響しません。結果は、その結果キーで指定します。結果キーは、ジョブ ID がある場合はそのジョブ ID、それ以外は URL であり、位置で指定することはありません。

curl "https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b?detail=summary" \
  -H "Authorization: Bearer $NODARO_API_KEY"
import { createClient, StaticTokenAuth } from '@nodaro/sdk'

const client = createClient({
  baseUrl: 'https://app.nodaro.ai',
  auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

const production = await client.studio.productions.get(id, { detail: 'full' })
const shot = await client.studio.productions.get(id, { shotId: 'shot-2' })

SDK は、エンベロープ(version、rebased、receipts、warnings、見積もりの credits、実行の jobIds)に型を付け、プロダクションのドキュメント自体は、オープンな JSON のままにします。

操作で編集する

プロダクションへの変更はすべて、操作です。操作とは、ショットの名前変更、移動、キャストのロールの割り当て、候補の採用など、名前の付いた編集のことです。POST /v1/studio/productions/:id/ops は、操作のバッチを検証し、適用します。操作の語彙は、このページには載せず、サーバーから提供されます。GET …/skill の operating の部分を読んでください。これは、接続しているデプロイメントに、常に一致しています。

# batch.json: { "ops": [ ...operations from GET /v1/studio/productions/skill... ], "baseVersion": 7 }
curl -X POST https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b/ops \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d @batch.json
const { production, version, rebased, receipts } =
  await client.studio.productions.ops(id, { ops, baseVersion: 7 })

レスポンスは { production, version, rebased, receipts, warnings } です。すべてのバッチに、次の 6 つの規則が当てはまります。

  1. すべてか無か。不正な操作が 1 つでもあると、バッチ全体が拒否され、何も書き込まれません。エラーは、0 から数える opIndex でその操作を示します。バッチに入れられる操作は、最大 100 個です。
  2. デフォルトはリベース。baseVersion は情報として送るだけです。古いバージョンをもとに作ったバッチも、最新のドキュメントに適用され、レスポンスには rebased: true と示されます。代わりに 409 workflow_conflict を受け取るには、strict: true を送ります。
  3. 安定したキーで指定する。ショットはその ID、フォルダーやカットはその ID、キャストの行はそのロールのスラッグ、結果はその結果キーで指定します。位置では指定しません。スタジオとスクリプトが、同じプロダクションを同時に編集する可能性があるためです。
  4. ID は自分で作る。ショット、フォルダー、カット、コピーを作成する操作は、自分が渡した ID を使います。これにより、手元のコピーとサーバーは、すべての ID で一致します。
  5. 削除はゴミ箱に入る。削除は、削除したものをプロダクションのゴミ箱に移動するだけで、操作によって復元できます。何かを完全に消すのは、明示的な完全削除だけです。
  6. レスポンスをそのまま採用する。手元のコピーは、マージするのではなく production に置き換え、次の baseVersion には version を送ります。

共有は操作ではありません。共有を変えようとするバッチは拒否されます。receipts には、操作ごとに過去形の 1 行、{ op, summary, ids?, impact? } が入ります。アシスタントが何をしたかを知りたい人には、これを見せてください。warnings には、失敗ではないものの伝える価値があること、たとえば何も変えなかった操作などが挙げられます。

バッチを適用する前にプレビューする

dryRun: true は、バッチが何を行うかを尋ねます。サーバーは、実際の書き込みと同じコンテキスト、同じ拒否ルールで処理を実行しますが、保存する前に停止します。応答は { dryRun: true, version, receipts, warnings } で、production は含まれません。各レシートには class(S は安全、D は削除、P は作品にアクセスできる人の変更、$ はクレジットの消費)が加わり、削除を元に戻せる場合は restorable: true も加わります。

プレビューに対応する前のデプロイメントは、dryRun を無視してバッチを適用します。そのため、まず空のバッチでこのフラグを確認してください。空のバッチは、どのデプロイメントでも何も変更しません。

{ "ops": [], "dryRun": true }

dryRun: true を含む応答だけが「はい」です。それ以外はすべて「いいえ」であり、ここではプレビューできないとユーザーに伝えて、バッチを送信しないでください。実際のプレビューの応答でも、このマーカーを確認してください。更新中のデプロイメントは、次のリクエストを別のサーバーで処理することがあるためです。マーカーの代わりに production を含む応答は、バッチが適用されたことを意味します。その場合は、その結果を採用し、もう一度送信しないでください。SDK は、この 2 つの確認をどちらも代わりに行い、StudioPreviewUnavailable または StudioPreviewAppliedError をスローします。

const preview = await client.studio.productions.ops(id, { ops, baseVersion, dryRun: true })
for (const r of preview.receipts) console.log(r.class, r.summary, r.restorable ?? false)

静止画とクリップを生成する

生成は、実行してからポーリングする方式です。kind: "still" または kind: "clip" と shotId を指定した POST /v1/studio/productions/:id/generate は、ジョブを送信し、プロダクション上に保留として記録し、{ jobIds, lane?, deduped?, production? } をすぐに返します。

  • ショットから組み立てる。サーバーは、ショットのプラン、ルック、キャスト、演出からリクエストを組み立てます。そのため、スクリプトからの呼び出しも、スタジオでのクリックも、同じ画像を作ります。overrides を使うと、ショット自体を変えずに、その 1 回の実行だけを変更できます。
  • 先に見積もる。dryRun: true は、実行の料金を出すだけで、何も書き込みません。応答は { dryRun: true, provider, count, credits, lane? } です。credits: null は、そのモデルの料金が不明であることを意味し、無料という意味ではありません。
  • 安全に再試行する。A-Za-z0-9_.:- からなる 8〜128 文字を自分で作った clientRequestId を使うと、再試行が安全になります。同じ ID を送ると、最初の呼び出しのジョブが deduped: true とともに返り、新たな料金はかかりません。有料の呼び出しは、これなしで再試行しないでください。
  • 結果を反映する。POST /v1/studio/productions/:id/reconcile は、完了したジョブを結果に変え、失敗したジョブを片付けます。{ landed, pending, failed, warnings, production, version } を返します。GET は書き込みを行わないため、何かを待っているときは reconcile してください。
  • クリップのレーンは自動で選ばれる。クリップの場合、モデルのルート(generate-video または text-to-video)はショットの入力から決まり、lane として返ってきます。mode は、どちらの入力の組み合わせを演出の元にするかだけを指定します。"start" または "references" です。
curl -X POST https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b/generate \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "still", "shotId": "shot-2", "count": 2, "clientRequestId": "still-shot-2-take-1" }'

curl -X POST https://app.nodaro.ai/v1/studio/productions/5d8f2b4a-7c1e-4a9d-8b3f-2e6c9a1d4f7b/reconcile \
  -H "Authorization: Bearer $NODARO_API_KEY"
import { isStudioGenerateEstimate } from '@nodaro/sdk'

const quote = await client.studio.productions.generateStill(id, 'shot-2', { count: 2, dryRun: true })
if (isStudioGenerateEstimate(quote)) console.log(quote.credits)

const run = await client.studio.productions.generateStill(id, 'shot-2', {
  count: 2,
  clientRequestId: crypto.randomUUID(),
})
// ...once the jobs finish:
const { landed, pending } = await client.studio.productions.reconcile(id)

ほかのメディアのルートは、1 つのショットに対して動作します。

ルートボディ説明
frame{ shotId, mode?, timestamp?, target?, clientRequestId? }クリップからフレームを取り出します。数秒待ってから、{ production, url? } を返します。
voice{ shotId, text, voiceId?, voiceType?, ttsProvider?, delivery?, clientRequestId? }ショットにセリフを読み上げて重ねます。処理を待ってから、{ production } を返します。
revoice{ shotId, plan, clientRequestId? }ショットのクリップに含まれる声を差し替えます。{ jobId, production } を返します。reconcile で反映されます。
music{ prompt, duration?, instrumental?, vocalGender?, model?, clientRequestId? }サウンドトラックを生成します。{ jobId, production } を返します。reconcile で反映されます。

clientRequestId は、frame と voice を含む、このページのすべての有料ルートで受け付けられます。

リンクされたクリップと撮り直し

デプロイメントによっては、確認済みの 2 つのフレームの間を動くクリップに対応しています。まず GET /v1/studio/productions/capabilities を確認してください。operations.generateLinkedClips と operations.retakeLinkedClips が、それぞれ利用できるかどうかを示します。

  • リンクされたクリップを見積もる。kind: "clip"、shotId、dryRun: true を指定した generate は、両方のフレームを確認し、inputHash を含む見積もりを返します。これを、実際のリクエストで expectedInputHash として送り返してください。その間に設定やフレームが変わっていた場合、このルートは何も送信する前に 409 sequence_quote_changed を返します。
  • クリップを撮り直す。既存のリンクされたクリップの新しいテイクを、元の設定とフレームのまま見積もるには、retakeResultKey を追加します。次に、見積もりの inputHash と新しい clientRequestId を付けて送信します。mode、overrides、count は送らないでください。元のリクエストやフレームが保持されていないテイクは、撮り直せません。

ブリーフからプロダクションを作る

ガイドを読む

GET /v1/studio/productions/skill は、{ skill, catalog, schema, operating, generatedFrom } を返します。プランの書き方、すべてのピッカー、モデル、使える値、プランの JSON Schema、操作の語彙です。

プランを検証する

{ plan } を指定した POST /v1/studio/productions/validate は、{ valid, errors, warnings, summary? } を返します。valid が true になるまで、各エラーの path を修正してください。キャストの名前は、自分のライブラリと照合されます。

プロダクションを作成する

{ name?, plan? } を指定した POST /v1/studio/productions は、プロダクションを作成し、プランを反映します。{ plan, mode: "append" } を指定した POST …/:id/import は、既存のプロダクションにプランのショットを追加します。どちらもメディアは生成しません。

または、監督にプランを書かせる

{ brief, llmModel, mode?, label?, clientRequestId? } を指定した POST …/:id/describe は、監督(Director)の実行を開始し、{ jobId, production } を返します。ジョブが完了したら、reconcile して、下書きされたショットを反映してください。

const { production } = await client.studio.productions.create({ name: 'The Lighthouse' })
const { jobId } = await client.studio.productions.describe(production.id, {
  brief: 'A keeper, a storm, and a light that will not start.',
  llmModel: 'gpt-5-mini',
  mode: 'replace',
})
// poll jobId with client.jobs.getStatus, then:
await client.studio.productions.reconcile(production.id)

書き出しを計画する

GET /v1/studio/productions/:id/export-plan は、映画を組み立てる手順を、順番どおりに返します。ショットごとの音声の結合、つなぎ合わせ、そして upscale による任意の 4K 仕上げです。何も実行しません。

{
  "canExport": true,
  "steps": [
    {
      "id": "voice-shot-2",
      "node": "merge-video-audio",
      "label": "Voice over shot 2",
      "credits": 20,
      "params": { "videoUrl": "https://cdn.nodaro.ai/studio/shot-2.mp4", "audioUrl": "https://cdn.nodaro.ai/studio/vo.mp3" }
    },
    {
      "id": "combine",
      "node": "combine-videos",
      "label": "Join 3 shots",
      "credits": 4,
      "params": {
        "videoUrls": [{ "fromStep": "voice-shot-2" }, "https://cdn.nodaro.ai/studio/shot-3.mp4"],
        "transition": "cut",
        "audioMode": "keep"
      }
    }
  ],
  "resultStepId": "combine",
  "estimate": 24,
  "unpriced": []
}

手順は、通常のノードのルート、たとえば動画とオーディオを結合(Merge Video & Audio)や動画を結合を使って、順番に実行します。{ "fromStep": "…" } という値は、前の手順の出力です。その手順が生成した URL に置き換えてください。そして、完成したファイルを、カットを追加する操作で、プロダクションにカットとして記録します。

組み立てるものがない場合、つまりクリップが 2 本未満の場合、canExport は false になります。いずれかの手順に料金がない場合、estimate は null になり、unpriced にそれらのモデルが示されます。合計の一部だけを示すと、実際より低い金額に見えてしまうためです。

プロダクションを共有・コピーする

  • 共有する。{ shared: true } を指定した POST …/:id/share は、読み取り専用の共有リンクを有効にします。{ shared: false } または POST …/:id/unshare は無効にします。共有を変更できるのは、オーナーまたはワークスペースの管理者だけです(それ以外は 403 forbidden)。operations.revisionedSharing が利用できる場合は、expectedVersion を追加すると、変更を、自分が確認したバージョンに結び付けられます。その後に別の編集があった場合は、409 workflow_conflict が返されます。
  • 編集できるコピーを許可する。operations.editableSharedCopies が利用できる場合、オーナーは shared: true と expectedVersion に加えて、allowEditableCopy: true も送れます。すると、ログイン済みでリンクを開いた人は、プラン、プロンプト、キャストの説明、保持されている入力、テイクの履歴をコピーできるようになります。ゴミ箱と非公開のレビューメモはコピーされず、共有を解除すると、新しいコピーはできなくなります。
  • コピーする。{ name? } を指定した POST …/:id/clone は、自分から見た状態のまま、プロダクションをコピーします。コピーは非公開で、一覧に表示される状態で始まり、自分のストレージを使います。

タイムラインを編集アプリに書き出す

POST /v1/freecut-export は、シーンのクリップからなるタイムラインを、編集アプリのプロジェクトファイルにします。外部の編集アプリで、カットの仕上げができるようにするためです。FreeCut JSON(freecut-v1)または Final Cut Pro XML(fcpxml-v1.10)ファイルを自分のストレージに書き込み、その URL を返します。クレジットはかからず、1 分あたり 10 リクエストまで使えます。このページのほかの部分と同じく、Nodaro Cloud が必要です。ほかのエディションは 404 を返します。

curl -X POST https://app.nodaro.ai/v1/freecut-export \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "json",
    "name": "The Lighthouse, cut 1",
    "timeline": {
      "musicAssetUrl": "https://cdn.nodaro.ai/studio/score.mp3",
      "scenes": [
        {
          "sceneEntityId": "scene-1",
          "compositeUrl": "https://cdn.nodaro.ai/studio/scene-1.mp4",
          "shots": [{ "shot_id": "s1", "duration_seconds": 4 }]
        },
        {
          "sceneEntityId": "scene-2",
          "compositeUrl": "https://cdn.nodaro.ai/studio/scene-2.mp4",
          "shots": [{
            "shot_id": "s2",
            "duration_seconds": 6,
            "cut_decision": { "in_offset_sec": 0, "out_offset_sec": 0.5, "transition_to_next": "dissolve" }
          }]
        }
      ]
    }
  }'
const file = await client.request('POST', '/v1/freecut-export', {
  body: { format: 'json', name: 'The Lighthouse, cut 1', timeline },
})
{
  "url": "https://cdn.nodaro.ai/exports/7b2d4f6a/freecut-3c5e7a9b-1d2f-4a6c-8e3b-5f7a9c1e2d4b.json",
  "format": "json",
  "assetId": "1e3a5c7b-9d2f-4b4a-8c6e-5f7d9b1a3c2e"
}

assetId は、そのファイルの、自分のライブラリ内のエントリーです。そのエントリーを作成できなかった場合は null になりますが、どちらの場合も url は有効です。

フィールド意味
format必須です。FreeCut JSON なら json、Final Cut Pro XML なら fcpxml です。
timeline必須です。タイムラインです。下記を参照してください。
name自分の記録用のラベルで、最大 200 文字です。
timeline.scenes必須です。再生順に、1 つ以上のシーンを指定します。各シーンが、動画トラック上の 1 つのクリップになります。
timeline.musicAssetUrl音楽トラックです。デフォルトの空文字列にすると、含まれません。
timeline.narrationAssetUrlナレーショントラックで、専用のオーディオレーンに配置されます。
timeline.fadeOutDurationSec音楽の末尾のフェードで、デフォルトは 0.8 秒です。FreeCut JSON のみです。
scene.sceneEntityId、scene.compositeUrl必須です。シーンの ID と、その結合済みクリップです。
scene.shots必須です。{ shot_id, duration_seconds, cut_decision? } からなる、1 つ以上のショットです。各ショットの長さを足すと、シーンの長さになります。
cut_decision.in_offset_sec、cut_decision.out_offset_secカットの決定には必須です。開始側のトリム(シーンの最初のショットから読み取ります)と、終了側のトリム(シーンの最後のショットから読み取ります)です。
cut_decision.transition_to_nextカットの決定には必須です。hard_cut、dissolve、match_cut、overlap のいずれかで、次のシーンへのトランジションです。
cut_decision.transition_duration_secトランジションのデフォルトの長さを上書きします。hard_cut と match_cut は 0、dissolve は 0.5 秒、overlap は 1 秒です。

dissolve と overlap は、トランジションの長さの分だけ、2 つのクリップを重ねます。hard_cut と match_cut は、クリップをそのままつなぎます。どのショットにも cut_decision がない場合、書き出しは単純な連結になります。シーンごとに 1 つのクリップを、順に、すべてハードカットでつなぎ、音楽はタイムライン全体に流れます。シーンの中のトリムは適用されません。各シーンのクリップは、すでに結合されているためです。

共有とリミックスのレコード

ショットレコードは、自分が組み立てたショットの状態を保存します。ピッカーの選択、プロンプト、対象のモデル、@ でメンションしたアセット、結果です。レコードは推測できない短い ID で保存され、この ID が /s/:id の共有リンクとワンクリックでのリミックスを支えています。これらのルートは、プロダクションとは別のものです。

メソッドパス認証説明
POST/v1/shotsベアラートークンレコードを作成します。{ id } を返します。
GET/v1/shots/:idなしレコードを読み取ります。非公開のレコードは、オーナー以外の全員に 404 を返します。IP アドレスごとに制限されます。
PATCH/v1/shots/:idオーナーvisibility を含む、任意のフィールドを更新します。
DELETE/v1/shots/:idオーナーレコードを削除します。

ボディには、mode(single、multi-shot、frame-to-motion、storyboard のいずれか)と、selectionState(各ピッカーの選択値で、{ pickerNodeType: valueId } または { pickerNodeType: { field: valueId } } の形式)が入ります。ほかに、freeText、negativePrompt、assembledPrompt、perModelPrompts、models、entityRefs、resultUrls を任意で指定できます。visibility のデフォルトは private です。レコードを共有可能にするには、public に設定します。

resultUrls に指定できるのは、ふつうの公開 http または https の URL だけです。署名付き URL は拒否されるため、トークンが共有レコードに漏れることはありません。レコードには schemaVersion が記録されます。レコードが、もう存在しないカタログのエントリーを指している場合、リミックスを失敗させる代わりに、メモを付けて読み飛ばします。

curl -X POST https://app.nodaro.ai/v1/shots \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "single",
    "selectionState": { "mood": "melancholy", "lighting": { "timeOfDay": "blue-hour" } },
    "freeText": "a woman at a bus stop",
    "models": ["nano-banana-pro"],
    "visibility": "public"
  }'
const { id: shotId } = await client.shots.create({
  mode: 'single',
  selectionState: { mood: 'melancholy', lighting: { timeOfDay: 'blue-hour' } },
  freeText: 'a woman at a bus stop',
})
await client.shots.update(shotId, { visibility: 'public' })
const { shot } = await client.shots.get(shotId)
nodaro shots create --file shot.json --visibility public
nodaro shots get <id> --json
nodaro shots delete <id>

MCP から使う

AI アシスタントは、get_studio_production_skill、validate_studio_plan、create_studio_production、edit_studio_production、generate_studio_still、generate_studio_clip、plan_studio_export などのツールを通じて、同じプロダクションを使います。書き込み権限でプロダクションを読み取ると、完了したジョブも反映されます。MCP のスタジオプロダクションを参照してください。

エラー

ステータスコード意味
400validation_errorボディまたはプランが正しくありません。path にフィールド名が示されます。書き出しのルートでは、issues に問題の一覧が入ります。
400op_invalid、op_target_missing…/ops で、いずれかの操作が正しくありませんでした。opIndex にその操作が示されます。何も書き込まれていません。修正して、バッチをもう一度送信してください。
402insufficient_creditsアカウントが、この生成の料金をまかなえません。
403forbidden共有を変更できるのは、オーナーまたはワークスペースの管理者だけです。
404not_foundこの呼び出し元に対応するプロダクションがないか、このデプロイメントがプロダクションを提供していません。
404op_target_missing生成またはメディアのルートで、指定したショット、結果、ロールが、プロダクションの中に見つかりません。
409workflow_conflictstrict: true または expectedVersion を送ったところ、プロダクションが変わっていました。もう一度読み取って、再度適用してください。
409production_busy書き込みの最中に、プロダクションが変わり続けました。もう一度読み取って、再試行してください。
409sequence_quote_changed見積もり以降に、リンクされたクリップの設定またはフレームが変わりました。もう一度見積もってください。
413storage_exceededアカウントが、ストレージの上限を超えています。
429rate_limit_exceeded1 分間に 10 回を超えてタイムラインを書き出しました。Retry-After にある時間だけ待ってください。

SDK では、これらは型付きのエラーとして届きます。StudioOpError(opIndex を含む)、書き込みの 2 つの 409 コードに対する WorkflowConflictError、InsufficientCreditsError、StorageExceededError、NotFoundError です。

よくある質問

最終更新

目次