Skip to content
Docs version r15
OpenAPIJSONYAML
API reference / Webhooks

generationEvent

EventgenerationEvent

Verify Base64(HMAC-SHA256(signing secret, timestamp + '.' + event_id + '.' + raw_body)) with a constant-time comparison and a five-minute Unix timestamp window. Deduplicate by event_id. Redelivery preserves the body and event_id.

Markdown fileTry in PlaygroundDocs version r15

VizoAPI sends this request to the webhook endpoint you create in the console.

Request headers

Request headers
FieldDescription
X-Relay-Key-IDstringRequired
X-Relay-Event-IDstringRequired
X-Relay-TimestampstringRequired
X-Relay-SignaturestringRequired

Request body

A JSON object. Fields that are not listed are rejected.

Request body
FieldDescription
billing_statusstringRequired

Whether Credits are held, charged or released.

closed_statusstringRequired
created_atdate-timeRequired
delivery_statusstringRequired

Whether the result is stored and downloadable. ready means it can be downloaded.

error_codestringOptional
event_idstringRequired
execution_statusstringRequired

Progress of the model run.

freebooleanRequired
late_result_availablebooleanRequired

A result that arrived after closing is provided at no charge.

resultobject, can be nullRequired

Delivered assets, once available.

Fields of result
Fields of result
asset_idstringRequired
task_idstringRequired
typestringRequired
generation.succeededgeneration.failedgeneration.canceledgeneration.unresolvedgeneration.refundedgeneration.late_result_available
versionintegerRequired

Responses

2XX Delivery accepted

Request example

cURL
# Your endpoint receives a POST. Replay one locally to test your handler:
curl -X POST "$YOUR_WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -H "X-Relay-Key-ID: $KEY_ID" \
  -H "X-Relay-Event-ID: $EVENT_ID" \
  -H "X-Relay-Timestamp: $TIMESTAMP" \
  -H "X-Relay-Signature: $SIGNATURE" \
  --data-binary @event.json
Python
import base64, hashlib, hmac, time

def verify(secret: bytes, headers: dict, raw_body: bytes) -> bool:
    ts = headers["X-Relay-Timestamp"]
    if abs(time.time() - int(ts)) > 300:
        return False  # outside the five-minute window
    msg = ts.encode() + b"." + headers["X-Relay-Event-ID"].encode() + b"." + raw_body
    digest = base64.b64encode(hmac.new(secret, msg, hashlib.sha256).digest()).decode()
    return hmac.compare_digest(digest, headers["X-Relay-Signature"])
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, headers, rawBody) {
  const ts = headers["x-relay-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // outside the five-minute window
  const msg = `${ts}.${headers["x-relay-event-id"]}.${rawBody}`;
  const want = createHmac("sha256", secret).update(msg).digest();
  const got = Buffer.from(headers["x-relay-signature"], "base64");
  return got.length === want.length && timingSafeEqual(got, want);
}
ResponseEvent
{
  "billing_status": "held",
  "closed_status": "string",
  "created_at": "2026-11-02T08:01:43Z",
  "delivery_status": "pending",
  "error_code": "string",
  "event_id": "string",
  "execution_status": "queued",
  "free": false,
  "late_result_available": false,
  "result": null,
  "task_id": "string",
  "type": "generation.succeeded",
  "version": 1
}