Webhooks

Get real-time notifications when generation jobs complete using webhooks

Overview

Webhooks allow you to receive HTTP callbacks when asynchronous operations complete, instead of polling the API. This is essential for long-running operations like video generation (5-15 minutes) or 3D generation (10-20 minutes).

Benefits

  • Eliminate polling and reduce API calls by 95%+
  • Get instant notifications when jobs complete
  • Scale efficiently with thousands of concurrent jobs
  • Automatic retry logic with exponential backoff

Setting Up Webhooks

1. Create Webhook Endpoint

First, create an endpoint in your application to receive webhook events:

// Express.js example
import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.json());

app.post('/api/webhooks/wazza', async (req, res) => {
  // 1. Verify webhook signature
  const signature = req.headers['x-wazza-signature'];
  const isValid = verifyWebhookSignature(
    req.body,
    signature,
    process.env.WAZZA_WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // 2. Process the webhook event
  const { event, jobId, status, output, error } = req.body;

  switch (event) {
    case 'job.completed':
      console.log(`Job ${jobId} completed`, output);
      // Update your database, notify users, etc.
      break;

    case 'job.failed':
      console.log(`Job ${jobId} failed`, error);
      // Handle failure, notify user, retry, etc.
      break;

    case 'job.processing':
      console.log(`Job ${jobId} is processing`);
      break;
  }

  // 3. Return 200 to acknowledge receipt
  res.status(200).json({ received: true });
});

function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  const digest = hmac.update(JSON.stringify(payload)).digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(digest)
  );
}

app.listen(3000);

2. Register Webhook URL

Register your webhook URL in the Wazza Console or via API:

// Register webhook via API
const response = await fetch('https://api.wazza.ai/v1/webhooks', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${WAZZA_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://your-domain.com/api/webhooks/wazza',
    events: ['job.completed', 'job.failed', 'job.processing'],
    secret: 'your-webhook-secret' // Used for signature verification
  })
});

const webhook = await response.json();
console.log('Webhook ID:', webhook.id);

3. Use Webhook in Requests

Include the webhook URL when creating generation jobs:

import WazzaEngine from '@wazza/engine';

const wazza = new WazzaEngine({
  apiKey: process.env.WAZZA_API_KEY
});

// Create job with webhook
const response = await wazza.generate({
  provider: 'openai',
  model: 'sora-2-pro',
  prompt: 'A cinematic video of a sunset over mountains',
  webhookUrl: 'https://your-domain.com/api/webhooks/wazza',
  webhookEvents: ['job.completed', 'job.failed']
});

console.log('Job created:', response.jobId);
// No need to poll - you'll receive a webhook when complete!

Webhook Events

Wazza Engine sends the following webhook events:

job.completed

Sent when a generation job completes successfully

{
  "event": "job.completed",
  "jobId": "job_abc123",
  "status": "completed",
  "output": {
    "url": "https://cdn.wazza.ai/outputs/abc123.mp4",
    "width": 1920,
    "height": 1080,
    "duration": 10,
    "format": "mp4"
  },
  "metadata": {
    "provider": "openai",
    "model": "sora-2-pro",
    "creditsUsed": 15
  },
  "timestamp": "2025-10-22T10:30:00Z"
}

job.failed

Sent when a generation job fails

{
  "event": "job.failed",
  "jobId": "job_abc123",
  "status": "failed",
  "error": {
    "code": "CONTENT_POLICY_VIOLATION",
    "message": "Content violates safety policy"
  },
  "timestamp": "2025-10-22T10:30:00Z"
}

job.processing

Sent periodically with progress updates (optional)

{
  "event": "job.processing",
  "jobId": "job_abc123",
  "status": "processing",
  "progress": {
    "percent": 45,
    "stage": "rendering",
    "estimatedTimeRemaining": 300
  },
  "timestamp": "2025-10-22T10:30:00Z"
}

Security

Signature Verification

Always verify webhook signatures to ensure requests come from Wazza:

import crypto from 'crypto';

function verifyWazzaWebhook(req) {
  const signature = req.headers['x-wazza-signature'];
  const timestamp = req.headers['x-wazza-timestamp'];
  const body = JSON.stringify(req.body);

  // 1. Check timestamp to prevent replay attacks
  const requestTime = parseInt(timestamp);
  const currentTime = Math.floor(Date.now() / 1000);
  if (Math.abs(currentTime - requestTime) > 300) { // 5 minutes
    throw new Error('Webhook timestamp too old');
  }

  // 2. Verify HMAC signature
  const payload = `${timestamp}.${body}`;
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
  const expectedSignature = hmac.update(payload).digest('hex');

  if (!crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  )) {
    throw new Error('Invalid webhook signature');
  }

  return true;
}

IP Allowlisting (Optional)

For additional security, restrict webhook requests to Wazza's IP ranges:

Wazza webhook IPs (staging and production):

52.89.214.238, 34.212.75.30, 54.218.53.128, 52.32.178.7

Retry Logic

Wazza automatically retries failed webhook deliveries with exponential backoff:

Attempt 1:Immediate
Attempt 2:30 seconds later
Attempt 3:5 minutes later
Attempt 4:30 minutes later
Attempt 5:2 hours later

After 5 failed attempts, the webhook is marked as failed and you'll receive an email notification. You can manually retry from the dashboard or fetch the job status via API.

Testing Webhooks

Local Testing with ngrok

# 1. Install ngrok
npm install -g ngrok

# 2. Start your local server
npm run dev

# 3. Create tunnel to localhost
ngrok http 3000

# 4. Use the ngrok URL as your webhook endpoint
# Example: https://abc123.ngrok.io/api/webhooks/wazza

Manual Testing

Trigger test webhook events from the Wazza Console:

  1. Go to Dashboard → Webhooks
  2. Select your webhook
  3. Click "Send Test Event"
  4. Choose event type (completed, failed, processing)
  5. Verify your endpoint receives and processes the event

Best Practices

1. Respond Quickly

  • Return 200 status within 5 seconds
  • Process events asynchronously (use job queue)
  • Don't make external API calls in webhook handler

2. Handle Duplicate Events

  • Webhooks may be delivered multiple times (idempotency)
  • Use jobId to deduplicate events
  • Store processed event IDs in database

3. Secure Your Endpoint

  • Always verify webhook signatures
  • Check timestamp to prevent replay attacks
  • Use HTTPS endpoints only
  • Consider IP allowlisting for sensitive data

4. Monitor Webhook Health

  • Track webhook delivery success rate
  • Set up alerts for failed deliveries
  • Log all webhook events for debugging
  • Use webhook dashboard to monitor status