Skip to main content

Payload structure

Every webhook delivery shares the same envelope format:
JSON
{
  "webhook_event": "generation.completed",
  "webhook_timestamp": "2025-01-15T12:00:00.000Z",
  "webhook_delivery_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "webhook_data": { ... }
}
webhook_event
string
Event type. One of generation.started, generation.completed, generation.failed, generation.canceled, or webhook.test.
webhook_timestamp
string
ISO 8601 timestamp when the event was dispatched.
webhook_delivery_id
string
Unique delivery identifier (UUID). Use this as an idempotency key to handle duplicate deliveries.
webhook_data
object
Generation snapshot that triggered the event.

webhook_data fields

Only fields with values are included. Optional fields are omitted (not set to null) when they do not apply to the event.
FieldTypePresent on
account_idstringAll events
model_identifierstringAll events
generation_idstringAll events
generation_statusstringAll events
generation_provider_initializestringgeneration.started
generation_provider_usedstringgeneration.completed
generation_prediction_idstringgeneration.started, generation.completed, generation.canceled
generation_output_filestring[]generation.completed
generation_errorstringgeneration.failed
generation_error_codestringgeneration.failed
credits_refundedbooleangeneration.canceled

generation.started

This event uses generation_status: processing.
JSON
{
  "webhook_event": "generation.started",
  "webhook_timestamp": "2025-01-15T12:00:00.000Z",
  "webhook_delivery_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "webhook_data": {
    "account_id": "your-account-id",
    "model_identifier": "bfl/flux-schnell",
    "generation_provider_initialize": "replicate",
    "generation_status": "processing",
    "generation_prediction_id": "abc123",
    "generation_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
FieldValue
generation_statusprocessing
generation_provider_initializeThe provider that received the generation request
generation_prediction_idThe provider’s prediction identifier

generation.completed

This event uses generation_status: succeeded.
JSON
{
  "webhook_event": "generation.completed",
  "webhook_timestamp": "2025-01-15T12:00:15.000Z",
  "webhook_delivery_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "webhook_data": {
    "account_id": "your-account-id",
    "model_identifier": "bfl/flux-schnell",
    "generation_provider_used": "replicate",
    "generation_status": "succeeded",
    "generation_prediction_id": "abc123",
    "generation_id": "550e8400-e29b-41d4-a716-446655440000",
    "generation_output_file": [
      "https://storage.babysea.ai/outputs/550e8400-e29b-41d4-a716-446655440000/output-0.png"
    ]
  }
}
FieldValue
generation_statussucceeded
generation_provider_usedThe provider that produced the final output
generation_output_fileArray of URLs pointing to the generated files

generation.failed

This event uses generation_status: failed.
JSON
{
  "webhook_event": "generation.failed",
  "webhook_timestamp": "2025-01-15T12:00:30.000Z",
  "webhook_delivery_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "webhook_data": {
    "account_id": "your-account-id",
    "model_identifier": "bfl/flux-schnell",
    "generation_status": "failed",
    "generation_id": "550e8400-e29b-41d4-a716-446655440000",
    "generation_error": "Provider request failed",
    "generation_error_code": "BSE4001"
  }
}
FieldValue
generation_statusfailed
generation_errorHuman-readable error description
generation_error_codeBabySea error code. See SDK Errors for the full list

generation.canceled

This event uses generation_status: canceled.
JSON
{
  "webhook_event": "generation.canceled",
  "webhook_timestamp": "2025-01-15T12:00:10.000Z",
  "webhook_delivery_id": "d4e5f6a7-b8c9-0123-def0-234567890123",
  "webhook_data": {
    "account_id": "your-account-id",
    "model_identifier": "bfl/flux-schnell",
    "generation_status": "canceled",
    "generation_prediction_id": "abc123",
    "generation_id": "550e8400-e29b-41d4-a716-446655440000",
    "credits_refunded": true
  }
}
FieldValue
generation_statuscanceled
generation_prediction_idIncluded if the generation was already submitted to a provider before cancellation
credits_refundedtrue if credits were refunded, false otherwise

webhook.test

This manual event is sent when you click Send test event on Webhook details. It uses a fixed test generation ID and a single delivery attempt.
JSON
{
  "webhook_event": "webhook.test",
  "webhook_timestamp": "2025-01-15T12:00:00.000Z",
  "webhook_delivery_id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
  "webhook_data": {
    "account_id": "your-account-id",
    "model_identifier": "test",
    "generation_status": "succeeded",
    "generation_id": "00000000-0000-0000-0000-000000000000"
  }
}

Handling events in code

Use the webhook_event field to route events to the correct handler:
TypeScript
import { verifyWebhook, type WebhookPayload } from 'babysea/webhooks';

export async function POST(req: Request) {
  const rawBody = await req.text();
  const signature = req.headers.get('x-babysea-signature') ?? '';

  const payload = await verifyWebhook(
    rawBody,
    signature,
    process.env.BABYSEA_WEBHOOK_SECRET!,
  );

  switch (payload.webhook_event) {
    case 'generation.completed':
      await handleCompleted(payload);
      break;
    case 'generation.failed':
      await handleFailed(payload);
      break;
    case 'generation.started':
      await handleStarted(payload);
      break;
    case 'generation.canceled':
      await handleCanceled(payload);
      break;
    case 'webhook.test':
      // No action needed
      break;
  }

  return new Response('OK', { status: 200 });
}

async function handleCompleted(payload: WebhookPayload) {
  const { generation_id, generation_output_file } = payload.webhook_data;
  // Download or store the output files
  for (const url of generation_output_file ?? []) {
    await saveOutput(generation_id, url);
  }
}

async function handleFailed(payload: WebhookPayload) {
  const { generation_id, generation_error, generation_error_code } = payload.webhook_data;
  // Log the failure and notify the user
  await logFailure(generation_id, generation_error, generation_error_code);
}

async function handleStarted(payload: WebhookPayload) {
  const { generation_id } = payload.webhook_data;
  // Update status in your database
  await updateStatus(generation_id, 'processing');
}

async function handleCanceled(payload: WebhookPayload) {
  const { generation_id, credits_refunded } = payload.webhook_data;
  // Handle cancellation
  await updateStatus(generation_id, 'canceled');
  if (credits_refunded) {
    await notifyRefund(generation_id);
  }
}