# 接收生成通知

`generationEvent`

发送到你的端点的事件： `generationEvent`

下面的接口说明为英文原文。

Signed per the Standard Webhooks specification. Verify with an official Standard Webhooks library and the endpoint's whsec_ signing secret: it checks Base64(HMAC-SHA256(Base64-decoded secret, webhook-id + '.' + webhook-timestamp + '.' + raw_body)) against each v1 entry in webhook-signature in constant time and rejects timestamps more than five minutes from now. Deduplicate by webhook-id. Redelivery preserves the body and webhook-id and signs again with a new timestamp.

VizoAPI 会把这个请求发送到你在控制台创建的 Webhook 端点。

## 请求头

- `webhook-id` (string, 必填) Unique event ID, equal to event_id in the body. Unchanged on retries and redelivery; use it to deduplicate.
- `webhook-timestamp` (string, 必填) Unix time in seconds when this attempt was sent.
- `webhook-signature` (string, 必填) Space-delimited list of v1 signatures. Two are sent for 24 hours after a secret rotation; accept the request if any one matches.

## 请求体

- `billing_status` (string, 必填) Pending awaits funding; unfunded closed without a hold; held means Credits are held for the task; settled charges verified delivery; released returns the hold; refunded reverses previously consumed generation Credits. 可选值: `pending`, `unfunded`, `held`, `settled`, `released`, `refunded`
- `closed_status` (string, 必填) Immutable customer closure. Unresolved means the resolution deadline passed without confirmed delivery. 可选值: `succeeded`, `failed`, `canceled`, `unresolved`
- `created_at` (date-time, 必填)
- `delivery_status` (string, 必填) Managed output delivery state, independent of supplier execution. 可选值: `pending`, `copying`, `ready`, `failed`
- `error_code` (string, 可选) Stable accepted-task failure code, matching generation error.code. Omitted when no failure code applies. Funding or recovery refusals, provider capacity, content policy, media validity, draft expiry and other execution failures remain distinct. 可选值: `insufficient_balance`, `balance_negative`, `budget_exceeded`, `spending_blocked`, `recovery_review`, `provider_unavailable`, `input_moderated`, `output_moderated`, `input_media_invalid`, `input_media_unreachable`, `draft_expired`, `input_rejected`, `generation_failed`
- `event_id` (string, 必填)
- `execution_status` (string, 必填) Supplier execution state: processing until execution ends. Succeeded can precede verified media delivery. 可选值: `processing`, `succeeded`, `failed`, `canceled`, `unresolved`
- `free` (boolean, 必填)
- `late_result_available` (boolean, 必填) 关闭后才到的结果，免费提供。
- `price` (object, 必填) 这个任务的积分：estimated 是预估额，也就是冻结额，实际扣费按实际用量计算，可能与预估略有差异；reserved 是当前冻结的积分；charged 是按实际用量和固定费率扣的积分，结算前为 0.000；refunded 是退款退回的积分。
  - `charged` (string, 必填) Credits consumed by this task. Set when billing_status is settled: the actual usage the model reported at the fixed rate, which may be above or below estimated; any unused part of the hold returns to the balance, and usage above the hold is charged too. 0.000 in every other state, including after a refund.
  - `estimated` (string, 必填) The estimated cost of this task, in Credits, fixed when the task was accepted. This is the amount held for the task. The charge follows actual usage and may differ slightly from it, above or below.
  - `refunded` (string, 可选) Credits a refund returned to the balance: what this task had been charged. 0.000 unless billing_status is refunded. Absent from events created before this field was added.
  - `reserved` (string, 必填) Credits held for this task when the event was created, as a decimal string with 3 places. Equal to estimated while billing_status is held, otherwise 0.000.
  - `unit` (string, 必填) Every amount is in Credits. 可选值: `credit`
- `result` (object, 必填, 可为 null) 交付后的素材。
  - `asset_id` (string, 必填)
- `task_id` (string, 必填)
- `type` (string, 必填) 可选值: `generation.succeeded`, `generation.failed`, `generation.canceled`, `generation.unresolved`, `generation.refunded`, `generation.late_result_available`
- `version` (integer, 必填)

```json
{
  "billing_status": "held",
  "closed_status": "succeeded",
  "created_at": "2026-11-02T08:01:43Z",
  "delivery_status": "pending",
  "error_code": "insufficient_balance",
  "event_id": "string",
  "execution_status": "processing",
  "free": false,
  "late_result_available": false,
  "price": {
    "charged": "0.000",
    "estimated": "157.000",
    "refunded": "0.000",
    "reserved": "157.000",
    "unit": "credit"
  },
  "result": null,
  "task_id": "gen_01JD8Z5QW3XR",
  "type": "generation.succeeded",
  "version": 1
}
```

## 响应

### 2XX Delivery accepted

```json
null
```

### cURL

```bash
# Your endpoint receives a POST signed per Standard Webhooks. Replay one locally to test your handler:
curl -X POST "$YOUR_WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -H "webhook-id: $EVENT_ID" \
  -H "webhook-timestamp: $TIMESTAMP" \
  -H "webhook-signature: v1,$SIGNATURE" \
  --data-binary @event.json
```

### Python

```python
# pip install standardwebhooks
from standardwebhooks import Webhook, WebhookVerificationError

wh = Webhook("whsec_...")  # your endpoint's signing secret

def verify(headers, raw_body: bytes):
    try:
        return wh.verify(raw_body, headers)  # returns the parsed event
    except WebhookVerificationError:
        return None
```

### Node.js

```javascript
// npm install standardwebhooks
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.WEBHOOK_SECRET); // whsec_...

// rawBody is a Buffer of the request body as received, e.g. express.raw({ type: "application/json" }).
export function verify(headers, rawBody) {
  try {
    return wh.verify(rawBody, headers); // returns the parsed event
  } catch {
    return null;
  }
}
```
