Skip to content

AI generation API webhooks

Receive terminal generation events over HTTP with a verifiable HMAC signature, automatic retries with exponential backoff, and a full delivery history.

Overview

Webhooks are configured per workspace. When a generation reaches a terminal state, FreeInd POSTs the event to your endpoint URL. Create endpoints in the dashboard (Notifications) or with POST /v1/webhooks. The signing secret is shown exactly once — store it securely.

  • At-least-once delivery with idempotent event ids — handle duplicates gracefully.
  • Up to 5 attempts with exponential backoff (base 30s, capped at 1 hour).
  • 10-second delivery timeout; respond 2xx to acknowledge.
  • Delivery history available at GET /v1/webhooks/:id/deliveries.

Events

generation.completedGeneration succeeded; output assets ready
generation.failedGeneration failed with an error code
generation.cancelledGeneration cancelled by user or operator
generation.expiredGeneration expired while queued

Subscribe to the events you need when creating the endpoint via eventTypes.

Request format

Each delivery is a POST with these headers and a JSON body:

Headerstext
X-FreeInd-Signature: t=<unix-timestamp>,v1=<hmac-sha256(secret, t + "." + body)>
X-FreeInd-Event: generation.completed
X-FreeInd-Delivery: <delivery-id>
Bodyjson
{
  "id": "<delivery-id>",
  "event": "generation.completed",
  "data": {
    "generationId": "<generation-id>",
    "workspaceId": "<workspace-id>",
    "mode": "text_to_image",
    "modelId": "<model-id>",
    "status": "succeeded",
    "outputAssetIds": ["<asset-id>"],
    "credits": 4,
    "startedAt": "2026-08-02T10:00:00Z",
    "completedAt": "2026-08-02T10:00:07Z"
  },
  "createdAt": "2026-08-02T10:00:07Z"
}

On generation.completed, data.outputAssetIds references the generated assets — download them via GET /v1/assets/:id/download.

Signature verification

Verify every delivery before trusting it. The signature is HMAC-SHA256 over <timestamp>.<raw body> using the endpoint's signing secret. Use a constant-time comparison:

Verify in Nodejavascript
import { createHmac, timingSafeEqual } from 'node:crypto';

function verifySignature(secret, rawBody, header) {
  const [timestamp, signature] = header.slice(3).split(',v1=');
  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Reject deliveries with a timestamp older than ~5 minutes to protect against replay. The same pattern applies in any language — HMAC primitives are standard.

Retries & delivery history

Deliveries are retried with exponential backoff when the endpoint returns a non-2xx status, times out, or the connection fails — up to 5 attempts. After the final attempt the delivery is marked expired. Every attempt is recorded with its response status and error:

  • GET /v1/webhooks/:id/deliveries — up to 100 recent deliveries per endpoint
  • Delivery statuses: pending → delivered | failed | expired
  • Disabling an endpoint (PATCH /v1/webhooks/:id with status: "disabled") pauses new deliveries

Best practices

  • Respond 2xx as fast as possible — do heavy work (downloads, fan-out) asynchronously.
  • Deduplicate by id (delivery id) — at-least-once means you may see duplicates.
  • Rotate secrets by creating a new endpoint, updating your consumer, then deleting the old one.
  • Prefer webhooks over polling for production workloads.

Cookies keep FreeInd working. Optional analytics require your permission. Read our cookie policy.