# ワークフロー

> API から保存済みの Nodaro のワークフローを実行し、新しい入力値を渡して、結果を待つかポーリングします。ワークフローの一覧表示、作成、更新、エクスポート、移動も行えます。

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

**ワークフロー**は、接続されたノードを保存した 1 つのキャンバスです。API はワークフローを実行し、1 回の実行だけ新しい入力値を渡し、ほかのリソースと同じように管理できます。実行は 1 回のリクエストで開始し、`executionId` が返されます。実行が終わるまでそれをポーリングします。短い実行では、同じリクエストの中で結果を待つこともできます。

## エンドポイント
### ワークフローを実行する
| メソッド | パス | 説明 |
| --- | --- | --- |
| `POST` | `/v1/workflows/:id/run` | 保存済みのワークフロー、またはその一部のノードを実行します。`executionId` とともに `202` を返します。 |
| `GET` | `/v1/api/workflows` | その API トークンで実行できるワークフローを一覧表示します。`?limit=` と `?cursor=` に対応します。 |
| `GET` | `/v1/api/schema?workflowId=…` | ワークフローの入力フィールドと出力を、`estimatedCredits` とともに返します。 |
| `POST` | `/v1/api/run` | 新しい入力値でワークフローを実行します。結果を待つには `?wait=true&timeout=…` を追加します。 |
| `GET` | `/v1/api/status/:execId` | 実行のステータス、ノードの数、使用したクレジットです。 |
| `GET` | `/v1/api/result/:execId` | 実行のステータスが `completed` または `failed` になった後の出力です。 |
| `POST` | `/v1/app/:slug/run` | 公開したアプリを、そのフォームのフィールドで実行します。[ミニアプリ](https://nodaro.ai/docs/developers/embed/miniapps)を参照してください。 |

### ワークフローを管理する
| メソッド | パス | 説明 |
| --- | --- | --- |
| `GET` | `/v1/projects/:projectId/workflows` | 1 つのプロジェクトのワークフローを、ノードとエッジを含まない形で返します。 |
| `GET` | `/v1/workflows` | すべてのプロジェクトにわたる、自分のワークフローです。 |
| `GET` | `/v1/workflows/:id` | 1 つのワークフローを、ノード、エッジ、設定とともに返します。 |
| `POST` | `/v1/projects/:projectId/workflows` | プロジェクト内にワークフローを作成します。 |
| `PATCH` | `/v1/workflows/:id` | ワークフローのフィールドを、任意の組み合わせで変更します。 |
| `DELETE` | `/v1/workflows/:id` | ワークフローを削除します。 |
| `GET` | `/v1/workflows/:id/export` | ワークフローを JSON バンドルとしてエクスポートします。キャラクター、オブジェクト、ロケーションを含めるには `?assets=true` を追加します。 |
| `POST` | `/v1/workflows/import` | バンドルからワークフローを作成します。 |
| `POST` | `/v1/workflows/:id/move` | ワークフローを別のプロジェクトに移動します。 |
| `GET` | `/v1/workflows/shared-with-me` | ほかの人が自分と共有したワークフローです。 |

共有、共同編集者、ワークフローごとの権限については、[ワークスペースと組織](https://nodaro.ai/docs/developers/api/workspaces)を参照してください。

## ワークフローを実行する 3 つの方法
| エンドポイント | 認証情報 | 入力値 | 使うタイミング |
| --- | --- | --- | --- |
| `POST /v1/workflows/:id/run` | どのトークンでも使えます。OAuth トークンには `workflows:execute` が必要です。 | 保存済みの値です。`nodeIds` で一部だけを実行します。 | 自分のアカウント、または OAuth ユーザーのために、保存されたとおりにワークフローを実行する場合です。 |
| `POST /v1/api/run` | 個人用 API トークン | `inputs` が、この実行の入力ノードの値を置き換えます。 | スクリプトが実行ごとに異なる値を必要とする場合、または結果を待ちたい場合です。 |
| `POST /v1/app/:slug/run` | どのトークンでも使えます | アプリのフォームフィールドと、ノードの生の上書き値です。 | ワークフローが、整えられたフォームを持つアプリとして公開されている場合です。 |

5 つの `/v1/api/` エンドポイントは、もともとの API トークン用の経路です。公開アプリより前からあり、引き続きサポートされていますが、入力を必要とする新しい連携では、通常はワークフローをアプリとして公開し、`POST /v1/app/:slug/run` を使います。このルートは、アプリのフィールドを、フラットな `inputs` と、任意の `inputOverrides` オブジェクト（生の `{ nodeId: { field: value } }` という上書き値）として受け取ります。この 2 つはマージされ、両方が同じフィールドを設定している場合は `inputOverrides` が優先されます。

## 保存したワークフローを実行する
`POST /v1/workflows/:id/run` は、保存されたとおりにワークフローの実行を開始します。すべてのノードを実行するには空のボディを送信し、一部だけを実行するには `nodeIds` を送信します。

**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/workflows/8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f/run \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json
{ "executionId": "3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b", "status": "pending" }
```

**TypeScript SDK**

```ts
const { executionId } = await client.workflows.run(workflowId)

// Or run only some nodes:
await client.workflows.run(workflowId, { nodeIds: ['text-prompt-1', 'generate-image-1'] })
```

**CLI**

```bash
nodaro workflows run 8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f --watch
nodaro workflows run 8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f --node text-prompt-1 generate-image-1
```

<TypeTable
type={{
nodeIds: { type: 'string[]', description: '任意。実行するノードの ID です。省略すると、ワークフロー全体を実行します。' },
}}
/>

応答は `{ executionId, status }` を伴う `202 Accepted` で、`status` は `pending` または `running` です。実行は `GET /v1/workflow-executions/:id` でポーリングします。詳しくは[実行](https://nodaro.ai/docs/developers/api/executions)を参照してください。`--watch` を付けると、CLI が代わりにポーリングし、実行が失敗するとコード `2`、キャンセルされるとコード `130` で終了します。

| ステータス | コード | 意味 |
| --- | --- | --- |
| 402 | `insufficient_credits` | 自分のクレジットでは、実行の最大コストをまかなえません。 |
| 403 | `forbidden` | ワークフローは見えますが、実行する権限がありません。ワークスペースでは、実行に編集権限とアクティブなメンバーシップが必要です。 |
| 404 | `not_found` | そのワークフローが存在しないか、自分には見えません。 |
| 409 | `already_running` | ワークフローには、すでにアクティブな実行があります。レスポンスには、その実行の `executionId` が含まれます。 |

## 新しい入力値でワークフローを実行する
`POST /v1/api/run` は、この 1 回の実行に限り入力ノードの値を置き換える `inputs` オブジェクトを受け取ります。認証には個人用 API トークンを使います。`inputs` のキーはノード ID で、便宜上、一意なノードラベルも使えます。各キーの中には、テキストプロンプトの `text` のように、ノードの入力フィールドを指定します。

Workflow: サンプルのワークフロー：API の実行が置き換えるテキストを持つテキストノードが、画像生成ノードに渡されます。

- テキスト → 画像生成 (プロンプト)

下の例では、[**テキスト**（Text）](https://nodaro.ai/docs/nodes/automate/text)ノード（ID は `text-prompt-1`）を、[**画像生成**（Generate Image）](https://nodaro.ai/docs/nodes/image/generate-image)ノードに接続したワークフローを実行します。

### 入力フィールドを見つける
`GET /v1/api/schema` は、ワークフローの入力を、各入力が受け取るフィールド、出力、実行にかかるクレジットの見積もりとともに一覧表示します。

```bash
curl -s "https://app.nodaro.ai/v1/api/schema?workflowId=$WORKFLOW_ID" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{
"workflowId": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f",
"name": "Sunset stills",
"estimatedCredits": 45,
"inputs": [
{ "nodeId": "text-prompt-1", "key": "text", "label": "Prompt", "type": "text" }
],
"outputs": [
{ "nodeId": "generate-image-1", "label": "Generate Image", "type": "image" }
]
}
```

### 実行を開始する
```bash
curl -s -X POST https://app.nodaro.ai/v1/api/run \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
"workflowId": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f",
"inputs": {
"text-prompt-1": { "text": "a cat at sunset" }
}
}'
```

```json
{ "executionId": "3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b", "status": "pending" }
```

### 実行が終わるまでポーリングする
`GET /v1/api/status/:execId` は、ステータス、ノードの数、それまでに使用したクレジットを返します。ステータスが `completed`、`failed`、`cancelled`、`timed_out`、`discarded` のいずれかになるまで、2〜5 秒ごとにポーリングします。

### 結果を取得する
`GET /v1/api/result/:execId` は、`completed` または `failed` で終わった実行の出力を返します。`cancelled`、`timed_out`、`discarded` のいずれかで終わった実行では、代わりにステータスのレスポンスから `errorMessage` を読み取ってください。これらの実行には結果のペイロードがなく、結果のルートは `202` を返し続けます。

```json
{
"executionId": "3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "completed",
"creditsUsed": 45,
"durationMs": 12450,
"errorMessage": null,
"outputs": [
{
"nodeId": "generate-image-1",
"label": "Generate Image",
"type": "image",
"url": "https://…/output.png"
}
]
}
```

この一連の流れを 1 つのスクリプトにしたものと、同じ呼び出しを TypeScript で書いたものです。

**curl**

```bash
BASE="https://app.nodaro.ai"
WORKFLOW_ID="8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f"

EXEC=$(curl -s -X POST "$BASE/v1/api/run" \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"workflowId\": \"$WORKFLOW_ID\", \"inputs\": {\"text-prompt-1\": {\"text\": \"a cat at sunset\"}}}" \
| jq -r .executionId)

while true; do
STATUS=$(curl -s -H "Authorization: Bearer $NODARO_API_KEY" \
"$BASE/v1/api/status/$EXEC" | jq -r .status)
echo "Status: $STATUS"
case "$STATUS" in completed|failed|cancelled|timed_out|discarded) break;; esac
sleep 5
done

