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

1

Register Endpoint

Configure a URL where Bluma should send event notifications
2

Event Occurs

An event happens (e.g., video completes generation)
3

Webhook Fired

Bluma sends an HTTP POST request to your endpoint
4

Process Event

Your server receives and processes the webhook payload

Supported Events

Video Events

EventDescriptionWhen Fired
video.queuedVideo generation startedImmediately after creation
video.processingVideo is being generatedWhen rendering begins
video.completedVideo is readyGeneration successful
video.failedVideo generation failedIf an error occurs
video.deletedVideo was deletedWhen user deletes video

Credit Events

EventDescriptionWhen Fired
credits.lowCredits running lowWhen balance < 10
credits.exhaustedNo credits remainingWhen 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

Webhook Headers

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:
  1. First retry: 3 seconds
  2. Second retry: 30 seconds
  3. Third retry: 5 minutes
  4. 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

  1. Create a temporary endpoint at webhook.site
  2. Register the URL as your webhook endpoint
  3. Trigger an event (generate a test video)
  4. 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

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
This is normal behavior. Implement idempotency using the event_id to safely handle duplicates.
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
Fix the issues causing failures, then re-enable by creating a new webhook subscription. Review delivery logs to identify root cause.

Next Steps