# ジョブ

> Nodaro のジョブをポーリングしてステータスと結果を取得し、失敗のヒントとクレジットの状態を確認します。100 件の一括確認、キャンセル、動画生成 Pro の停止と継続も扱います。

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

**ジョブ**は、Nodaro での生成の 1 単位です。1 枚の画像、1 本の動画のレンダリング、1 つの音声クリップが、それぞれ 1 つのジョブになります。ノードを実行するとジョブ ID が返り、ワークフローを実行すると AI ノードごとに 1 つのジョブが作成されます。ジョブのエンドポイントは、各ジョブのステータス、進行状況、結果、クレジットを返します。ジョブが `completed`、`failed`、`cancelled` のいずれかになるまでポーリングし、結果を `output_data` から読み取ります。

## エンドポイント
| メソッド | パス | 説明 |
| --- | --- | --- |
| `GET` | `/v1/jobs/:id/status` | ポーリング用の軽量なステータスです。ステータス、進行状況、結果、エラーを返します。 |
| `GET` | `/v1/jobs/:id` | 送信した内容（`input_data`）とクレジットを含む、ジョブ全体を返します。 |
| `GET` | `/v1/jobs` | 自分のジョブを、新しい順にページ単位で返します。 |
| `GET` | `/v1/jobs/status?ids=…` | 最大 100 件のジョブのステータスを返します。ID はクエリで指定します。 |
| `POST` | `/v1/jobs/batch-status` | 最大 100 件のジョブのステータスを返します。ID はリクエストボディで指定します。 |
| `POST` | `/v1/jobs/:id/cancel` | ジョブをキャンセルし、確保されたクレジットを解放します。 |
| `DELETE` | `/v1/jobs/:id` | ジョブと、そのジョブが生成した非公開のメディアを削除します。 |
| `GET` | `/v1/component/execute/:jobId/wait-limit` | コンポーネントの実行を、サーバーがどれだけ待つかを返します。 |
| `POST` | `/v1/generate-video-pro/:jobId/stop` | **動画生成 Pro**（Generate Video Pro）の実行を停止し、完成したセグメントを残します。 |
| `POST` | `/v1/generate-video-pro/continue` | 動画生成 Pro の実行を、新しいジョブとして続けます。 |
| `POST` | `/v1/credits/video-pro-estimate` | 動画生成 Pro の実行を開始せずに、料金を見積もります。 |

OAuth トークンでジョブを読み取るには、`jobs:read` スコープが必要です。個人用 API トークンには、スコープは必要ありません。

## ジョブの読み取り
**curl**

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

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

**TypeScript SDK**

```ts
const { data } = await client.jobs.getStatus(jobId)
if (data.status === 'completed') console.log(data.output_data)

// The full record, with input_data and credits:
const { data: job } = await client.jobs.get(jobId)
```

**CLI**

```bash
nodaro jobs get 0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10 --json
```

`GET /v1/jobs/:id/status` は、ポーリングのループ向けに作られています。`input_data` と料金のフィールドを省くため、`GET /v1/jobs/:id` より軽量です。どちらも `{ "data": … }` を返し、存在しないジョブや自分のものではないジョブには `404` を返します。ジョブのフィールドは、サーバーが送るとおりのスネークケースです。

