Skip to main content

Overview

You can register webhook endpoints to receive generation task lifecycle events whenever you call:
  • POST /api/v1/generation/:provider/:model+ (Bearer token)
Webhooks are delivered as HTTP POST requests to your configured URL, and are HMAC-signed. Webhooks are scoped by workspace: an endpoint will only receive events for the workspace it is created under.

Event Types

Supported events:
  • task.created
  • task.started
  • task.completed
  • task.failed
  • task.canceled
These events are emitted for generation tasks only.

Delivery Format

Your endpoint will receive:
  • Method: POST
  • Header: Content-Type: application/json
Additional headers:
  • X-Webhook-Signature: v1=${hex}
  • X-Webhook-Timestamp: Unix timestamp (seconds)
  • X-Webhook-Event-Id: evt_…
  • X-Webhook-Event-Type: e.g. task.completed
Body:
{
"event": "task.completed",
"prediction": {
  "id": "TASK_DOCUMENT_ID",
  "state": "succeeded",
  "input": { "prompt": "..." },
  "error": null,
  "logs": null,
  "created_at": "2026-01-01T00:00:00.000Z",
  "started_at": "2026-01-01T00:00:01.000Z",
  "completed_at": "2026-01-01T00:00:10.000Z",
  "workspace": "WORKSPACE_DOCUMENT_ID",
  "owner": {
    "id": 1,
    "email": "user@example.com",
    "name": "User",
    "avatar": "https://..."
  },
  "artifacts": [
    {
      "documentId": "ARTIFACT_DOCUMENT_ID",
      "createdAt": "2026-01-01T00:00:00.000Z",
      "state": "succeeded",
      "type": "image",
      "provider": "google",
      "model": "nano-banana",
      "duration": null,
      "size": null,
      "visibility": "private",
      "tags": [],
      "prompt": "original prompt",
      "actual_prompt": "final prompt used by the model",
      "resolution": "1080p",
      "aspect_ratio": "9:16",
      "mode": "text-image",
      "like_count": 0,
      "url": "https://...",
      "asset": {
        "url": "https://...",
        "name": "...",
        "mime": "image/png",
        "width": 1080,
        "height": 1920,
        "formats": {}
      }
    }
  ],
  "credits": {
    "available": 3533,
    "usage": 4
  }
}
}

Signature Verification

Signature is computed as:
  • signed_payload = “$timestamp.$rawBodyString”
  • signature = HMAC_SHA256(secret, signed_payload)
  • Header: X-Webhook-Signature: v1=$signature

Node.js Example

import crypto from "node:crypto";

export function verifyWebhook({
  secret,
  timestamp,
  signatureHeader,
  rawBody,
}: {
  secret: string;
  timestamp: string;
  signatureHeader: string;
  rawBody: string;
}) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  const received = signatureHeader.replace(/^v1=/, "");
  const a = Buffer.from(received, "hex");
  const b = Buffer.from(expected, "hex");
  if (a.length !== b.length) return false;

  return crypto.timingSafeEqual(a, b);
}
Recommended checks:
  • Require timestamp within a short window (e.g. 5 minutes) to prevent replay.
  • Deduplicate by X-Webhook-Event-Id (idempotency).

Retries & Timeouts

  • Timeout: 30 seconds per delivery attempt.
  • Non-2xx responses and network errors will be retried.
  • Max retries: 5 attempts.
  • Backoff schedule (approx): 1m, 5m, 15m, 1h, 4h.

Best Practices

  • Respond quickly with 2xx after verification; do heavy work asynchronously.
  • Treat events as at-least-once delivery; implement idempotency using event id.
  • Always verify signature before processing.