Configure your webhook URL in the Developer dashboard. Your webhook secret starts with whsec_ and is used to sign all payloads.
Webhooks allow you to receive HTTP POST notifications when image generations complete or fail, eliminating the need for polling.
Best practices:
Verify signatures . Always verify webhook signatures before processing events to ensure authenticity
Respond quickly . Return a 2xx status code within 10 seconds to acknowledge receipt
Use HTTPS . Always use HTTPS endpoints in production for secure communication
Secure secrets . Store webhook secrets in environment variables, never in code
Implement idempotency . Use the webhook id field to prevent duplicate processing of the same event
Handle retries . Failed webhooks will be retried with exponential backoff. Ensure your endpoint is idempotent
Events
Triggered when a generation finishes successfully.
Triggered when a generation fails with an error.
All webhook requests include the following headers:
HMAC-SHA256 signature for payload verification.
Unix timestamp of when the webhook was sent.
Webhook payload format version.
Always set to BabySea-Webhook/1.0.
All API responses include these headers for monitoring and debugging:
Unique identifier for the request (UUID v4). Include this when contacting support for faster debugging.
Current API version being used.
Maximum number of requests allowed per minute for this endpoint.
Number of requests remaining in the current rate limit window.
Unix timestamp indicating when the rate limit window resets.
Payload structure
Webhook events are sent as POST requests with a JSON body:
{
"id" : "bb7fbf57-1209-4d4e-88d4-0af285a96047" ,
"event" : "generation.completed" ,
"created_at" : "2026-01-22T16:07:08.044Z" ,
"api_version" : "2026-01-05" ,
"data" : {
"generation_id" : "f53f23ab-c8b6-4e4e-acd5-310a552821d7" ,
"status" : "succeeded" ,
"name" : "Room Design" ,
"prompt" : "Make the sheets in the style of the logo. Make the scene natural" ,
"model" : "google/nano-banana" ,
"credit_cost" : 2 ,
"output_images" : [
"https://app.babysea.ai/storage/v1/object/public/image_generation/output/f53f23ab-c8b6-4e4e-acd5-310a552821d7/tmpscku3dnk.jpeg"
],
"output_success" : 1 ,
"output_failed" : 0 ,
"failure_reason" : null ,
"predict_time" : 9.043826488 ,
"total_time" : 9.452142989 ,
"created_at" : "2026-01-22T16:06:55.588566" ,
"completed_at" : "2026-01-22T16:07:08.044Z"
}
}
Payload schema
Unique identifier for this webhook delivery. Use for idempotency.
Event type that triggered the webhook. Values: generation.completed, generation.failed.
ISO 8601 timestamp of when the webhook was created.
API version used for this webhook.
Container object for generation data. Unique identifier for the generation (UUID v4).
Final status of the generation. Values: succeeded, failed.
User-defined name for the generation.
Text prompt used to generate the image.
Identifier of the AI model used for generation.
Number of credits deducted for this generation.
Array of URLs pointing to the successfully generated images.
Number of images successfully generated.
Number of images that failed to generate.
Reason for generation failure (null if successful).
Model inference time in seconds.
Total processing time in seconds (includes inference and post-processing).
ISO 8601 timestamp of when the generation was created.
ISO 8601 timestamp of when the generation completed.
Signature verification
Always verify webhook signatures to ensure requests are from BabySea and prevent unauthorized access.
const crypto = require ( 'crypto' );
function verifyWebhookSignature ( req ) {
const signature = req . headers [ 'x-webhook-signature' ];
const timestamp = req . headers [ 'x-webhook-timestamp' ];
const payload = JSON . stringify ( req . body );
// Parse signature header: t=timestamp,v1=signature
const [, v1Part ] = signature . split ( ',' );
const receivedSig = v1Part . split ( '=' )[ 1 ];
// Compute expected signature
const signedPayload = ` ${ timestamp } . ${ payload } ` ;
const expectedSig = crypto
. createHmac ( 'sha256' , process . env . BABYSEA_WEBHOOK_SECRET )
. update ( signedPayload )
. digest ( 'hex' );
// Verify timestamp is recent (prevent replay attacks)
const now = Math . floor ( Date . now () / 1000 );
if ( Math . abs ( now - parseInt ( timestamp )) > 300 ) {
return false ; // Reject if older than 5 minutes
}
return receivedSig === expectedSig ;
}
import hmac
import hashlib
import time
import os
def verify_webhook_signature ( request ):
signature = request.headers.get( 'X-Webhook-Signature' )
timestamp = request.headers.get( 'X-Webhook-Timestamp' )
payload = request.get_data( as_text = True )
# Parse signature header
parts = dict (p.split( '=' ) for p in signature.split( ',' ))
received_sig = parts.get( 'v1' )
# Compute expected signature
signed_payload = f " { timestamp } . { payload } "
expected_sig = hmac.new(
os.environ[ 'BABYSEA_WEBHOOK_SECRET' ].encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# Verify timestamp (prevent replay attacks)
if abs (time.time() - int (timestamp)) > 300 :
return False
return hmac.compare_digest(received_sig, expected_sig)
Rate limits
API requests are rate limited per account to ensure system stability.
Generation
You can create up to 10 generations per 60 seconds , with only 1 concurrent generation allowed.
Endpoint
GET /v1/image-generation/:id
10 requests per minute
POST /v1/image-generation/:modelIdentifier
1 concurrent request maximum
Handling
Monitor remaining requests : Check the X-RateLimit-Remaining header to track your usage
Implement exponential backoff : When rate limited (429 status), wait before retrying with increasing delays
Use reset timestamp : Check X-RateLimit-Reset to know exactly when to retry
Queue requests : Implement client-side queuing to stay within rate limits
Log request IDs : Always log X-Request-ID for debugging failed or rate-limited request