| フィールド | 意味 |
| --- | --- |
| `id` | ジョブ ID です。 |
| `status` | ジョブの状態です。[ジョブのステータス](#job-statuses)を参照してください。 |
| `progress` | 0〜100 です。 |
| `output_data` | 結果です。メディアのジョブには `imageUrl`、`videoUrl`、`audioUrl` のいずれかが入り、多くの場合は `thumbnailUrl` も付きます。その他のジョブには、`text` や `json` など、独自のフィールドが入ります。 |
| `error_message` | ジョブが失敗した理由を、文章で示します。 |
| `error_hint` | 2 種類の失敗について、構造化された理由を示します。[ジョブが失敗した理由](#why-a-job-failed)を参照してください。 |
| `credit_status` | `reserved`、`committed`、`refunded`、`null` のいずれかです。[ジョブのクレジット](#credits-of-a-job)を参照してください。 |
| `recovering` | モデルが結果を返した後にワーカーが停止したジョブを、プラットフォームが修復している間は `true` です。 |
| `input_data` | ジョブ全体を取得した場合のみ。修正後の、実際に送られた内容です。たとえば、最終的な `prompt`、自分で書いた `userPrompt`、`direction` の ID などです。 |
| `credits` | ジョブ全体を取得した場合のみ。ジョブのクレジットです。 |
| `job_type`、`source`、`source_detail` | ジョブ全体を取得した場合のみ。ジョブの種類と、ジョブの出どころです。`source` は `internal`、`mcp`、`app`、`cli`、`sdk`、`extension`、`web`、`api` のいずれかで、`source_detail` は `sdk/1.10.0` のようにクライアントを示します。 |
| `created_at`、`started_at`、`completed_at` | ジョブ全体を取得した場合のみ。タイムスタンプです。 |

`input_data` と `output_data` は、公開用のビューです。サーバー内部でのみ使うフィールドは、どの呼び出し元に対しても取り除かれます。

## ジョブのステータス
| ステータス | 最終状態 | 意味 |
| --- | --- | --- |
| `pending`、`queued`、`processing` | いいえ | ジョブは実行を待っているか、実行中です。 |
| `pending_review` | いいえ | 結果はできており、デプロイ環境が人によるレビューのために保留しています。ポーリングを続けてください。 |
| `completed` | はい | 結果は `output_data` にあります。 |
| `failed` | はい | 理由は `error_message` と `error_hint` に示されます。 |
| `cancelled` | はい | あなたまたはプラットフォームが、ジョブをキャンセルしました。 |

**修復中のジョブ**。モデルが結果を返した後にワーカーが停止した場合、プラットフォームが修復する間、ジョブは `recovering: true` のまま `processing` にとどまります。その後、ジョブは自動的に完了するか、クレジットが返還されます。遅いモデルでは数十分かかることがあり、SDK のデフォルトの待機時間より長くなります。`runAndWait` からの `JobTimeoutError` はジョブをキャンセルしないので、後でもう一度取得してください。

**レビューのために保留されたジョブ**。レビューのポリシーを登録したデプロイ環境では、ジョブが `pending_review` になることがあります。処理は完了しています。保留の間、クレジットはずっと `reserved` のままで、結果を公開するかどうかは人が判断します。その後、ジョブは `completed`（承認）、`policy-block` のヒント付きの `failed`（却下）、`cancelled` のいずれかで終わります。保留中のジョブは、処理中のほかのジョブと同じようにキャンセルできます。リクエストを再実行しないでください。重複したジョブも保留されるためです。デプロイ環境がレビューの期限を設定している場合、期限内に誰もレビューしなかったジョブは却下されます。ジョブは `failed` で終わり、確保されたクレジットは返還され、保留されていた結果は削除されます。保留中のジョブが自動的に承認されることはありません。SDK の `runAndWait` は、`pending_review` を検出した最初のポーリングで `JobHeldError` をスローし、CLI の `--watch` はコード `3` で終了します。

## ポーリングのポイント
- **2〜5 秒ごとにポーリングします**。ジョブの状態は、ミリ秒単位ではなく秒単位で変わります。
- **ステータスの読み取りは、トークンのレート制限の対象外です**。個人用 API トークンの 1 分あたりの制限にカウントされるのは、ワークフローの実行とワークフローの一覧取得だけです。[レート制限](https://nodaro.ai/docs/developers/api/rate-limits)を参照してください。
- **多くのジョブは、1 回の呼び出しで追跡します**。ジョブごとにリクエストを送るのではなく、[一括取得のエンドポイント](#poll-many-jobs-at-once)を使います。
- **待機はクライアントに任せます**。SDK の `client.nodes.runAndWait` は、最大 15 分間、2 秒ごとにポーリングします。CLI の `--watch` は、ジョブが終わるまでポーリングします。

## ジョブが失敗した理由
[エラー](https://nodaro.ai/docs/developers/api/errors)のエラーの表は、ジョブを作成しなかったリクエストを対象としています。後から失敗したジョブには `error_message` が含まれ、2 種類の失敗では、構造化された `error_hint` も含まれます。

**モデルの安全フィルターがリクエストをブロックした場合**：

```json
{ "kind": "safety-block", "class": "safety", "retried": true, "suggestedProvider": "nano-banana-pro" }
```

- `class` は `copyright`、`likeness`、`safety` のいずれかです。`copyright` または `likeness` によるブロックは確定的で、同じリクエストが通ることはありません。
- 一部のモデルでは、`safety` フィルターの判定が常に一貫しているとは限りません。[GPT Image 2](https://nodaro.ai/docs/models/image/gpt-image-2)、[GPT Image 2.5 Flare](https://nodaro.ai/docs/models/image/gpt-image-2-5-flare)、[GPT Image 2.5 Sunburst](https://nodaro.ai/docs/models/image/gpt-image-2-5-sunburst) では、Nodaro が同じリクエストを、追加料金なしで 1 回だけ再試行します。`retried` は、その再試行がすでに行われたかどうかを示します。
- `suggestedProvider` は、そのモデルに推奨の代替モデルがある場合にだけ含まれます。値は実在するモデル ID なので、同じプロンプトとリファレンスを、そのモデルに送ってください。

**デプロイ環境のポリシーがジョブを拒否した場合**：

```json
{ "kind": "policy-block", "policyId": "brand-safety", "reason": "This image was not approved for release.", "hookPoint": "result" }
```

- `reason` は、デプロイ環境のポリシーがユーザー向けに書いたテキストです。そのまま表示してください。
- `hookPoint` は、ジョブが実行前に拒否された場合は `request`、結果が後から拒否された場合は `result` です。レビュー担当者による却下も、`result` に含まれます。
- ポリシーによる拒否を、プラットフォームが再試行することはありません。ほかのモデルも提案されません。

**それ以外の失敗には `error_hint` がありません**。設定や入力メディアがそのモデルで無効なために、モデルがリクエスト自体を拒否した場合は、`error_message` にそのことが示されます。設定やメディアを変更してから、もう一度実行してください。モデル側のエラーは、文面にかかわらず、再試行する価値があります。`error_hint` は、`error_message` を含むすべてのジョブのペイロードに含まれます。対象は、ジョブ全体、ステータスのルート、一覧、2 つの一括取得のルートです。

## ジョブのクレジット
`credit_status` は、ジョブのクレジットの確保の状態を示します。

| 値 | 意味 |
| --- | --- |
| `reserved` | ジョブの実行中、クレジットが確保されています。 |
| `committed` | ジョブが結果を返し、クレジットが課金されました。 |
| `refunded` | 確保されていたクレジットが解放されました。たとえば、安全フィルターでブロックされた後です。 |
| `null` | ジョブに、報告するクレジットの記録がありません。 |

`credit_status` は、`GET /v1/jobs/:id`、`GET /v1/jobs/:id/status`、`GET /v1/jobs/status` に含まれ、`GET /v1/jobs` と `POST /v1/jobs/batch-status` には含まれません。安全フィルターやポリシーによるブロックで終わった生成は、必ず返還されます。まれな例外は、ポリシーが結果を拒否する前に、クレジットがすでに精算されていたジョブです。[クレジット](https://nodaro.ai/docs/developers/api/credits)を参照してください。

## 自分のジョブの一覧
`GET /v1/jobs` は、自分のジョブを新しい順に、`{ data: Job[], next }` の形式で返します。

| クエリパラメーター | 意味 |
| --- | --- |
| `limit` | ページのサイズで、最大 100 です。 |
| `cursor` | 前のページの `next` の値です。 |
| `type` | ジョブを作成したルートで、完全一致で指定します。たとえば `llm-structured` や `video-analysis` です。 |
| `origin` | ジョブを送ったクライアントアプリで、完全一致で指定します。たとえば `studio` です。 |
| `attachToCharacterId` | 1 人のキャラクターのジョブです。[キャラクター](https://nodaro.ai/docs/developers/api/characters)を参照してください。 |

`type` と `origin` は、組み合わせて使えます。ページに含まれる行は `limit` より少ないことがあり、0 行でも `next` が付くことがあります。行数を数えるのではなく、`next` がある限りページの取得を続けてください。

```ts
const { data: runs, next } = await client.jobs.list({ type: 'llm-structured', origin: 'my-app' })
```

## 複数のジョブの一括ポーリング
次の 2 つのエンドポイントは、1 回のやり取りで最大 100 件のジョブのステータスを返します。

**GET /v1/jobs/status**

```bash
curl -s "https://app.nodaro.ai/v1/jobs/status?ids=$JOB_A,$JOB_B,$JOB_C" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{
"jobs": [
{ "id": "0f1a9c2e-…", "status": "completed", "output_data": { "imageUrl": "https://…/a.png" }, "error_message": null, "error_hint": null, "credit_status": "committed" },
{ "id": "7d3e5f60-…", "status": "processing", "output_data": null, "error_message": null, "error_hint": null, "credit_status": "reserved" }
]
}
```

**POST /v1/jobs/batch-status**

```bash
curl -s -X POST https://app.nodaro.ai/v1/jobs/batch-status \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"jobIds\": [\"$JOB_A\", \"$JOB_B\", \"$JOB_C\"]}"
```

レスポンスは `{ "data": [ … ] }` で、各ジョブの `id`、`status`、`output_data`、`error_message`、`error_hint` が含まれます。`credit_status` は含まれません。

存在しない ID や、ほかのユーザーのジョブの ID は、エラーにならずに除外されます。レスポンスを、自分の ID の一覧と照らし合わせてください。

## ジョブのキャンセルと削除
`POST /v1/jobs/:id/cancel` は、ジョブをキャンセルし、確保していたクレジットを解放します。レスポンスは `{ "success": true, "cancelled": 1 }` です。`pending_review` で保留中のジョブもキャンセルできます。

`DELETE /v1/jobs/:id` は、ジョブと、そのジョブが生成した非公開のメディアを削除し、`{ "success": true }` を返します。ジョブを削除できるのは、その所有者だけです。実行中のジョブはその状態のまま削除されるので、処理を止めたい場合は、先にキャンセルしてください。

**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/jobs/$JOB_ID/cancel \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts
const { cancelled } = await client.jobs.cancel(jobId)
await client.jobs.delete(jobId)
```

**CLI**

```bash
nodaro jobs cancel 0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10
```

ワークフローの実行全体を止めるには、代わりにその実行をキャンセルします。[実行](https://nodaro.ai/docs/developers/api/executions)を参照してください。

## コンポーネントの実行
`POST /v1/component/execute` は、保存した[**コンポーネント**（Component）](https://nodaro.ai/docs/nodes/automate/component)をバックグラウンドで実行し、`{ jobId }` とともに `202` を返します。このジョブは、ほかのジョブと同じようにポーリングします。サーバーは、コンポーネントの実行に 90 分を割り当て、さらに、その中にある時間のかかるレンダリングの時間枠を加えます。たとえば、[**EDL 適用**（Apply EDL）](https://nodaro.ai/docs/nodes/video/apply-edl)ノードの最終レンダリングがそうです。

独自の時間制限を持つクライアントは、サーバーが待機する時間を問い合わせられます。

```bash
curl -s https://app.nodaro.ai/v1/component/execute/$JOB_ID/wait-limit \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{ "data": { "budgetExcessMs": 0, "waitLimitMs": 5400000, "pendingBudgetedNodes": true } }
```

- `waitLimitMs` は、この実行に対するサーバーの待機時間で、90 分に `budgetExcessMs` を加えた値です。
- `budgetExcessMs` は、実行が時間のかかるレンダリングを開始するまで `0` のままです。
- `pendingBudgetedNodes` は、時間のかかるレンダリングがまだ始まっていない間と、実行自体がまだ始まっていない間は `true` です。そのため、超過分が `0` でも、時間のかかる処理が含まれていないとは限りません。

どちらの値も実行中に変わることがあるので、`waitLimitMs` に達したら、もう一度問い合わせてください。このルートは実行の所有者にだけ応答し、それ以外のユーザーと、コンポーネントの実行ではないジョブには `404` を返します。エディター自体も、このルールに従っています。時間のかかる処理を含まない実行では 30 分、時間のかかるレンダリングが始まった後は `waitLimitMs`、`pendingBudgetedNodes` が `true` の間は最低 90 分待ち、その後もう一度問い合わせます。

## 動画生成 Pro の実行
[動画生成 Pro](https://nodaro.ai/docs/nodes/video/generate-video-pro) は、長い動画を 1 セグメントずつ作り、セグメントの間で進行状況を保存します。そのため、実行を停止して、後から続けられます。動画生成 Pro は Nodaro Cloud で動作し、セルフホスティング環境では、Nodaro Cloud への接続を通じて実行されます。

### 実行の停止
`POST /v1/generate-video-pro/:jobId/stop` は、`processing` の実行を安全に停止します。

- 生成中のセグメントは破棄されますが、モデルはレンダリングを続けるため、その料金は請求されます。
- 残りのセグメントはスキップされます。
- 完成したセグメントはすべてつなぎ合わされ、ジョブの最終的な動画になります。
- 確保されたクレジットのうち、使われなかった分は返還されます。

レスポンスは `{ "jobId": "…", "stopping": true }` です。ジョブのポーリングを続けてください。ジョブは `completed` で終わり、`output_data.pro.stopped` が `true` になり、`stoppedAtSegment` が含まれます。まだ `pending` のジョブは、代わりにキャンセルされ、全額が返還されます。

### 実行の継続
`POST /v1/generate-video-pro/continue` は、終了したジョブから**新しいジョブ**を開始します。元のジョブのプランと、`fromSegment` より前の納品済みのセグメントをすべて再利用し、そこから先を生成し直します。

```json
{ "fromJobId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "fromSegment": 4 }
```

- `fromSegment` は 1 から数えます。デフォルトは、納品されなかった最初のセグメントです。
- 元のジョブは、終了している必要があります。つまり、停止したジョブ、納品済みのセグメントが 1 つ以上ある失敗したジョブ、完了したジョブのいずれかです。完了した実行で `fromSegment` を明示的に指定すると、その終盤を生成し直します。
- 支払うのは、生成し直すセグメントの分と、動画生成 Pro の固定料金だけです。
- このルートは `Idempotency-Key` ヘッダーに対応しているので、再試行したリクエストが 2 つ目のジョブを開始することはありません。

レスポンスは `{ jobId, continuedFromJobId, fromSegment, segmentCount }` です。新しい `jobId` をポーリングしてください。

**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/generate-video-pro/$JOB_ID/stop \
  -H "Authorization: Bearer $NODARO_API_KEY"

curl -s -X POST https://app.nodaro.ai/v1/generate-video-pro/continue \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4c1f0a7e-2b3d-4e5f-8a9b-0c1d2e3f4a5b" \
  -d "{\"fromJobId\": \"$JOB_ID\", \"fromSegment\": 4}"
```

**TypeScript SDK**

```ts
await client.videoPro.stop(jobId)
const { data } = await client.jobs.getStatus(jobId) // completes with a partial video

const { jobId: childId } = await client.videoPro.continueRun(jobId, { fromSegment: 4 })
```

**CLI**

```bash
nodaro video-pro stop 9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d
nodaro video-pro continue 9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d --from-segment 4 --watch
```

どちらのルートも、自分のものではないジョブには `404` を、動画生成 Pro の実行ではないジョブには `400` を返します。

### 実行の見積もり
`POST /v1/credits/video-pro-estimate` は、ジョブを作成したりクレジットを確保したりせずに、動画生成 Pro の実行の料金を見積もります。

```json
{ "provider": "gemini-omni-flash", "resolution": "720p", "duration": 12, "renderMethod": "keyframes", "segmentMode": "short" }
```

ある設定例では、レスポンスは `{ "data": { "credits": 760, "upperBound": true } }` になります。現在の料金は、実際のレスポンスで確認してください。

<TypeTable
type={{
provider: { type: 'string', description: '動画モデルです。', required: true },
resolution: { type: 'string', description: '解像度です。', default: '720p' },
duration: { type: 'integer', description: '合計の長さ（秒）で、1〜3600 です。', default: '8' },
aspectRatio: { type: 'string', description: 'フレームの縦横比です。' },
renderMethod: { type: 'string', description: 'extend または keyframes です。' },
anchorMode: { type: 'string', description: 'upfront、progressive、none のいずれかです。' },
contextTailSec: { type: 'number', description: '2〜15 秒です。' },
segmentMode: { type: 'string', description: 'short、long、max のいずれかです。preferredSegmentSec や segmentDurations とは併用できません。' },
preferredSegmentSec: { type: 'integer', description: '4〜15 秒です。' },
segmentDurations: { type: 'integer[]', description: 'セグメントの長さを明示的に 1〜24 個指定します。それぞれ 1〜30 秒です。' },
planOnly: { type: 'boolean', description: 'プラン作成の段階だけの料金を見積もります。' },
}}
/>

- `segmentMode` が `short` または `long` の場合、実行はまず、アクション全体をソースの区間に割り当てます。`upperBound: true` は、その金額がプラン作成前の確保の上限であることを意味します。最終的な請求額は、実際のプランに従います。
- `planOnly` の見積もりはプラン作成の料金を対象とし、`upperBound: false` を返します。その `sourceSegmentDurations` と `planCheckpoint` は、同じモードと設定のまま、`sourceSegmentDurations` と `seedPlan` として送り返せます。

セグメント分割の仕組みと各設定の役割については、[動画生成 Pro](https://nodaro.ai/docs/nodes/video/generate-video-pro) を参照してください。

## Frequently asked questions

### Nodaro API のジョブとは何ですか？

ジョブは、1 枚の画像、1 本の動画のレンダリング、1 つの音声クリップなど、生成の 1 単位です。POST /v1/ の後にノードタイプを付けて呼び出すとジョブ ID が返り、ワークフローの実行では、実行する AI ノードごとに 1 つのジョブが作成されます。

### Nodaro のジョブは、どのくらいの間隔でポーリングすればよいですか？

2〜5 秒ごとです。ジョブの状態は、ミリ秒単位ではなく秒単位で変わります。多くのジョブを追跡するには、1 回の呼び出しで最大 100 件のジョブを返す GET /v1/jobs/status または POST /v1/jobs/batch-status を使います。

### pending_review というステータスは、どういう意味ですか？

デプロイ環境が、結果を公開する前に、人によるレビューのために保留していることを示します。ジョブはまだ処理中で、クレジットは確保されたままです。ポーリングを続けてください。ジョブは最終的に completed、failed、cancelled のいずれかになります。

### 失敗したジョブのクレジットが返還されたかどうかは、どうすればわかりますか？

GET /v1/jobs/:id または GET /v1/jobs/:id/status で credit_status を確認します。値は reserved、committed、refunded のいずれかです。安全フィルターやデプロイ環境のポリシーでブロックされた生成は、必ず返還されます。

### 動画生成 Pro（Generate Video Pro）の実行を停止して、それまでに作ったものを残せますか？

はい。POST /v1/generate-video-pro/:jobId/stop は、完成したセグメントを残し、それらをつなぎ合わせて最終的な動画にし、使われなかった確保分を返還します。後から、新しいジョブとして実行を続けることもできます。
