Webhooks
Overview
Receive real-time notifications when generations complete, eliminating the need for polling.
Events
generation.completedGeneration finished successfullygeneration.failedGeneration failed with error
Webhook Headers
X-Webhook-SignatureHMAC-SHA256 signature for verificationX-Webhook-TimestampUnix timestamp when sentX-Webhook-VersionWebhook format versionUser-AgentBabySea-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-IDUnique identifier for each request. Include this in support tickets for faster debuggingX-API-VersionCurrent API version. Check this to ensure compatibility
Handling Rate Limits
- Monitor the
X-RateLimit-Remainingheader - Implement exponential backoff when rate limited
- Use the
X-RateLimit-Resettimestamp to know when to retry - Queue requests on your side to stay within limits
- Log
X-Request-IDfor debugging failed requests