curl -s -H "Authorization: Bearer $NODARO_API_KEY" "$BASE/v1/api/result/$EXEC" | jq .
```

**TypeScript SDK**

```ts
// The /v1/api/ lane has no dedicated SDK method: call it with client.request.
const schema = await client.request('GET', '/v1/api/schema', {
query: { workflowId },
})

const { executionId } = await client.request<{ executionId: string }>('POST', '/v1/api/run', {
body: { workflowId, inputs: { 'text-prompt-1': { text: 'a cat at sunset' } } },
})

const final = ['completed', 'failed', 'cancelled', 'timed_out', 'discarded']
let status = 'pending'
while (!final.includes(status)) {
await new Promise((r) => setTimeout(r, 3_000))
;({ status } = await client.request<{ status: string }>('GET', `/v1/api/status/${executionId}`))
}

const result = await client.request('GET', `/v1/api/result/${executionId}`)
```

CLI は、保存された値のままでワークフローを実行します。ターミナルから値を渡すには、ワークフローをアプリとして公開し、`nodaro apps run <slug> --input prompt="…"` を実行します。

<TypeTable
type={{
workflowId: { type: 'string (uuid)', description: '実行するワークフローです。', required: true },
inputs: { type: 'object', description: '任意。入力ノードの新しい値で、キーはノード ID か一意なノードラベルです。各値は、そのノードのフィールドをまとめたオブジェクトです。たとえば { text: "…" } です。' },
}}
/>

ワークフローのスコープを持つトークンは、そのスコープ内のワークフローだけを実行できます。それ以外のワークフローには `403 forbidden` が返されます。`POST /v1/api/run` と `GET /v1/api/workflows` は、トークンの 1 分あたりの制限にカウントされます。ステータス、結果、スキーマの読み取りはカウントされません。[レート制限](https://nodaro.ai/docs/developers/api/rate-limits)を参照してください。

## 同期か非同期か
`POST /v1/api/run` は、デフォルトでは非同期です。すぐに `{ executionId, status: "pending" }` を伴う `202 Accepted` を返し、その後ポーリングします。

短いワークフローでは、終わるまで接続を保持できます。

```http
POST /v1/api/run?wait=true&timeout=120
```

- サーバーは、最大 `timeout` 秒まで、5 秒ごとに実行を確認します。デフォルトは 120、最大は 600 です。
- 実行がその時間内に終わった場合、レスポンスは `GET /v1/api/result/:execId` と同じペイロードです。`status` は `completed`、`failed`、`cancelled`、`timed_out`、`discarded` のいずれかです。
- 終わらなかった場合、レスポンスは `{ executionId, status: "pending" }` を伴う `202` になるので、そこからポーリングします。

1 分未満で終わると見込める実行、たとえばテキスト生成や軽い画像処理には、同期の形式を使います。動画のレンダリングやアップスケールを行うワークフローには、非同期の形式を使います。SDK では、クライアントがデフォルトで 60 秒後にあきらめるため、`createClient` の `timeoutMs` を、自分の `timeout` より大きい値に設定してください。

`POST /v1/workflows/:id/run` と生成系のルートは、常に非同期です。単体のノードについては、SDK の `nodes.runAndWait` が代わりにジョブをポーリングします。[単体のノードを実行する](https://nodaro.ai/docs/developers/api/nodes)を参照してください。

## 実行で変更できないもの
実行のリクエストで、ワークフローの送信系のノードの宛先を変更することはできません。ワークフローがどこに送信し、どこから取得するかは、`POST /v1/workflows/:id/run`、`POST /v1/api/run`、`POST /v1/app/:slug/run` を含むすべての実行エンドポイントで、ワークフロー自身が決めます。

送信系のノードとは、[**Webhook 出力**（Webhook Output）](https://nodaro.ai/docs/nodes/publish/webhook-output)と、[**SNS に公開**（Publish to Social）](https://nodaro.ai/docs/nodes/publish/publish-to-social)のような SNS への公開ノード、そして取得系のノードです。取得系のノードには、[**Web スクレイピング**（Web Scrape）](https://nodaro.ai/docs/nodes/automate/web-scrape)、[**RSS フィード**（RSS Feed）](https://nodaro.ai/docs/nodes/automate/rss-feed)、[**Telegram チャンネルフィード**（Telegram Channel Feed）](https://nodaro.ai/docs/nodes/automate/telegram-channel-feed)、[**動画 URL**（Video URL）](https://nodaro.ai/docs/nodes/automate/video-url)があります。これらのノードでは、上書きで次のものを変更できません。

- 名前が `Url` または `Urls` で終わるフィールド。
- `target`、`targets`、`query`、`channel`、`chatId`、`connectionId`、`credentialId`、`platform`、`webhook`、`endpoint`、`host`、`privacy`。
- 取得系のノードがどの宛先フィールドを読み取るかを決める、`actor` と `mode` のセレクターです。

このルールは、オブジェクトや `fieldMappings` のエントリーの中にネストした値にも及び、新しいアドレスだけでなく空の値にも適用されます。宛先を空にすると、そのノードは代わりに上流のテキストを読み取ってしまうためです。このようなリクエストは、何も実行される前に `400 locked_field` を返します。エラーには、問題のあるフィールドが最大 10 個まで列挙され、残りは件数で示されます。また、そのようなノードで 32 階層を超えてネストした上書きは、無条件に拒否されます。

これらのノードにあるキャプションや上限といった通常のフィールド、そしてアップロードやリファレンス音声のような入力ノードのメディアの `url` フィールドは、引き続き上書きできます。

## パラメーターの補正
画像と動画の生成ルートは、モデルに関わらず、`aspectRatio`、`resolution`、`quality` について 1 つの語彙を受け付けます。すべてに対応しているモデルはありません。選んだモデルが対応していない値を拒否する代わりに、サーバーはそれをモデルが対応する値に**補正**し、何を変更したかを伝えます。ワークフローの途中で拒否すると、すでに生成が済んでいて課金されている、隣のノードまですべて失敗してしまいます。

値を補正するルートは、`POST /v1/generate-image`、`/v1/image-to-image`、`/v1/edit-image`、`/v1/text-to-video`、`/v1/generate-video` です。

```json
{
"jobId": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10",
"adjustments": [
{
"field": "aspectRatio",
"from": "3:2",
"to": "auto",
"reason": "GPT Image 2 does not support aspectRatio \"3:2\" — using \"auto\" instead. Supported: auto, 1:1, 16:9, 9:16, 4:3, 3:4."
},
{
"field": "resolution",
"from": "4K",
"to": "1K",
"reason": "GPT Image 2 only renders 1K at the \"auto\" aspect ratio."
}
]
}
```

- **`adjustments` は、何も変更がなければ存在しません。**有効なリクエストのレスポンスは、`{ "jobId": "…" }` だけになります。
- **`to` は、モデルにその設定がない場合は存在せず**、値は取り除かれます。たとえば、アップスケーラーに送信した `aspectRatio` です。
- **クレジットは、補正後の値に従います。**[GPT Image 2](https://nodaro.ai/docs/models/image/gpt-image-2) に `2K` の `auto` をリクエストすると、`auto` は 1K でしかレンダリングしないため、1K でレンダリングされ、1K の料金が課金されます。
- **保存済みのワークフローも補正されます。**API または MCP を通じて保存したワークフローは、書き込み時に同じ補正を受けます。
- **各モデルが対応する値**は、`GET /v1/models` にあります。[モデルを調べる](https://nodaro.ai/docs/developers/api/nodes#discover-models)を参照してください。

### 動画のルートでの補正
`/v1/text-to-video` と `/v1/generate-video` は、同じ形式で `adjustments` を返します。`/v1/generate-video` は、各補正の `reason` を、`voice_unsupported_for_provider` のようなパラメーター以外の警告と並べて、`warnings` の配列にも繰り返します。どの設定が変わったかを知りたいときは `adjustments` を読んでください。この 2 つが食い違うことはありません。

- **対応していない値は、最も近い選択肢に切り上げ・切り下げされます。**最も安い値や最初の値になることはありません。1080p が上限のモデルに `4k` をリクエストすると、480p ではなく 1080p でレンダリングされ、縦長の `9:21` は、`16:9` ではなく `9:16` になります。
- **`resolution` を省略すると、その料金がかかる段階で送信されます。**プラットフォームがモデルのデフォルトの段階を定めている場合、その段階が課金され、モデルにもその段階がリクエストされます。この値はジョブの `input_data` に表示されるため、何が送信されたかを必ず確認できます。
- **料金の計算前に、表記が正規化されます。**`4K` は `4k` として読み取られるため、4K の段階で課金され、レンダリングされます。
- **一部のモデルは、それ以外の値に対して固定の段階でレンダリングします。**[MiniMax Hailuo 3](https://nodaro.ai/docs/models/video/minimax-h3) は、`768P` 以外のすべての値に対して 2K でレンダリングし、[Wan 3.0](https://nodaro.ai/docs/models/video/wan-3-0) ファミリーは 720p でレンダリングします。これらのモデルでは、モデルが実際に出力する段階に合わせて補正されるため、料金がレンダリング結果と一致します。モデルの `GET /v1/models` のエントリーでは、これが `unlistedResolutionRendersAs` に示されます。
- **`duration` は、指定したとおりに送信されます。**ただし、[LTX 2.3](https://nodaro.ai/docs/models/video/ltx-2-3-pro) のモデルは例外です。これらのモデルは、解像度ごとに決まった長さの段階で課金されるため、2 つの段階の間の長さは最も近い段階に移動し、`adjustments` に報告されます。
- **`duration: -1` は、対応するモデルでは「自動」（Auto）を意味します。**[Seedance 2](https://nodaro.ai/docs/models/video/seedance-2) ファミリー（`GET /v1/models` で `autoDuration: true`）が対応しています。モデルがクリップの長さを選びます。リファレンス動画を編集する場合は、その元の動画の長さになります。「自動」での実行では、モデルの最長のクリップ分のクレジットが確保され、実際に生成された長さを超える分が返還されます。ほかのモデルは `-1` を無視し、デフォルトの長さでレンダリングします。

キャラクターとロケーションの画像ルート（`/v1/generate-character`、`/v1/generate-character-asset`、`/v1/generate-location`、`/v1/generate-location-asset`）も、同じ方法で `quality` と `resolution` を補正しますが、`adjustments` は返しません。補正後の値は、`GET /v1/jobs/:id` から取得したジョブの `input_data` でのみ確認できます。

## ワークフローを管理する
### 一覧表示と読み取り
`GET /v1/projects/:projectId/workflows` は、プロジェクトのワークフローを、`nodes`、`edges`、`settings` を含まない形で返します。`GET /v1/workflows/:id` は、1 つのワークフローを完全な形で返します。組織では、一覧は自分が操作しているワークスペースに従います。[ワークスペース](https://nodaro.ai/docs/developers/api/workspaces)を参照してください。

**curl**

```bash
curl -s https://app.nodaro.ai/v1/projects/$PROJECT_ID/workflows \
  -H "Authorization: Bearer $NODARO_API_KEY" | jq '.data[] | {id, name}'
