# ノード

> POST /v1/<node-type> で、任意の Nodaro ノードを実行します。ノード、モデル、ピッカーの値を調べ、リファレンスや演出の ID でプロンプトを調整する方法も説明します。

Source: https://nodaro.ai/ja/docs/developers/api/nodes

**ノードの実行**は、ワークフローを組まずに、Nodaro のノードを 1 つだけ直接呼び出します。`POST /v1/<node-type>` に、ノードの設定をボディとして送信します。`generate-image` や `generate-video` から `text-to-speech` まで、すべての生成ノードがこの形に従っており、検出用のエンドポイントで、どのノード、モデル、設定が存在するかを調べられます。ほとんどのノードの実行は非同期です。レスポンスは `jobId` で、結果ができるまでポーリングします。

## ノードを実行する
ノードタイプのルートは、`POST /v1/` の後にそのタイプを付けたもので、ボディはノードの設定を JSON にしたものです。

```http
POST /v1/generate-image
Authorization: Bearer ndr_…
Content-Type: application/json

{ "prompt": "a lighthouse in a storm, oil painting", "provider": "nano-banana-pro", "aspectRatio": "3:4" }
```

何が返ってくるかは、ノードによって異なります。

- **生成ノード**は、`200` とともに `{ "jobId": "…" }` を返します。処理はワーカー上で実行され、ステータスが `completed` になるまで `GET /v1/jobs/:id/status` でジョブをポーリングします。[ジョブ](https://nodaro.ai/docs/developers/api/jobs)を参照してください。
- **画像と動画の生成**では、`adjustments` が加わることがあります。これは、選んだモデルのためにサーバーが補正した設定の一覧です。`generate-video` では、`warnings` が加わることもあります。[パラメーターの補正](https://nodaro.ai/docs/developers/api/workflows#parameter-corrections)を参照してください。
- `combine-text` などの**インラインノード**は、`jobId` を返さずに、完全な結果をすぐに返します。
- `web-scrape` などの**スクレイパー**も、すぐに応答します。レスポンスには、履歴のための `jobId` と、データ自体の両方が含まれます。

生成は、開始時にクレジットを確保します。アカウントがその実行分をまかなえない場合、呼び出しは `402 insufficient_credits` を返します。

### パスが長いノード
言語モデルを呼び出すテキストノードの多くは、同じ `POST /v1/<node-type>` の規則に従います。`generate-script`、`image-critic`、`qa-check`、`describe-to-picker` です。それ以外の一部は、より長いパスで登録されています。

| ノードタイプ | ルート |
| --- | --- |
| `llm-chat` | `POST /v1/llm-chat/generate` |
| `after-effects` | `POST /v1/after-effects/generate` |
| `motion-graphics` | `POST /v1/motion-graphics/generate` |
| `lottie-overlay` | `POST /v1/lottie-overlay/generate` |
| `3d-title` | `POST /v1/3d-title/generate` |
| `image-to-text` | `POST /v1/image-to-text/describe` |
| `video-composer` | `POST /v1/scene-graph/generate` |

SDK の `client.nodes.run(type, params)` は `/v1/<type>` に POST します。そのため、これらのノードは `client.request('POST', '/v1/llm-chat/generate', { body })` のように呼び出します。

言語モデルのルートは、2 つの任意のフィールドを受け付けます。

- **`reasoningEffort`**：モデルによって、`none`、`low`、`medium`、`high`、`xhigh`、`max` のいずれかです。省略するか、そのモデルが対応していない段階を指定すると、モデル自体のデフォルトになります。`xhigh` と `max` は、クレジットのティアが 1 段階上がります。
- **`advancedMode: true`**：Gemini モデルのみです。リクエストはモデル開発元自身の API 上で実行されます。これは、`temperature`、`maxTokens`、推論の全範囲が効く唯一の経路です。`reasoningEffort` とは別に、クレジットのティアが 1 段階上がります。この経路がないモデルは、`400 advanced_mode_unsupported` を返します。

## 例：画像を生成する
### ジョブを開始する
**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/generate-image \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
"prompt": "a knight on a hill at dawn, cinematic",
"provider": "nano-banana-pro",
"aspectRatio": "16:9",
"resolution": "2K"
}'
```

```json
{ "jobId": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10" }
```

**TypeScript SDK**

```ts

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

const result = await client.nodes.run('generate-image', {
prompt: 'a knight on a hill at dawn, cinematic',
provider: 'nano-banana-pro',
aspectRatio: '16:9',
resolution: '2K',
})
```

**CLI**

```bash
nodaro nodes run generate-image \
  --param prompt="a knight on a hill at dawn, cinematic" \
  --param provider=nano-banana-pro \
  --param aspectRatio=16:9 \
  --param resolution=2K
```

### ジョブをポーリングする
ステータスが `completed` か `failed` になるまで、2〜5 秒ごとにジョブのステータスを確認します。

```bash
curl -s https://app.nodaro.ai/v1/jobs/0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10/status \
  -H "Authorization: Bearer $NODARO_API_KEY" | jq -r .data.status
```

### 結果を読み取る
完了した画像のジョブは、画像の URL を `output_data.imageUrl` に持ちます。動画のジョブは `videoUrl` を、オーディオのジョブは `audioUrl` を使い、多くのジョブは `thumbnailUrl` も持ちます。

```json
{
"data": {
"id": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10",
"status": "completed",
"progress": 100,
"output_data": { "imageUrl": "https://…/0f1a9c2e.png" },
"error_message": null
}
}
```

SDK と CLI は、代わりにポーリングを行えます。

**TypeScript SDK**

```ts
const output = await client.nodes.runAndWait('generate-image', {
prompt: 'a knight on a hill at dawn, cinematic',
provider: 'nano-banana-pro',
})
console.log(output.imageUrl)

// Several candidates at once, in input order:
const results = await client.nodes.runMany('generate-image', [
{ prompt: 'a knight on a hill, sunrise' },
{ prompt: 'a knight on a hill, golden hour' },
{ prompt: 'a knight on a hill, blue hour' },
])
for (const { jobId, output } of results) console.log(jobId, output.imageUrl)
```

**CLI**

```bash
nodaro nodes run generate-image \
  --param prompt="a knight on a hill at dawn, cinematic" \
  --param provider=nano-banana-pro \
  --watch --json | jq -r '.output_data.imageUrl'
```

`runAndWait` は、デフォルトで 2,000 ミリ秒ごとに、最大 15 分間ポーリングします。これは `pollMs` と `maxMs` で変更でき、`signal` に渡す `AbortSignal` で停止でき、`onProgress` で進行状況を追えます。`instanceof` で捕捉できる、型付きのエラーをスローします。

| エラー | 発生する場合 |
| --- | --- |
| `InsufficientCreditsError`、`StorageExceededError`、`JobBlockedError` | ジョブが開始される前に、実行が拒否された場合です。 |
| `JobFailedError` | ジョブが `failed` か `cancelled` で終わった場合です。`jobId` とエラーメッセージを持ちます。 |
| `JobTimeoutError` | `maxMs` が経過した場合です。ジョブはキャンセルされず、通常はそのまま完了します。後で `client.jobs.get(jobId)` を使って取得してください。 |
| `JobAbortedError` | 自分の `signal` が発火した場合です。 |
| `JobHeldError` | 結果をレビューするデプロイ環境で、ジョブが `pending_review` になった場合です。ジョブはキャンセルされません。後で確認してください。 |

CLI では、配列やネストしたオブジェクトなどの複雑なボディを、`--params-file body.json` でファイルから渡します。同じキーについては、フラグの値がファイルの値を上書きします。`true`、`false`、`null`、数値は、テキストから変換されます。

### 画像生成の設定
以下のフィールドは、`POST /v1/generate-image` でよく使う設定です。各設定がエディターで何を行うかは、[**画像生成**（Generate Image）](https://nodaro.ai/docs/nodes/image/generate-image)を、各モデルが対応している内容は、[画像モデル](https://nodaro.ai/docs/models/image)を参照してください。

<TypeTable
type={{
prompt: { type: 'string', description: '画像の説明です。通信上は最大 30,000 文字ですが、各モデルにはそれより低い独自の上限があり、長いプロンプトはその上限に収まるように短くされます。', required: true },
provider: { type: 'string', description: 'モデル ID です。nano-banana-pro や gpt-image-2-5-flare などです。GET /v1/nodes/generate-image の providers に、すべての ID が一覧表示されます。' },
aspectRatio: { type: 'string', description: 'auto、1:1、16:9、9:16、4:3、3:4、3:2、2:3、5:4、4:5、21:9 などです。モデルが対応していない値は、補正されます。' },
resolution: { type: 'string', description: '対応しているモデルでの 1K、2K、4K、または 0.5 MP、1 MP、2 MP、4 MP です。クレジットの料金に影響します。' },
quality: { type: 'string', description: '品質のティアがあるモデルでの、basic、medium、high です。クレジットの料金に影響します。' },
negativePrompt: { type: 'string', description: '避けたい内容で、最大 5,000 文字です。' },
seed: { type: 'integer', description: '対応しているモデルで、実行を再現可能にする固定の数値です。' },
referenceImageUrls: { type: 'string[]', description: '最大 14 個のリファレンス画像の URL です。重要なものを先に指定します。' },
connectedReferences: { type: 'object[]', description: 'ルートがプロンプトに組み立てる、ラベル付きのリファレンスです。「リファレンスを使う」を参照してください。' },
describedReferences: { type: 'object[]', description: '画像を持っていない、名前付きの被写体を最大 10 個です。' },
referenceOrder: { type: 'string[]', description: 'リファレンスに番号を振る順序で並べた、リファレンス ID です。' },
referenceLock: { type: 'string', description: 'standard または multi-person です。検証済みの、リファレンスに忠実に従わせる言い回しを追加します。' },
direction: { type: 'object', description: 'カメラ、光、ルックのピッカー ID です。「ID による演出」を参照してください。' },
subject: { type: 'object', description: 'ショットの人物、スタイリング、小道具のピッカー ID です。' },
baseImageUrl: { type: 'string', description: '最初から生成する代わりに編集する画像です。' },
maskUrl: { type: 'string', description: 'baseImageUrl と一緒に使います。白い部分を変更し、黒い部分を保つマスクです。再生成されるのは、白い部分だけです。' },
strength: { type: 'number', description: '0〜1 です。対応しているモデルで、リファインが baseImageUrl からどれだけ離れてよいかです。' },
guidanceScale: { type: 'number', description: '対応しているモデルでの、0〜20 です。' },
}}
/>

マスクを使った編集やリファインの料金は、そのモデルでの新規生成と同じです。

## 動画を生成する
動画を作るルートは 2 つあります。`POST /v1/generate-video` は、画像からアニメーションを作ります。`imageUrl` の開始フレーム、任意で `endFrameUrl` の最後のフレーム、または、対応しているモデルではリファレンスだけです。`POST /v1/text-to-video` は、プロンプトだけからクリップを作るので、`prompt` が必須です。`provider` を省略すると、プラットフォームのデフォルトの動画モデルが使われます。

```json
{
"imageUrl": "https://…/frame.png",
"provider": "seedance-2",
"prompt": "she turns toward the window",
"duration": 8,
"resolution": "720p",
"direction": { "cameraMotion": "dolly-in", "timeOfDay": "dawn" }
}
```

レスポンスは `{ "jobId": "…" }` で、当てはまる場合は `warnings` と `adjustments` も含まれます。完了したジョブは `output_data.videoUrl` を持ちます。

<TypeTable
type={{
imageUrl: { type: 'string', description: '開始フレームです。モデルがリファレンスだけで実行できる場合を除き、必須です。' },
endFrameUrl: { type: 'string', description: '最初と最後のフレームによる動画に対応したモデルでの、最後のフレームです。' },
prompt: { type: 'string', description: 'クリップの中で起こることで、通信上は最大 30,000 文字です。動画モデルには、それよりずっと低い独自の上限があります。' },
provider: { type: 'string', description: 'モデル ID です。seedance-2、kling-3.0、veo3.1 などです。' },
duration: { type: 'number', description: 'モデルによって 1〜60 秒です。Seedance 2 系列では、-1 が「自動」（Auto）を意味します。' },
resolution: { type: 'string', description: 'たとえば 480p、720p、1080p、4k です。対応していない値は、そのモデルが持つ最も近い段階に変更されます。' },
aspectRatio: { type: 'string', description: 'モデルによって、16:9、9:16、1:1、4:3、3:4、4:5、5:4、21:9、9:21、adaptive のいずれかです。' },
generateAudio: { type: 'boolean', description: '対応しているモデルで、動画に音声を付けるよう指定します。' },
negativePrompt: { type: 'string', description: '避けたい内容です。' },
referenceImageUrls: { type: 'string[]', description: 'リファレンス画像で、通信上は最大 30 個です。各モデルには、それぞれ独自の上限があります。' },
referenceVideoUrls: { type: 'string[]', description: '対応しているモデルでの、リファレンスクリップで、最大 10 個です。' },
referenceAudioUrls: { type: 'string[]', description: '対応しているモデルでの、リファレンスオーディオで、最大 10 個です。' },
connectedReferences: { type: 'object[]', description: 'ラベル付きの画像のリファレンスです。「リファレンスを使う」を参照してください。' },
direction: { type: 'object', description: 'カメラモーション、フレーミング、光、ルックのピッカー ID です。' },
subject: { type: 'object', description: '人物、スタイリング、小道具のピッカー ID です。' },
seed: { type: 'integer', description: '対応しているモデルで、実行を再現可能にする固定の数値です。' },
}}
/>

モデルによって異なる規則が、いくつかあります。

- **Seedance 2 系列では、`resolution` と `aspectRatio` がそのまま渡されます**。モデルが対応していない値は、拒否されずに無視されます。`4k` と `adaptive` に対応しているのは、[Seedance 2](https://nodaro.ai/docs/models/video/seedance-2) だけです。[Seedance 2.5](https://nodaro.ai/docs/models/video/seedance-2-5) は、1 回の呼び出しで最大 30 秒を作れます。開始フレームがある場合、常にそのフレームのアスペクト比でレンダリングします。
- **[MiniMax Hailuo 3](https://nodaro.ai/docs/models/video/minimax-h3) の `resolution` は `2K` または `768P`** で、まさにこの表記で指定します。送信する値は、`GET /v1/nodes/:type` の `providerResolutionWire` に一覧表示されます。
- Seedance 2 と MiniMax Hailuo 3 では、**フレームとリファレンスを組み合わせられます**。開始または終了フレームと一緒にリファレンスを送信すると、そのフレームは固定の始点や終点ではなく、プロンプト内の番号付きリファレンスになります。[Wan 3.0](https://nodaro.ai/docs/models/video/wan-3-0) では両方を送れないため、代わりにフレームがリファレンスの一覧の末尾に追加され、呼び出しはそのまま成功します。
- **リファレンス動画は、料金が高くなります。**課金対象にしているモデルでは、リファレンスクリップの料金は、そのクリップ自体の長さと出力の長さを合わせて決まります。そのため、元のクリップが長いほど、確保されるクレジットも増えます。Wan 3.0 は、出力の秒数だけを課金します。

各モデルの長さ、解像度、クレジット料金については、[動画モデル](https://nodaro.ai/docs/models/video)を読むか、`GET /v1/models` を呼び出してください。

## リファレンスを使う
リファレンスは、モデルに合わせてほしい画像、そして動画ではクリップとオーディオでもあります。フラットな URL のリストとして送ることも、ルートが番号を振り、ラベルを付けてプロンプトに書き込む**構造化されたリファレンス**として送ることもできます。これは、エディターが接続されたノードに対して行うのと同じです。

### 構造化されたリファレンス
`connectedReferences` は、`POST /v1/generate-image`、`POST /v1/generate-video`、`POST /v1/text-to-video`、`POST /v1/extend-video` で受け付けられます。各エントリーは、1 枚の画像を表します。

<TypeTable
type={{
id: { type: 'string', description: '自分で決める、リファレンスの安定した ID です。: や / を含められるので、画像の URL も有効な ID です。', required: true },
defaultName: { type: 'string', description: 'リファレンスの名前です。たとえば Maya や Old Town です。', required: true },
source: { type: 'string', description: 'manual、wired-image、wired-character、wired-face、wired-object、wired-creature、wired-location のいずれかです。', required: true },
url: { type: 'string', description: '画像の URL です。プライベートなアドレスや、http、https 以外のスキームは拒否されます。', required: true },
description: { type: 'string', description: 'プロンプトがこのリファレンスに使うラベルです。' },
defaultRole: { type: 'string', description: 'メンションで役割を指定しなかったときに、画像から取り入れる内容です。たとえば background です。' },
identityLock: { type: '{ enabled: boolean; text?: string }', description: 'デフォルトはオフです。有効にすると、このリファレンスのアイデンティティを正確に固定する短い一文が追加されます。text は組み込みの言い回しを置き換え、その中の {ref} は、このリファレンスへの参照を表します。' },
descriptionOverride: { type: 'string', description: '最大 2,000 文字です。この実行だけで有効な、このリファレンスの説明です。キャラクターやオブジェクトに保存された説明より優先されます。' },
}}
/>

動画のルートでは、これらのエントリーを番号付きのリファレンスに変換します。

- **メンションしなかったリファレンスは、すべて添付されます。**その URL は重複を除いてリファレンスの一覧に加わり、`@image_1 (reference): <label>` のような行が付きます。`wired-character` のエントリーは、代わりに「Use these characters:」という指示の一部になります。
- **一覧は、番号を振る前に、そのモデルの上限で打ち切られます。**そのため、プロンプト内のリファレンス番号が、送信されていない画像を指すことはありません。
- **プロンプト内の `{image:N:label}`** は、添付されたリファレンスに対して番号が振られ、「`@image_N` からの label」になります。
- **`{ref:<id>}` と `{ref:<id>:label}`** は、指定した `id` でリファレンスを指定します。プラットフォームは、一覧に番号を振った後に、そのトークンをリファレンスの番号に置き換えます。そのため、番号は自分で計算する必要がありません。一覧には、まずフラットな `referenceImageUrls`、次にメンションしなかったキャラクター、その後は指定した順のほかのエントリー、という順序で番号が振られます。上限を超えていた、あるいはモデルがリファレンスを受け付けないなどの理由でリファレンスが添付されなかったトークンは、そのラベルに、それもなければ `defaultName` に、それもなければ何もない状態にフォールバックします。生のテキストのままモデルに渡ることはありません。
- **`referenceOrder`** は、リファレンス ID のリストで、リファレンスの順序を変え、それに合わせて番号も振り直します。`POST /v1/generate-image` でも使えます。

`connectedReferences` が扱うのは画像だけです。`referenceVideoUrls` と `referenceAudioUrls` は、フラットなリストのままです。`connectedReferences` を省略すると、ルートは以前と同じように動作します。`prompt` と `referenceImageUrls` は、そのまま送信されます。

### 画像のリファレンスに対応する動画モデル
| モデルファミリー | 画像のリファレンス |
| --- | --- |
| [Seedance 2](https://nodaro.ai/docs/models/video/seedance-2) 系列 | 最大 9 |
| [HappyHorse Ref2V](https://nodaro.ai/docs/models/video/happyhorse-1-1-ref2v) | 最大 9 |
| [Gemini Omni](https://nodaro.ai/docs/models/video/gemini-omni)、[Kling 3 Omni](https://nodaro.ai/docs/models/video/kling-3-omni)、[Grok Imagine image-to-video](https://nodaro.ai/docs/models/video/grok-imagine-i2v) | 最大 7 |
| [VEO 3.1 Fast](https://nodaro.ai/docs/models/video/veo-3-1-fast)、[VEO 3.1 Lite](https://nodaro.ai/docs/models/video/veo-3-1-lite) | 最大 3 |

それ以外のモデルでは、`{image:N}` トークンはラベルに置き換えられるだけで、何も添付されません。[VEO 3.1 Quality](https://nodaro.ai/docs/models/video/veo-3-1-quality) は一覧にありません。`veo3` で送ったリファレンスは無視され、実行にはそのフレームが使われます。

### リファレンスだけから動画を作る
`POST /v1/generate-video` では、送信するリファレンスの種類のうち少なくとも 1 つにモデルが対応していれば、開始フレームは省略できます。たとえば、`referenceImageUrls` だけを使う Kling 3 Omni です。VEO 3.1 Fast または Lite でリファレンスだけの実行を行うと、自動でリファレンスモードに切り替わるため、`generationType` は不要です。モデルが使えない種類のリファレンスは数に入りません。画像だけを受け付けるモデルにオーディオだけのリファレンスを送ると、`400` で拒否されます。

**終了フレームだけ**も、それをリファレンスに組み込むモデルでは使えます。Seedance 2 系列、MiniMax Hailuo 3、Wan 3.0 です。`endFrameUrl` を 1 回送信するだけでよく、`referenceImageUrls` に同じ画像を重ねて指定する必要はありません。`@nodaro/shared` パッケージは `videoProviderFoldsLoneEndFrame(provider)` をエクスポートしているので、自分のインターフェースでも同じ規則を使えます。

リクエストに開始フレームもリファレンスモードもなく、モデルが使えるリファレンスもない場合、応答はモデルによって異なります。

- Kling 3 Omni、HappyHorse Ref2V、Hailuo 2.3 のように、テキストだけから動画を作れないモデルは、`400 image_required` を返します。メッセージには、代わりにリファレンスが使えるかどうかが示されます。該当するモデルについては、`GET /v1/models` が正式な情報源です。
- それ以外のすべてのモデルは `400 validation_error` を返し、プロンプトだけのクリップには `POST /v1/text-to-video` を使うように伝えます。

### 動画を延長
`POST /v1/extend-video` が `connectedReferences` と `referenceImageUrls` を受け付けるのは、`provider: "seedance-2-extend"` のときだけです。それ以外の延長用モデルは、`400` で拒否します。上限は自分の画像 8 枚です。元のクリップの最後のフレームが、リファレンス枠を 1 つ使うためです。この枠は自分の画像の後に置かれるので、自分の番号がずれることはありません。元のクリップの最後の 2 秒は `@video_1` として渡され、延長の料金にすでに含まれています。リファレンス画像によって、クレジットが追加でかかることはありません。**動画を延長**（Extend Video）には `direction` も `subject` もありません。プロンプトは、すでに見た目が決まっているクリップの続きだからです。

### 説明だけのリファレンス
`describedReferences` は、説明はできるが、まだ画像がない被写体を指定します。台本の役どころなどです。`POST /v1/generate-image`、`/v1/generate-video`、`/v1/text-to-video`、`/v1/extend-video` は、`{ name, description }` のエントリーを最大 10 個受け付けます。`name` は最大 80 文字、`description` は最大 2,000 文字です。

- **何も添付されません。**説明だけのリファレンスはリファレンス枠を使わないため、ほかのリファレンスの番号も変わりません。
- **モデルには、1 行のテキストとして渡されます。**`<Name> — <description>.` という形です。プロンプトには名前をそのまま残します。たとえば `Natalie walks down the pier.` のようにします。この行が、Natalie が誰であるかをモデルに伝えます。`@` によるメンションは書かないでください。その記法は、添付された画像を指すためのものです。
- **単独でも機能します。**`connectedReferences` なしで `describedReferences` だけを送信できます。まだキャラクターが存在しない段階で書かれたストーリーでは、これが一般的なケースです。
- **名前か説明のないエントリーは取り除かれ**、同じ名前が繰り返されている場合は 1 回だけ書き込まれます。説明だけのリファレンスは URL を持たないため、どの延長用モデルでも使えます。

### 実行ごとの説明とキャプション
- **`descriptionOverride`** は、`connectedReferences` のエントリーに指定するもので、このリファレンスがこの実行に限って何であるかを、最大 2,000 文字で示します。保存された説明より優先して、リファレンスの指示にすでにある説明を埋めます。指示に説明がない場合は、1 行を追加します。そのため、モデルに伝わるのは 1 回だけで、2 回になることはありません。
- **`referenceVideoCaptions` と `referenceAudioCaptions`** は、`POST /v1/generate-video` と `/v1/text-to-video` で、リファレンスのクリップとオーディオを、それぞれ最大 500 文字で説明します。これらはインデックスで対応しています。`referenceVideoCaptions[0]` は `referenceVideoUrls[0]` を説明します。それぞれが `@video_1: <caption>.` のような行になり、空のエントリーは、対応をずらさずに 1 つのクリップを読み飛ばします。

### プロンプトでリファレンスをメンションする
`POST /v1/generate-image` では、リファレンスを末尾の一覧に残す代わりに、文の中に置くことができます。`@<name-slug>:<index>` と書くか、`@<name-slug>:<index>:<role>` と書いて、そこから何を取り入れるかを指定します。

```json
{
"prompt": "a wide shot of @nessie:1 rising beside @dock:2:material",
"connectedReferences": [
{ "id": "cr-1", "defaultName": "Nessie", "source": "wired-creature", "url": "https://…/nessie.png" },
{ "id": "ob-1", "defaultName": "Dock", "source": "wired-object", "url": "https://…/dock.png" }
]
}
```

モデルは「a wide shot of the creature from reference image A rising beside the material from reference image B」を受け取ります。

- **スラッグは `defaultName`** から作られます。小文字にし、それ以外の文字の連続は 1 つの `-` に変換します。`Old Town` は `old-town` になります。スラッグを設定するフィールドはありません。
- **インデックスは、メンションをリファレンスに一致させるためだけのものです。**プロンプトに書き込まれることはなく、リファレンスへの番号付けはプラットフォームが自分で行います。
- 画像（`manual` と `wired-image`）の**役割**は、`object`、`person`、`face`、`clothes`、`background`、`style`、`pose`、`texture` です。クリーチャーは `creature`、`anatomy`、`markings`、`pose`、`color`、`style` を取ります。オブジェクトは `object`、`shape`、`material`、`color`、`texture`、`style` を取ります。それ以外の 1 語は、書かれたとおりに渡されます。役割を指定しない場合は、そのエントリーの `defaultRole` が適用されます。
- メンションの後に付ける **`~lock` と `~nolock`** は、`@town:1:background~lock` のように、そのメンションに限って、リファレンスの顔の固定をオンまたはオフにします。
- **名前は、次の順序で照合されます。**キャラクター、ロケーション、画像、クリーチャー、オブジェクトです。キャラクターと画像が同じ名前を共有している場合はキャラクターを意味し、同じ種類の中では、最初に一致したものが優先されます。
- **スラッグが数字で始まる名前は、メンションできません。**たとえば `3D Render` は `3d-render` になります。メンションするには、リファレンスの名前を変更してください。モデルの上限を超えていたリファレンスへのメンションは、そのままの文字として残ります。
- **リファレンスをメンションすると、その位置が移動します。**末尾の一覧から、入力した場所へと移ります。それより後のリファレンスの文字も、文に合わせて変わります。クリーチャーやオブジェクトでは、メンションによって、末尾に追加されるはずだった行も置き換えられます。

### リファレンスの固定
`POST /v1/generate-image` の `referenceLock` は、シーンの前に、検証済みの、リファレンスに忠実に従わせる Nodaro の言い回しを追加します。

| 値 | 追加される内容 | 使う場面 |
| --- | --- | --- |
| `standard` | リファレンスに写っているものだけを使い、似姿を保ち、それらを組み合わせるという指示 | 複数のリファレンスからの合成 |
| `multi-person` | 上記に加えて、顔を変えたり混ぜたりしないという規則 | 1 つのショットに 2 人以上の顔 |

送信するのはテキストではなく ID です。言い回しはプラットフォーム側が持っており、クライアントを更新しなくても改善されます。フィールドを省略すると、ロックは追加されません。

## ID による演出
`direction` は、カメラ、光、ルックを、文章ではなく**ピッカー ID** で表します。`POST /v1/generate-image`、`POST /v1/generate-video`、`POST /v1/text-to-video` が受け付けます。Nodaro は、各 ID に対応する検証済みの言い回しを、自分でプロンプトに書き込みます。そのため、保存したリクエストは、クライアントが書いたテキストのまま固定されるのではなく、時間とともに改善された言い回しを取り込みます。

```json
{
"prompt": "a knight on a hill",
"provider": "nano-banana-pro",
"direction": {
"shotSize": "wide-shot",
"lens": "wide-24mm",
"lightingStyle": "rembrandt",
"style": "anime",
"mood": ["happy", "joyful"]
}
}
```

### キーと、その ID の取得元
各キーは、クリエイティブコントロールピッカーの 1 つのフィールドです。有効な ID は、エディターのピッカーが使うのと同じカタログである `GET /v1/picker-catalogs/<picker>` から取得します。

| キー | ピッカー | 画像 | 動画 |
| --- | --- | --- | --- |
| `shotSize`、`angle`、`coverage`、`composition`、`vantage` | [**フレーミング**（Framing）](https://nodaro.ai/docs/nodes/creative-controls/framing) | はい | はい |
| `pose` | [**ポーズ**（Pose）](https://nodaro.ai/docs/nodes/creative-controls/pose) | はい | はい |
| `compositionEffect` | [**構図エフェクト**（Composition Effects）](https://nodaro.ai/docs/nodes/creative-controls/composition-effects) | はい | はい |
| `cameraFormat` | [**カメラ／フィルム**（Camera / Film Stock）](https://nodaro.ai/docs/nodes/creative-controls/camera-format) | はい | はい |
| `lens` | [**レンズ**（Lens）](https://nodaro.ai/docs/nodes/creative-controls/lens) | はい | はい |
| `aperture`、`shutterSpeed`、`isoValue` | [**露出設定**（Exposure Settings）](https://nodaro.ai/docs/nodes/creative-controls/exposure-settings) | はい | いいえ |
| `timeOfDay`、`lightingStyle`、`lightingDirection`、`lightingRatio`、`colorTemperature` | [**ライティング**（Lighting）](https://nodaro.ai/docs/nodes/creative-controls/lighting) | はい | はい |
| `colorLook` | [**カラー／ルック**（Color / Look）](https://nodaro.ai/docs/nodes/creative-controls/color-look) | はい | はい |
| `atmosphere` | [**大気効果**（Atmosphere）](https://nodaro.ai/docs/nodes/creative-controls/atmosphere) | はい | はい |
| `postProcess` | [**ポストプロセスエフェクト**（Post-Process Effects）](https://nodaro.ai/docs/nodes/creative-controls/post-process-effects) | はい | いいえ |
| `style` | [**スタイル**（Style）](https://nodaro.ai/docs/nodes/creative-controls/style) | はい | はい |
| `mood` | [**ムード**（Mood）](https://nodaro.ai/docs/nodes/creative-controls/mood) | はい | はい |
| `aesthetic` | [**テイスト**（Aesthetic）](https://nodaro.ai/docs/nodes/creative-controls/aesthetic) | はい | はい |
| `photoGenre` | [**写真ジャンル**（Photo Genre）](https://nodaro.ai/docs/nodes/creative-controls/photo-genre) | はい | いいえ |
| `photographer` | [**写真家**（Photographer）](https://nodaro.ai/docs/nodes/creative-controls/photographer) | はい | いいえ |
| `renderQuality` | [**レンダリング品質**（Render Quality）](https://nodaro.ai/docs/nodes/creative-controls/render-quality) | はい | いいえ |
| `setting` | [**舞台設定**（Setting）](https://nodaro.ai/docs/nodes/creative-controls/setting) | はい | はい |
| `era` | [**年代／時代**（Era）](https://nodaro.ai/docs/nodes/creative-controls/era) | はい | はい |
| `backdrop` | [**背景**（Backdrop）](https://nodaro.ai/docs/nodes/creative-controls/backdrop) | はい | はい |
| `cameraMotion` | [**カメラモーション**（Camera Motion）](https://nodaro.ai/docs/nodes/creative-controls/camera-motion) | いいえ | はい |
| `actionFx` | [**アクション FX**（Action FX）](https://nodaro.ai/docs/nodes/creative-controls/action-fx) | いいえ | はい |
| `temporalSpeed`、`temporalFreeze`、`temporalDirection`、`temporalShutter` | [**時間表現**（Temporal）](https://nodaro.ai/docs/nodes/creative-controls/temporal) | いいえ | はい |
| `transition` | [**トランジション**（Transition）](https://nodaro.ai/docs/nodes/creative-controls/transition) | いいえ | はい |
| `loopSubject` | [**ループの被写体**（Loop Subject）](https://nodaro.ai/docs/nodes/creative-controls/loop-subject) | いいえ | はい |

あるルートに当てはまらないキーも受け付けられ、何も追加しないだけです。そのため、ルックの ID を 1 つのマップにまとめて、変更せずに画像と動画の両方のルートに送信できます。

### 値と上限
- **1 つの ID か、リストです。**複数選択のキーである `mood`、`aesthetic`、`photographer`、`atmosphere`、`postProcess`、`composition`、`lightingStyle` は、それぞれ独自の上限まで受け付け、それを超える ID は取り除かれます。単一選択のキーにリストを指定した場合は、最初のエントリーが使われます。
- **拒否される上限が 2 つあります。**`400 validation_error` になるのは、1 つのキーに 8 個を超えるエントリーを指定した場合と、100 文字を超える ID を指定した場合です。
- **なしと空は違います。**キーがないことは、ヒントがないことを意味し、デフォルト値を意味することはありません。空の文字列や空のリストも、何も追加しません。
- **不明なキーと不明な ID は、拒否されずに読み飛ばされます。**古いサーバーに新しいクライアントを使うと、エラーの代わりにヒントが減るだけです。新しいキーを送るクライアントより先に、サーバーを更新してください。
- **カスタムのカタログパックです。**独自のカタログパックを登録したデプロイ環境では、`GET /v1/picker-catalogs` が、パックが追加する ID を一覧表示します。これらの ID は受け付けられますが、`direction` に言い回しを追加することはありません。

### 言葉が入る場所
これらの一節は、プロンプトの後の `[style]` セクションに追加されます。フィルムの行には `cameraFormat`、`colorLook`、`style`、`era` が、シーンの行にはそれ以外のルックのキーが入ります。

```text
a knight on a hill

[style]:
<film line>
<scene line>
```

- 行の中の順序は、プラットフォーム側の固定の順序であり、指定したキーの順序ではありません。2 つのキーが同じ一節を指すと、それは 1 回だけ書き込まれます。
- 何も入らない行は省かれます。どのキーも何も追加しない場合、セクション自体がなくなり、`prompt` はそのままモデルに渡ります。
- 動画のルートでは、モーションのキーは異なる働き方をします。`cross-dissolve` のような短い専門用語を追加し、本文の中、つまり自分の文章の後にとどまります。モーションはショットの一部だからです。`cameraMotion` が最初に来ます。`[style]` セクションに入るのは、ルックのキーだけです。

### プロンプトが長すぎる場合
各モデルが受け付けるプロンプトの長さには、それぞれ上限があります。`direction` をすべて含めると、それだけで小さな上限を超えることがあります。たとえば、画像側の Seedream では 3,000 文字、動画側の Kling では 1,000 文字です。そうなった場合、Nodaro は、固定の順序の末尾から、プロンプトが収まるまで、direction の一節を 1 つずつ取り除きます。それより先に、ほかの部分が取り除かれることはありません。

- subject の一節が取り除かれるのは、direction の一節がすべて取り除かれた後だけです。
- 自分の文章、リファレンス、それらを結び付ける語句、`@` によるメンション、`Style:` と `Avoid:` の行は、常に残ります。
- ヒントがなくなってもまだプロンプトが収まらない場合に限り、テキストの末尾が切り詰められ、`...` が追加されます。

動画のルートでは、この予算に、ルートが追加するリファレンスの指示も数えられ、これらが取り除かれることはありません。ネガティブプロンプトの設定がないモデルでは、`negativePrompt` が `Avoid:` の行として追加され、その分の余地が最初に確保されます。そのため、長いネガティブプロンプトは、文章ではなくヒントの一節を犠牲にします。任意の `injectCharacterContext` のテキストは、この処理の後に追加され、この予算には含まれません。

ジョブには、何が起きたかが記録されます。`input_data.prompt` はモデルが受け取った内容、`input_data.userPrompt` は自分が送信したテキスト（`direction` だけを送信した場合は空の文字列）、`input_data.direction` は送信したとおりの ID です。

### ノードに保存された演出
保存されたワークフロー内の[画像生成](https://nodaro.ai/docs/nodes/image/generate-image)ノードは、API、MCP、またはワークフローを作成するアプリが書き込んだ、同じ `direction` オブジェクトをデータの中に持てます。エディターは、実行のたびに、そして最終的なプロンプトのプレビューでも、これを反映します。保存された ID は、接続されたフレーミング、ライティング、スタイルのピッカーに追加されます。接続によるヒントが先に来て、その後に保存された ID が続きます。プリセットとワークフローのエクスポートは、ノードのほかの部分と一緒に ID を保持します。

## ID による被写体の指定
`subject` は、**ショットに誰が写っているか**についての、同じ考え方です。人物、そのスタイリング、フレーム内の小道具です。`POST /v1/generate-image`、`POST /v1/generate-video`、`POST /v1/text-to-video` が受け付けます。

```json
{
"prompt": "on the seawall at dusk",
"provider": "nano-banana-pro",
"subject": {
"type": "woman",
"age": "age-30s",
"ethnicity": "east-asian",
"hairBase": "base-short-straight",
"makeup": "makeup-smoky",
"outerwear": "outerwear-trench",
"heldProp": "smartphone"
}
}
```

- **キーは、[人物（Person）](https://nodaro.ai/docs/nodes/creative-controls/person)と[スタイリング（Styling）](https://nodaro.ai/docs/nodes/creative-controls/styling)ピッカーのフィールドです。**`type`、`age`、`ethnicity`、`faceShape`、`hairColor`、`skinTone`、`makeup`、`outfit`、`outerwear`、`footwear` などで、これに加えて 3 つの小道具のキー、`heldProp`（[**手に持つ小道具**（Held Prop）](https://nodaro.ai/docs/nodes/creative-controls/held-prop)）、`material`（[**素材**（Material）](https://nodaro.ai/docs/nodes/creative-controls/material)）、`animal`（[**動物**（Animal）](https://nodaro.ai/docs/nodes/creative-controls/animal)）があります。`GET /v1/picker-catalogs/person` と `/styling` に、すべてのフィールドと ID が一覧表示されます。
- **`subject` と `direction` は、決して重なりません。**それぞれのキーは別々なので、1 つの選択が 2 つの一節を追加することはありません。`pose` は `direction` に属します。
- **`customAge` は、数値を指定する唯一のフィールドです。**正確な年齢を指定するには、`"age": "age-custom"` と一緒に `"customAge": 34` を送信します。値は四捨五入され、0〜120 の範囲に保たれます。
- **リストには、キーごとに上限があります。**`jewelry`、`wardrobeState`、`distinctiveFeature` は 3 個までです。`ethnicity`、`regionalAesthetic`、`hairColor`、`eyeColor`、`lipState`、`eyeState`、`skinTexture`、`hairState`、`heldProp`、`material` は 2 個までです。`animal` を含む、それ以外のすべてのキーは 1 個までです。それを超える ID は取り除かれます。
- **拒否される上限**：1 つのキーに 8 個を超えるエントリー、100 文字を超える ID、128 個を超えるキー、64 文字を超えるキーは、いずれも `400 validation_error` になります。
- **不明なキーは取り除かれ、不明な ID は読み飛ばされます。**ジョブの `input_data.subject` には、実際に使われた ID がそのまま記録されます。
- **subject の一節は、自分の文章の一部です。**direction の一節より前に置かれ、`[style]` セクションに入ることはありません。人物が 1 つの一節に、スタイリングがもう 1 つの一節になり、重なる選択は 1 回だけ書き込まれます。画像のルートでは、それぞれの選択が完全な一節を追加しますが、動画のルートでは短い用語だけを追加します。開始フレームに、すでに被写体が誰であるかが写っているためです。

## ノードを調べる
`GET /v1/nodes` は、サーバーが認識するすべてのノードタイプを一覧表示し、`GET /v1/nodes/:type` は 1 つを返します。どちらも公開されており、トークンは不要で、5 分間キャッシュされます。不明なタイプは `404 not_found` を返します。

**curl**

```bash
curl -s https://app.nodaro.ai/v1/nodes/generate-image | jq .data
```

**TypeScript SDK**

```ts
const { data: nodes } = await client.nodes.list()
const imageNodes = nodes.filter((n) => n.category === 'ai-image')

const { data: generateImage } = await client.nodes.get('generate-image')
console.log(generateImage.providers)
```

**CLI**

```bash
nodaro nodes list --category ai-image
nodaro nodes get generate-image
```

```json
{
"data": {
"type": "generate-image",
"label": "Generate Image",
"category": "ai-image",
"description": "Generate an image from a text prompt using an AI provider.",
"outputType": "image",
"creditCost": "2-620",
"providers": ["nano-banana-pro", "gpt-image-2", "gpt-image-2-5-flare", "seedream-5-pro", "z-image"],
"capabilities": ["supports-reference-image", "supports-aspect-ratio"],
"inputSchema": {
"fields": [
{ "key": "prompt", "type": "text", "required": true },
{ "key": "provider", "type": "select", "options": ["nano-banana-pro", "gpt-image-2"] },
{ "key": "aspectRatio", "type": "select" },
{ "key": "promptPrefix", "type": "text" },
{ "key": "promptSuffix", "type": "text" }
]
}
}
}
```

| フィールド | 意味 |
| --- | --- |
| `type` | ノードタイプです。ルートの `POST /v1/<type>` にもなります。 |
| `label`、`category`、`description` | エディターが、このノードをどう名付け、どうグループ分けするかです。 |
| `outputType` | `text`、`image`、`video`、`audio`、`data`、`none` のいずれかです。 |
| `creditCost` | ノードのクレジット料金、またはその範囲です。Nodaro Cloud のみです。クレジットのないエディションでは省かれます。 |
| `providers` | ノードが `provider` に受け付けるモデル ID です。 |
| `capabilities` | `supports-reference-image` のような、機能のフラグです。 |
| `inputSchema.fields` | ノードの設定です。型、必須かどうか、選択肢を含みます。 |

プロンプトを受け付けるすべてのノードには、`promptPrefix` と `promptSuffix` も一覧表示されます。プロンプトの前後に追加されるテキストです。[プロンプトの前後のテキスト](https://nodaro.ai/docs/concepts/prompt-pre-post-text)を参照してください。モデルごとの上限があるノードには、追加のフィールドがあります。

| フィールド | 意味 |
| --- | --- |
| `maxDurationSec` | ノードが受け付ける、最長の長さです。 |
| `sparseProviders` | セグメントの長さの選択肢が少ないモデルです。その間の値は、最も近いものに変更されます。 |
| `providerResolutions` | 各モデルの解像度です。たとえば `{ "minimax-h3": ["2K", "768P"] }` です。 |
| `providerResolutionWire` | 各解像度について送信する、正確な値です。たとえば MiniMax Hailuo 3 の安いティアでは、`768p` ではなく `768P` です。 |
| `soundtrack` | **動画生成 Pro**（Generate Video Pro）で、サーバーが元の音声の `soundtrack` 入力を受け付けることを示します。 |

このディスクリプターのフィールドは今後も増えていくため、知らないフィールドは無視してください。同じデータが、[ノードリファレンス](https://nodaro.ai/docs/nodes)のもとにもなっています。

## モデルを調べる
`GET /v1/models` は、種類と開発元でグループ分けされたモデルカタログを返します。公開されており、5 分間キャッシュされ、MCP の `list_models` ツールが返すのと同じデータです。

**curl**

```bash
curl -s "https://app.nodaro.ai/v1/models?kind=video&mode=i2v" | jq '.totalModels'
```

**TypeScript SDK**

```ts
const catalog = await client.models.list({ kind: 'video', mode: 'i2v' })
for (const section of catalog.sections)
for (const family of section.families)
for (const m of family.models) console.log(m.id)
```

**CLI**

```bash
nodaro models list --kind video --mode i2v
```

レスポンスは `{ sections, recommendations, totalModels }` です。各モデルは、その機能（`modes`、`features`、`aspectRatios`、`resolutions`、`durations`）、Nodaro Cloud でのバリアントごとのクレジット `pricing`、簡潔な `promptTips`、そして `doctrineCovered` を持ちます。`doctrineCovered` は、そのモデルファミリーについて、出典のあるプロンプトガイドが存在する場合にだけ `true` になります。

| クエリ | 値 | 絞り込み対象 |
| --- | --- | --- |
| `kind` | `image`、`video`、`audio` のいずれか | メディアの種類 1 つ |
| `mode` | たとえば `t2i`、`i2v`、`t2v`、`tts`、`video-analysis` | 操作 1 つ |
| `family` | 開発元です。たとえば `Google` や `Bytedance` です | 開発元 1 つ |
| `featuredOnly` | `true` | 注目のモデル |

[モデル](https://nodaro.ai/docs/models)のページにも、同じカタログが載っています。

## ピッカーの値を調べる
クリエイティブコントロールのピッカーには、有効な ID の公開カタログがあります。`direction` と `subject` が取る値です。

| メソッド | パス | 返す内容 |
| --- | --- | --- |
| `GET` | `/v1/picker-catalogs` | すべてのピッカーです。`nodeType`、`label`、`kind`、そのフィールド、`optionCount`、`imageCount` です。 |
| `GET` | `/v1/picker-catalogs/:nodeType` | 1 つのピッカーの選択肢です。`?detail=full` を付けると、各選択肢の `description` と `promptHint` が加わります。`?category=` は単一フィールドのピッカーを絞り込み、`?field=` は複数フィールドのピッカーの 1 つのフィールドを返します。 |
| `GET` | `/v1/catalogs` | デプロイ環境が整備したとおりの、すべてのカタログを 1 回の呼び出しで返します。`data` が存在するのは、デプロイ環境がカタログパックを登録した場合だけです。 |
| `POST` | `/v1/text-to-picker` | 「AI Fill」です。自由記述のシーンの説明から、多くのピッカーの ID を選びます。クレジットがかかります。 |

すべての選択肢は、`id`、`label`、そしてプロンプトで使う短い言い回しである `term` を持ち、画像がある選択肢には `imageUrl` も付きます。カタログは、`@nodaro/shared` npm パッケージのデータとしても提供されます。完全な形については、[ピッカーカタログ](https://nodaro.ai/docs/developers/picker-catalogs)を読んでください。ターミナルからは、`nodaro pickers list`、`nodaro pickers get mood --full`、`nodaro pickers analyze "<text>"` を使います。

## 構造化された LLM 出力
`POST /v1/llm/structured` は、1 回の言語モデルの呼び出しを実行します。その回答は、指定した JSON Schema に強制的に当てはめられ、検証されたうえで、オブジェクトとして返されます。料金は、モデルのティアに応じたクレジットで課金されます。

```json
{
"system": "You write production plans.",
"input": "A rainy chase through Rome.",
"jsonSchema": {
"type": "object",
"properties": { "title": { "type": "string" } },
"required": ["title"]
}
}
```

回答は `{ jobId, output, usage: { inputTokens, outputTokens } }` で、`output` が、指定したスキーマの形になります。

- **フィールド**：`system`、`input`、`jsonSchema` が必須で、任意で `schemaName`（最大 64 文字）、`llmModel`、`reasoningEffort`、`maxRetries`、`origin`、`advancedMode`、`temperature`、`maxTokens` を指定できます。`system` と `input` は、それぞれ最大 100,000 文字まで指定でき、`input` には少なくとも 1 文字が必要です。
- **モデル**：`llmModel` を指定しない場合、呼び出しは Gemini 3.6 Flash で実行されます。
- **スキーマ**：ルートは、単純なオブジェクトのスキーマである必要があり、最大 64 KB、深さ 20 レベルまでです。`properties`、`required`、`additionalProperties`、`items`、基本の型、ルートより下の `enum`、`const`、`anyOf`、`oneOf`、数値と長さの範囲、`multipleOf`、`exclusiveMinimum`、`description` を使えます。`not`、`if`、`then`、`else`、`dependent` 系のキーワード、外部の `$ref`、ルートでのコンビネーターは `400` を返します。ルートより下の `required` 分岐による `anyOf` は受け付けられますが、強制はされません。フィールドをまたぐ規則は、自分で確認してください。
- **再試行**：`maxRetries` は 0〜3 で、デフォルトは 2 です。無効な回答を、検証エラーとともにモデルへ差し戻す回数です。
- **サンプリング**：`maxTokens` はすべての呼び出しに適用され、モデル自体の上限を超えることはできません。`temperature` は、`advancedMode: true` も一緒に送信しない限り無視されます。この場合、クレジットのティアが 1 段階上がります。
- **所要時間**：この呼び出しは同期的で、数分かかることがあります。各試行は、2 つの経路のそれぞれで最大 240 秒かかることがあるため、最悪の場合、デフォルトの `maxRetries` では 24 分、最大値では 32 分かかります。HTTP クライアントのタイムアウトを延ばすか、下記のジョブの形式を使ってください。SDK のデフォルトの `timeoutMs` である 60 秒では短すぎます。
- **エラー**：`400 validation_error`、`401`、`402`、`500 internal_error`、再試行を使い切った後の `502 llm_error`、`503 provider_unavailable` です。

SDK では、`client.llm.structured(body)` を呼び出します。

### ジョブとして実行する
`POST /v1/llm/structured/jobs` は、同じボディを受け取り、すぐに `{ jobId }` を返します。`GET /v1/jobs/:id/status` をポーリングしてください。`completed` になると、`output_data` は `{ output, inputTokens, outputTokens }` になり、`failed` になると、`error_message` に理由が示されます。作成したドラフトは、`GET /v1/jobs?type=llm-structured&origin=<your app>` でもう一度見つけられます。3 つの追加フィールドを受け付けます。

- `label`：ジョブの表示名で、最大 120 文字です。
- `videoUrl`：動画からドラフトを作ります。動画はまず、自分が所有する別のジョブとして、動画分析の料金で分析され、その分析結果が `input` に追加されます。実行中は、`output_data.stage` が `analyzing`、続いて `drafting` になります。
- `videoAnalysis`：その分析のための `{ llmModel?, selectionMode? }` です。

`POST /v1/jobs/:id/cancel` でドラフトをキャンセルすると、実行中の分析もキャンセルされます。分析が拒否されると、ドラフトのために確保されていたクレジットはすべて返還されます。言語モデルの呼び出しを nodaro.ai に送るインストール環境では、ジョブの形式は `503 provider_unavailable` を返します。その場合は、同期呼び出しを使ってください。SDK では、`client.llm.structuredJob(body)` を呼び出します。

## Frequently asked questions

### Nodaro API で画像を生成するにはどうすればよいですか？

prompt と provider を指定して POST /v1/generate-image を送信します。レスポンスには jobId が含まれます。ステータスが completed になるまで GET /v1/jobs/:id/status をポーリングし、output_data.imageUrl を読み取ります。SDK では、client.nodes.runAndWait がこの両方のステップを行います。

### API から実行できるノードはどれですか？

GET /v1/nodes が一覧表示する、すべてのノードタイプです。ルートは POST /v1/ の後にノードタイプを付けたもので、ノードの設定を JSON のボディとして送ります。一部のテキストノードは、/v1/llm-chat/generate のような、より長いパスを使います。

### direction オブジェクトとは何ですか？

画像や動画の生成と一緒に送る、ピッカー ID をまとめたフラットなマップです。たとえば { "shotSize": "wide-shot", "timeOfDay": "golden-hour" } です。Nodaro は、各 ID に対応する検証済みの言い回しをプロンプトに書き込みます。有効な ID は GET /v1/picker-catalogs から取得します。

### API でキャラクターの一貫性を保つにはどうすればよいですか？

キャラクターの画像を、source に wired-character、defaultName にキャラクターの名前を指定して、connectedReferences で送信します。ルートは画像を添付し、エディターと同じように、アイデンティティの指示をプロンプトに書き込みます。

### 検出用のエンドポイントにトークンは必要ですか？

いいえ。GET /v1/nodes、GET /v1/models、GET /v1/picker-catalogs、GET /v1/catalogs は公開されており、5 分間キャッシュされます。公開されるのはカタログのデータだけで、アカウントに関する情報は含まれません。

### プロンプトと direction の ID を合わせた長さが、モデルの上限を超えるとどうなりますか？

Nodaro は、まず direction の一節を、末尾から 1 つずつ、プロンプトが収まるまで取り除きます。subject の一節はそれよりも後に取り除かれ、あなた自身の文章とリファレンスは、それらのために取り除かれることはありません。
