Skip to main content

Webhooks


Overview

Receive real-time notifications when generations complete, eliminating the need for polling.

Events

  • generation.completed Generation finished successfully
  • generation.failed Generation failed with error

Webhook Headers

  • X-Webhook-Signature HMAC-SHA256 signature for verification
  • X-Webhook-Timestamp Unix timestamp when sent
  • X-Webhook-Version Webhook format version
  • User-Agent BabySea-Webhook/1.0

Payload Structure

Webhook events are sent as POST requests with JSON body:

{
"id": "{delivery_id}",
"event": "{event_type}",
"created_at": "{iso_timestamp}",
"api_version": "{api_version}",
"data": {
"generation_id": "{generation_id}",
"status": "{status}",
"name": "{generation_name}",
"prompt": "{prompt}",
"model": "{model_name}",
"credit_cost": "{integer}",
"output_images": ["{image_url}"],
"output_success": "{integer}",
"output_failed": "{integer}",
"failure_reason": "{string_or_null}",
"predict_time": "{number}",
"total_time": "{number}",
"created_at": "{iso_timestamp}",
"completed_at": "{iso_timestamp}"
}
}

Signature Verification (Node.js)

Always verify webhook signatures to ensure requests are from BabySea:

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;
}

Signature Verification (Python)

import hmac
import hashlib
import time

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)

Configuration

Configure your webhook URL in the Developer dashboard. Your webhook secret, starting with whsec_, is used to sign all payloads.

Best Practices

  • Always verify signatures before processing webhooks
  • Respond with 2xx status within 10 seconds
  • Use HTTPS endpoints in production
  • Store webhook secrets securely (environment variables)
  • Implement idempotency using the webhook id field

Request Limits

API requests are rate limited per account to ensure system stability.

info

You can create up to 10 generations per 60 seconds, with only 1 concurrent generation allowed.

  • GET /status = 60 req/min
  • GET /data/account = 60 req/min
  • GET /data/list/image = 30 req/min
  • GET /image-generation/:id = 10 req/min
  • POST /image-generation/:modelIdentifier = 1 max

Response Headers

All API responses include these headers:

  • X-Request-ID: {uuid}
  • X-API-Version: {version}
  • X-RateLimit-Limit: {max_requests_per_minute}
  • X-RateLimit-Remaining: {remaining_requests}
  • X-RateLimit-Reset: {unix_timestamp}
tip
  • X-Request-ID Unique identifier for each request. Include this in support tickets for faster debugging
  • X-API-Version Current API version. Check this to ensure compatibility

Handling Rate Limits

  • Monitor the X-RateLimit-Remaining header
  • Implement exponential backoff when rate limited
  • Use the X-RateLimit-Reset timestamp to know when to retry
  • Queue requests on your side to stay within limits
  • Log X-Request-ID for debugging failed requests