Payload structure
Every webhook delivery shares the same envelope format:
{
"webhook_event": "generation.completed",
"webhook_timestamp": "2025-01-15T12:00:00.000Z",
"webhook_delivery_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"webhook_data": { ... }
}
Event type. One of generation.started, generation.completed, generation.failed, generation.canceled, or webhook.test.
ISO 8601 timestamp when the event was dispatched.
Unique delivery identifier (UUID). Use this as an idempotency key to handle duplicate deliveries.
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.
| Field | Type | Present on |
|---|
account_id | string | All events |
model_identifier | string | All events |
generation_id | string | All events |
generation_status | string | All events |
generation_provider_initialize | string | generation.started |
generation_provider_used | string | generation.completed |
generation_prediction_id | string | generation.started, generation.completed, generation.canceled |
generation_output_file | string[] | generation.completed |
generation_error | string | generation.failed |
generation_error_code | string | generation.failed |
credits_refunded | boolean | generation.canceled |
generation.started
This event uses generation_status: processing.
{
"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"
}
}
| Field | Value |
|---|
generation_status | processing |
generation_provider_initialize | The provider that received the generation request |
generation_prediction_id | The provider’s prediction identifier |
generation.completed
This event uses generation_status: succeeded.
{
"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"
]
}
}
| Field | Value |
|---|
generation_status | succeeded |
generation_provider_used | The provider that produced the final output |
generation_output_file | Array of URLs pointing to the generated files |
generation.failed
This event uses generation_status: failed.
{
"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"
}
}
| Field | Value |
|---|
generation_status | failed |
generation_error | Human-readable error description |
generation_error_code | BabySea error code. See SDK Errors for the full list |
generation.canceled
This event uses generation_status: canceled.
{
"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
}
}
| Field | Value |
|---|
generation_status | canceled |
generation_prediction_id | Included if the generation was already submitted to a provider before cancellation |
credits_refunded | true 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.
{
"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:
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);
}
}