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

# Webhooks

> 工作結束時通知你的伺服器。

## 設定

提交工作時帶 `webhook_url`（公開的 `https` 網址）：

```bash theme={null}
curl https://api.blon.ai/v1/jobs \
  -H "Authorization: Bearer $BLON_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"model":"gpt-image-2","input":{"prompt":"A ceramic teapot on a linen table, soft morning light"},"webhook_url":"https://example.com/hooks/blon","metadata":{"order":"A-17"}}'
```

工作結束時，Blon 會送一個事件到該網址：

| 事件 | 送出時機 |
| - | - |
| `job.completed` | 工作完成 |
| `job.failed` | 工作失敗 |
| `job.canceled` | 工作已取消 |

每筆工作的每個事件最多送一次。

## Payload

body 是事件本身，`data` 是整筆工作；產物網址每次投遞都會重新簽發。

```json theme={null}
{
  "id": "whd_4f1c2e9b7a6d4c3b",
  "event": "job.completed",
  "created_at": "2026-10-30T08:00:09.000Z",
  "data": {
    "id": "cmh2x0f6k0001qv8z9d4c1a7e",
    "object": "job",
    "model": "gpt-image-2",
    "status": "completed",
    "provider_status": "succeeded",
    "queue_position": null,
    "cancelable": false,
    "next_poll_after_seconds": null,
    "urls": {
      "get": "https://api.blon.ai/v1/jobs/cmh2x0f6k0001qv8z9d4c1a7e",
      "cancel": null
    },
    "input": {
      "prompt": "A ceramic teapot on a linen table, soft morning light"
    },
    "metadata": {
      "order": "A-17"
    },
    "cost": {
      "currency": "CREDITS",
      "estimated": "8",
      "charged": "8",
      "refunded": "0"
    },
    "usage": null,
    "artifacts": [
      {
        "id": "cmh2x0g2p0002qv8zb3n5k8wd",
        "name": "default",
        "type": "image",
        "url": "https://blon.ai/api/assets/cmh2x0g2p0002qv8zb3n5k8wd?ticket=…",
        "url_expires_at": "2026-10-30T08:15:00.000Z",
        "content_type": "image/png",
        "size_bytes": 1482113,
        "width": 1024,
        "height": 1024,
        "duration_ms": null
      }
    ],
    "expires_at": "2026-11-06T08:00:00.000Z",
    "error": null,
    "webhook": {
      "url": "https://example.com/hooks/blon",
      "status": "delivered",
      "attempts": 1
    },
    "idempotency_hit": false,
    "request_id": "cmh2x0f6k0001qv8z9d4c1a7e",
    "created_at": "2026-10-30T08:00:00.000Z",
    "started_at": "2026-10-30T08:00:01.000Z",
    "completed_at": "2026-10-30T08:00:09.000Z",
    "updated_at": "2026-10-30T08:00:09.000Z"
  }
}
```

工作的 `webhook` 欄位顯示投遞狀況：`status`（`pending`、`delivered` 或 `failed`）與 `attempts`。

## 驗證簽章

簽章遵循 [Standard Webhooks](https://www.standardwebhooks.com)：每個請求帶 `webhook-id`（即事件 `id`）、`webhook-timestamp` 與 `webhook-signature` 三個 header。請以 Standard Webhooks 官方套件，搭配簽章秘密與原始 request body 驗證。

```javascript Node.js theme={null}
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.BLON_WEBHOOK_SECRET);
// Pass the raw body string and the request headers; throws when the signature is invalid.
const event = wh.verify(rawBody, {
  "webhook-id": req.headers["webhook-id"],
  "webhook-timestamp": req.headers["webhook-timestamp"],
  "webhook-signature": req.headers["webhook-signature"],
});
```

```python Python theme={null}
from standardwebhooks.webhooks import Webhook

wh = Webhook(os.environ["BLON_WEBHOOK_SECRET"])
# raises when the signature is invalid
event = wh.verify(raw_body, dict(request.headers))
```

簽章秘密在[開發者主控台](https://blon.ai/console/webhooks)查看與輪替；輪替後新舊秘密並存 24 小時。

## 投遞與重試

* 每次投遞等待 10 秒。
* `2xx` 視為成功。
* `4xx` 視為永久失敗，不再重試。
* 其餘情況最多重試 10 次，從 30 秒起倍增，最長間隔 1 小時。

沒有重新投遞端點；漏收事件時，請以 `GET /jobs/{id}` 讀取工作。

請先快速回應 `2xx`，耗時的處理留到之後；以事件 `id` 排除重複。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.