```

**TypeScript SDK**

```ts
const { data: workflows } = await client.workflows.list({ projectId })
const { data: workflow } = await client.workflows.get(workflows[0].id)
console.log(workflow.nodes.length)
```

**CLI**

```bash
nodaro workflows list --project $PROJECT_ID --json
nodaro workflows get 8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f
```

### 作成と更新
`POST /v1/projects/:projectId/workflows` で、プロジェクト内にワークフローを作成します。プロジェクト以外はすべて任意で、指定しない場合はサーバーのデフォルト値が使われます。

```ts
const { data: wf } = await client.workflows.create({
projectId,
name: 'My workflow',
nodes: [],
edges: [],
})
```

`PATCH /v1/workflows/:id` は、フィールドを任意の組み合わせで変更します。同時編集に対して安全な更新にするには、直前の読み取りで得た整数の `version` を、`expectedVersion` として送信します。それ以降にワークフローが変更されていた場合、更新は `409 workflow_conflict` で拒否され、エラーには `currentVersion`、`currentUpdatedAt`、そして現在のワークフロー全体である `currentRecord` が含まれます。追加の読み取りをせずに、変更を `currentRecord` にマージして、もう一度保存してください。タイムスタンプである `expectedUpdatedAt` も、同じように機能します。

```ts

