What are Webhooks?
Webhooks allow your application to receive real-time notifications when events occur in the Bluma platform, eliminating the need to poll for status updates.
How Webhooks Work
Register Endpoint
Configure a URL where Bluma should send event notifications
Event Occurs
An event happens (e.g., video completes generation)
Webhook Fired
Bluma sends an HTTP POST request to your endpoint
Process Event
Your server receives and processes the webhook payload
Supported Events
Video Events
Event Description When Fired video.queuedVideo generation started Immediately after creation video.processingVideo is being generated When rendering begins video.completedVideo is ready Generation successful video.failedVideo generation failed If an error occurs video.deletedVideo was deleted When user deletes video
Credit Events
Event Description When Fired credits.lowCredits running low When balance < 10 credits.exhaustedNo credits remaining When balance reaches 0
Webhook Payload
All webhooks include a consistent structure:
{
"id" : "evt_abc123xyz" ,
"type" : "video.completed" ,
"created_at" : "2025-11-03T10:31:45Z" ,
"data" : {
// Event-specific data
}
}
Example: Video Completed
{
"id" : "evt_abc123xyz" ,
"type" : "video.completed" ,
"created_at" : "2025-11-03T10:31:45Z" ,
"data" : {
"id" : "batch_abc123" ,
"status" : "completed" ,
"template_id" : "meme-dialogue" ,
"url" : "https://cdn.getbluma.com/videos/batch_abc123.mp4" ,
"thumbnail_url" : "https://cdn.getbluma.com/thumbnails/batch_abc123.jpg" ,
"duration" : 45 ,
"size_bytes" : 8453120 ,
"credits_consumed" : 6
}
}
Example: Video Failed
{
"id" : "evt_xyz789def" ,
"type" : "video.failed" ,
"created_at" : "2025-11-03T10:35:00Z" ,
"data" : {
"id" : "batch_xyz789" ,
"status" : "failed" ,
"template_id" : "ugc-text-overlay" ,
"error" : {
"type" : "rendering_failed" ,
"detail" : "Invalid asset URL provided in context"
}
}
}
Registering Webhooks
Create a Webhook
curl -X POST https://api.getbluma.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/bluma",
"events": ["video.completed", "video.failed"]
}'
{
"id" : "webhook_xyz789" ,
"url" : "https://yourapp.com/webhooks/bluma" ,
"events" : [ "video.completed" , "video.failed" ],
"secret" : "whsec_a1b2c3d4e5f6..." ,
"is_active" : true ,
"created_at" : "2025-11-03T10:30:00Z" ,
"warning" : "Save the webhook secret now. You will need it to verify webhook signatures."
}
The webhook secret is shown only once during creation. Save it securely - you’ll need it to verify webhook signatures.
List Your Webhooks
curl https://api.getbluma.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY"
Delete a Webhook
curl -X DELETE https://api.getbluma.com/api/v1/webhooks/webhook_xyz789 \
-H "Authorization: Bearer YOUR_API_KEY"
Security
Verifying Signatures
All webhooks include an X-Bluma-Signature header with an HMAC-SHA256 signature. Always verify this signature to ensure the webhook is genuine.
Node.js Example
import crypto from 'crypto' ;
import express from 'express' ;
const WEBHOOK_SECRET = process . env . BLUMA_WEBHOOK_SECRET ;
app . post ( '/webhooks/bluma' , express . raw ({ type: 'application/json' }), ( req , res ) => {
const signature = req . headers [ 'x-bluma-signature' ] as string ;
const payload = req . body . toString ();
// Verify signature
const expectedSignature = crypto
. createHmac ( 'sha256' , WEBHOOK_SECRET )
. update ( payload )
. digest ( 'hex' );
if ( signature !== `sha256= ${ expectedSignature } ` ) {
console . error ( 'Invalid webhook signature!' );
return res . status ( 401 ). send ( 'Invalid signature' );
}
// Signature verified - process the event
const event = JSON . parse ( payload );
switch ( event . type ) {
case 'video.completed' :
console . log ( 'Video ready:' , event . data . url );
// Download video, notify user, etc.
break ;
case 'video.failed' :
console . error ( 'Video failed:' , event . data . error );
// Alert user, retry, etc.
break ;
}
res . sendStatus ( 200 );
});
Python Example
import hmac
import hashlib
from flask import Flask, request
WEBHOOK_SECRET = os.getenv( 'BLUMA_WEBHOOK_SECRET' )
app = Flask( __name__ )
@app.route ( '/webhooks/bluma' , methods = [ 'POST' ])
def webhook ():
signature = request.headers.get( 'X-Bluma-Signature' )
payload = request.get_data()
# Verify signature
expected_signature = hmac.new(
WEBHOOK_SECRET .encode(),
payload,
hashlib.sha256
).hexdigest()
if signature != f 'sha256= { expected_signature } ' :
print ( 'Invalid webhook signature!' )
return 'Invalid signature' , 401
# Signature verified - process event
event = request.get_json()
if event[ 'type' ] == 'video.completed' :
print ( f "Video ready: { event[ 'data' ][ 'url' ] } " )
# Download video, notify user, etc.
elif event[ 'type' ] == 'video.failed' :
print ( f "Video failed: { event[ 'data' ][ 'error' ] } " )
# Alert user, retry, etc.
return '' , 200
Each webhook request includes:
POST /webhooks/bluma HTTP/1.1
Host: yourapp.com
Content-Type: application/json
X-Bluma-Signature: sha256=abc123...
X-Bluma-Event-Id: evt_abc123xyz
X-Bluma-Event-Type: video.completed
User-Agent: Bluma-Webhooks/1.0
Delivery & Retries
Retry Schedule
If your endpoint fails to respond with a 2xx status code:
First retry: 3 seconds
Second retry: 30 seconds
Third retry: 5 minutes
Fourth retry: 1 hour
After 4 failed attempts, the webhook delivery is marked as failed.
Automatic Disabling
Webhooks are automatically disabled after 10 consecutive failures to prevent unnecessary load. Re-enable them once your endpoint is fixed.
Monitoring Deliveries
Check webhook delivery logs:
curl https://api.getbluma.com/api/v1/webhooks/webhook_xyz789/deliveries \
-H "Authorization: Bearer YOUR_API_KEY"
{
"deliveries" : [
{
"id" : "delivery_abc123" ,
"event_id" : "evt_abc123xyz" ,
"event_type" : "video.completed" ,
"attempt_number" : 1 ,
"status_code" : 200 ,
"duration_ms" : 145 ,
"error_message" : null ,
"created_at" : "2025-11-03T10:31:45Z"
},
{
"id" : "delivery_xyz789" ,
"event_id" : "evt_xyz789def" ,
"event_type" : "video.completed" ,
"attempt_number" : 2 ,
"status_code" : 500 ,
"duration_ms" : 1203 ,
"error_message" : "Connection timeout" ,
"next_retry_at" : "2025-11-03T10:35:00Z" ,
"created_at" : "2025-11-03T10:32:00Z"
}
]
}
Best Practices
Respond Quickly Return a 200 status immediately, then process the event asynchronously
Handle Duplicates Use the event_id to deduplicate events (store processed IDs)
Verify Signatures Always validate the HMAC signature before processing
Monitor Failures Track delivery failures and fix issues promptly to avoid auto-disable
Idempotency Example
const processedEvents = new Set ();
app . post ( '/webhooks/bluma' , async ( req , res ) => {
const event = req . body ;
// Check if already processed
if ( processedEvents . has ( event . id )) {
console . log ( 'Duplicate event, skipping' );
return res . sendStatus ( 200 );
}
// Process event
await handleEvent ( event );
// Mark as processed
processedEvents . add ( event . id );
res . sendStatus ( 200 );
});
Testing Webhooks
Using webhook.site
Create a temporary endpoint at webhook.site
Register the URL as your webhook endpoint
Trigger an event (generate a test video)
View the webhook payload in real-time
Local Testing with ngrok
# Start your local server
npm start # or python app.py
# Expose it publicly
ngrok http 3000
# Use the ngrok URL as your webhook endpoint
curl -X POST https://api.getbluma.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"url": "https://abc123.ngrok.io/webhooks/bluma",
"events": ["video.completed"]
}'
Troubleshooting
Webhooks not being delivered
Possible causes:
Your endpoint is returning non-2xx status codes
Firewall blocking Bluma’s servers
SSL certificate issues
Solutions:
Check delivery logs for error messages
Verify your endpoint returns 200 OK
Ensure HTTPS is properly configured
Receiving duplicate events
This is normal behavior. Implement idempotency using the event_id to safely handle duplicates.
Signature verification failing
Check that:
You’re using the correct webhook secret
You’re computing the HMAC on the raw request body (not parsed JSON)
The secret hasn’t been regenerated or changed
Webhook was auto-disabled
Fix the issues causing failures, then re-enable by creating a new webhook subscription. Review delivery logs to identify root cause.
Next Steps