> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# タスク完了コールバック（Webhook）

> 非同期生成タスクの送信時にコールバックURLを指定すると、タスク完了後（成功・失敗を問わず）に結果を能動的にPOSTします。ポーリング不要です。

動画 / 画像 / 音声などの非同期生成タスクを送信する際に、コールバックURLを指定できます。**タスクが完了（成功または失敗）すると**、結果をあなたのURLに能動的にPOSTするため、ずっとポーリングして問い合わせる必要はありません。

## クイックスタート

タスク送信時に、リクエストボディに `webhook` フィールドを追加します:

```bash theme={null}
curl -X POST https://あなたの接続ドメイン/v1/images/generations \
  -H "Authorization: Bearer あなたのAPIKey" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "a red apple on a table",
    "size": "1024x1024",
    "webhook": "https://your-server.com"
  }'
```

タスク完了後、**`あなたのURL + /callback`** に POST リクエストを送信します。

<Note>
  動画・音声などその他の非同期タスクのエンドポイントも同様で、いずれもリクエストボディに `webhook` フィールドを追加します。
</Note>

## URLのルール

指定する `webhook` は\*\*ベースURL（base）\*\*であり、その後ろに自動的に `/callback` を連結します:

| あなたの `webhook`                 | 実際にPOSTする先                             |
| ------------------------------ | -------------------------------------- |
| `https://your-server.com`      | `https://your-server.com/callback`     |
| `https://your-server.com/api`  | `https://your-server.com/api/callback` |
| `https://your-server.com/api/` | `https://your-server.com/api/callback` |

そのため、サーバー側で `POST .../callback` を受け取るエンドポイントを用意する必要があります。

## 受信する内容

プッシュされる内容は\*\*「[タスクステータスの取得](/ja/api-reference/tasks/status)」エンドポイントが返すものと完全に同じ\*\*です。同じ解析ロジックで処理できます。

<CodeGroup>
  ```json 成功（status: completed） theme={null}
  {
    "id": "task_01KV7FXR8BEYS1BWHJCT3JMCJ5",
    "status": "completed",
    "progress": 100,
    "created": 1781589029,
    "completed": 1781589058,
    "actual_time": 29,
    "cost": 0.006,
    "credits_cost": 0.06,
    "result": {
      "images": [{ "url": ["https://.../result.png"], "expires_at": 1781675458 }]
    }
  }
  ```

  ```json 失敗（status: failed） theme={null}
  {
    "id": "task_xxx",
    "status": "failed",
    "progress": 100,
    "created": 1781589029,
    "completed": 1781589050,
    "error": { "message": "失敗理由...", "code": "" }
  }
  ```
</CodeGroup>

<Note>
  動画タスクの結果は `result.videos`、音声は `result.audios` にあります。
</Note>

<Warning>
  タスクが**終了状態**（`completed` / `failed`）に達したときのみプッシュします。処理中はプッシュしません。
</Warning>

## リトライと重複排除（重要）

* **リトライ**: サーバー側が約10秒以内に `2xx` を返さない、または `5xx` を返した場合、自動的に最大 **3回** リトライします。間隔は約 **10秒、30秒、60秒** です。3回とも失敗すると諦めます（約2分以内に終了）。
* **リトライしないケース**: エンドポイントが `4xx` を返した場合（URL / リクエストに問題があると見なす）、リトライせずに直ちに諦めます。
* **重複排除**: 通常、1つのタスクは1回のみプッシュされます。ただし極端なケース（送信後・確認前に当方で再起動が発生した場合など）では、**重複プッシュを受信する可能性があります**。重複処理を避けるため、必ず **`id`（task\_id）で冪等に重複排除** してください。

**受信エンドポイントの推奨事項:**

<Steps>
  <Step title="できるだけ早く 2xx を返す">
    まず受け取ってキューに入れ、その後で非同期に処理してください。処理完了まで当方を待たせないでください。
  </Step>

  <Step title="id で重複排除する">
    `id`（task\_id）を冪等キーとして使用し、重複処理を回避してください。
  </Step>

  <Step title="署名を設定・検証する">
    本番環境では、コールバックリクエストの送信元を検証し、偽造されたリクエストを拒否してください。
  </Step>
</Steps>

## コールバックURLの要件

セキュリティのため、コールバックURLは次を満たす必要があります:

| 要件       | 説明                                                              |
| -------- | --------------------------------------------------------------- |
| 公開アクセス可能 | 内部 / ローカルアドレス（`127.0.0.1`、`10.x`、`192.168.x` など）**は不可**（拒否されます） |
| プロトコル    | `http` または `https`（`https` 推奨）                                  |
| ポート      | 標準ポート（`80` / `443`）を使用。非標準ポートはブロックされる可能性があります                   |
| ドメイン     | 当方自身のサービスドメインを指してはいけません                                         |

要件を満たさないURLは直ちに破棄されます（プッシュもリトライもされません）。

## よくある質問

<AccordionGroup>
  <Accordion title="webhook付きでタスクを送信したのにプッシュが届かない？">
    1つずつ確認してください:

    1. タスクは**本当に完了しましたか**？ タスク詳細を確認し、`status` が `completed` / `failed` になっていますか（処理中はプッシュしません）？
    2. あなたのURLは**公開アクセス可能**ですか？ 当方からあなたの `/callback` に到達できますか？
    3. ポートは標準ポート（80 / 443）ですか？ 非標準ポートはセキュリティポリシーでブロックされる可能性があります。
    4. あなたの `/callback` は**速やかに 2xx を返しましたか**？ 4xx を返すと直ちに諦めます。
    5. `https` を使っていますか？ 証明書は有効ですか？
  </Accordion>

  <Accordion title="result の url が配列になっているのはなぜ？">
    一部のモデルは一度に複数枚の画像を生成するため、`images[].url` は配列になることがあります。配列として処理してください。
  </Accordion>

  <Accordion title="結果リンクに有効期限はありますか？">
    `result` に `expires_at`（Unixタイムスタンプ）が含まれる場合、そのリンクの有効期限を示します。速やかに保存し直してください。
  </Accordion>

  <Accordion title="「処理中」のステータスはプッシュされますか？">
    いいえ。タスクが最終的に成功または失敗したときに1回だけプッシュします。
  </Accordion>
</AccordionGroup>

## 最小の受信側サンプル

```python Python theme={null}
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class H(BaseHTTPRequestHandler):
    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        body = self.rfile.read(n)
        data = json.loads(body)
        print("タスクコールバックを受信:", data["id"], data["status"])
        # TODO: id で重複排除し、署名を検証してから処理する
        self.send_response(200); self.end_headers()
        self.wfile.write(b'{"ok":true}')

HTTPServer(("0.0.0.0", 443), H).serve_forever()
```

受信後はできるだけ早く `200` を返し、処理ロジックはバックグラウンドで非同期に実行してください。