try {
await client.workflows.update(id, { name: 'Renamed', expectedVersion: 7 })
} catch (err) {
if (err instanceof WorkflowConflictError && err.currentRecord) {
await client.workflows.update(id, { name: 'Renamed', expectedVersion: err.currentVersion })
} else throw err
}
```

- ノードの `data` にある実行状態の値、たとえば実行ステータス、現在のジョブ、進行状況は、保存時に取り除かれ、保存されません。
- `thumbnailUrl` は、すでにホストされている画像から、ワークフローのプレビュー画像を設定します。`null` を指定すると解除されます。
- `edges` だけを送信して[**動画オーバーレイ**（Video Overlay）](https://nodaro.ai/docs/nodes/video/video-overlay)のレイヤーを接続する保存は、そのノードを書き換えるため、`expectedVersion` がなくても、読み取り後にワークフローが変更されていた場合は `409 workflow_conflict` を返します。
- `visibility` は `private` または `workspace` です。変更できるのは作成者かワークスペースの管理者だけで、ワークスペースの外にあるワークフローには `400 not_workspace_scoped` が返されます。

### 削除
`DELETE /v1/workflows/:id` は、ワークフローを削除します。削除できるのは作成者とワークスペースの管理者だけで、共同編集者にはできません。存在しないワークフローや、自分には見えないワークフローの削除は `404` を返すため、削除が黙って成功することはありません。ワークフローが見えても削除できない共同編集者には、`403` が返されます。

### エクスポートとインポート
`GET /v1/workflows/:id/export` は、ワークフローを持ち運び可能な JSON バンドルとして返します。`?assets=true` を付けると、そのワークフローが使うキャラクター、オブジェクト、ロケーションも、自分のものであればバンドルに含まれます。`localhost` やプライベートネットワーク上のファイルのように、ノードがほかのインストール環境から取得できないメディアを指している場合、バンドルはそれらを `portability.unreachableMedia` に一覧表示します。

`POST /v1/workflows/import` は、バンドルからワークフローを作成します。

```http
POST /v1/workflows/import
{ "projectId": "<project uuid>", "workflow_json": { "version": 1, "name": "…", "nodes": [], "edges": [] } }
```

インポートは、バンドルされたキャラクター、オブジェクト、クリーチャー、ロケーションを自分のアカウントの下に再作成し、ノードをそれらに向け直します。取得できるメディアは、このインストール環境のストレージにコピーされます。ワークフローのメディア用に最大 25 ファイル、バンドルされたエンティティ用にさらに最大 25 ファイルで、画像は最大 20 MB、動画や音声は最大 50 MB です。レスポンスには、新しいワークフローと `importReport` が含まれます。

| フィールド | 意味 |
| --- | --- |
| `rehosted` | このインストール環境にコピーされたファイルの数です。 |
| `unreachable` | プライベートホスト上のメディアで、元の場所を指したままです。これらのノードは、ファイルが再度アップロードされるまで実行されません。 |
| `skipped` | コピーできなかったメディアで、`HTTP 404` のような理由が付きます。 |
| `assetIdMap` | バンドルされた各エンティティの ID を、そのために作成された行に対応付けたものです。 |
| `assetsSkipped` | ストレージの容量が足りず、作成されなかったエンティティです。それでもワークフロー自体は作成されます。 |

CLI からは、`nodaro workflows export <id> --with-assets --output bundle.json` を実行し、続けて `nodaro workflows import bundle.json --project <projectId>` を実行します。何が引き継がれるかについては、[インポートとエクスポート](https://nodaro.ai/docs/guides/import-export)を参照してください。

### 別のプロジェクトへの移動
```http
POST /v1/workflows/:id/move
{ "projectId": "…" }
```

移動はワークフローへの書き込みなので、OAuth トークンには `workflows:write` が必要です。`projectId` を指定した `PATCH /v1/workflows/:id` も同じ動作をし、同じルールに従います。移動できるのは、自分が作成した作業です。組織の中では、ワークスペースの管理者が、自分の管理する 2 つのワークスペースの間で作業を移動することもできます。個人のプロジェクトは、移動元と移動先の両方が自分のものである必要があります。

| ステータス | コード | 意味 |
| --- | --- | --- |
| 400 | `validation_error` | ワークフローは、すでにそのプロジェクトにあります。 |
| 403 | `not_permitted` | そのワークフローを移動する権限がないか、その移動先に移動する権限がありません。 |
| 404 | `not_found` | そのワークフローが存在しないか、自分にはそのプロジェクトが存在しません。 |
| 409 | `move_blocked` | その作業は、課題のために作成されたものです。 |
| 409 | `workspace_archived` | 移動先のワークスペースがアーカイブされています。 |

ワークスペースが変わる移動では、ワークフローの共同編集者への権限が取り消され、レスポンスに報告されます。これにより、アクセスを失った人に伝えられます。

```json
{
"data": { "id": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f", "projectId": "5e4d3c2b-1a09-4f8e-8d7c-6b5a49382716" },
"droppedCollaborators": [{ "userId": "2b3c4d5e-6f70-4a81-92b3-c4d5e6f70812", "name": "Sam" }]
}
```

`PATCH` の形式では、`droppedCollaborators` は、権限が取り消された場合にのみ含まれます。

## ワークフローの OAuth スコープ
| スコープ | ルート |
| --- | --- |
| `workflows:read` | `GET /v1/projects/:projectId/workflows`, `GET /v1/workflows`, `GET /v1/workflows/:id`, `GET /v1/workflows/:id/export` |
| `workflows:write` | 作成、更新、削除、インポート、移動、および `POST /v1/workflows/:parentId/sub-workflows` |
| `workflows:execute` | `POST /v1/workflows/:id/run` |

個人用 API トークンには、スコープは必要ありません。[OAuth アプリ](https://nodaro.ai/docs/developers/oauth)を参照してください。

## スタジオプロダクションのノード
キャンバスの一部のノードは、スタジオプロダクションに属し、リンクされたフレームに依存します。これらは、スタジオプロダクション API を通じて生成する必要があります。`POST /v1/workflows/:id/run` で実行したり、直接生成したりすると、`400 sequence_execution_required` が返されます。[スタジオプロダクション](https://nodaro.ai/docs/developers/api/studio-productions)を参照してください。

## Frequently asked questions

### API から Nodaro のワークフローを実行するにはどうすればよいですか？

POST /v1/workflows/:id/run を送信します。保存済みのワークフローが実行され、ポーリングする executionId とともに 202 が返ります。1 回の実行だけ入力値を変更するには、inputs オブジェクトと個人用 API トークンを使って POST /v1/api/run を呼び出します。

### ワークフローの実行に入力値を渡すにはどうすればよいですか？

POST /v1/api/run に inputs オブジェクトを送信します。キーはノード ID か一意なノードラベルで、その中にノードのフィールド（たとえば text）を指定します。GET /v1/api/schema は、ワークフローの入力フィールドを一覧表示します。

### 1 回のリクエストで、ワークフローの結果を待てますか？

はい。POST /v1/api/run に ?wait=true&timeout=120 を追加します。サーバーは接続を最大 600 秒保持して出力を返します。実行がまだ続いている場合は、executionId とともに 202 を返すので、そこからポーリングしてください。

### API が、送信したアスペクト比や解像度を変更したのはなぜですか？

選んだモデルがその値に対応していないため、実行を失敗させる代わりに、Nodaro がモデルの対応する最も近い値に補正したためです。レスポンスの adjustments に、すべての変更が一覧表示されます。クレジットは、補正後の値に基づいて課金されます。

### API での実行で、ワークフローが結果を送る先を変更できますか？

いいえ。Webhook 出力の URL や、SNS への公開先のアカウントなど、送信系のノードの宛先フィールドは、実行によって上書きできません。それを試みたリクエストには 400 locked_field が返されます。
