TutorialsAdvancedWebhook Configuration
IntermediateUpdated Dec 10, 2025

Webhook Configuration

Receive async results for long-running tasks with secure webhook delivery and HMAC signature verification.

MP
Maya Patel
API Architect
7 min read

Why Webhooks?

Long-running tasks (video generation, batch processing) can take minutes. Instead of polling, webhooks push results to your server when ready.

Setting Up a Webhook Endpoint

Express.js Example

javascript
const express = require('express');
const crypto = require('crypto');

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

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

app.post('/webhook/abstrakt', (req, res) => {
  // Verify signature
  const signature = req.headers['x-abstrakt-signature'];
  const payload = JSON.stringify(req.body);
  
  const expectedSignature = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');
  
  if (signature !== expectedSignature) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Process the webhook
  const { request_id, status, result } = req.body;
  
  if (status === 'completed') {
    console.log(`Job ${request_id} completed!`);
    console.log(`Result: ${result.items[0].url}`);
    // Save to database, notify user, etc.
  }
  
  res.json({ received: true });
});

app.listen(3000);

Python Flask Example

python
from flask import Flask, request, jsonify
import hmac
import hashlib
import os

app = Flask(__name__)
WEBHOOK_SECRET = os.environ['WEBHOOK_SECRET']

def verify_signature(payload, signature):
    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.route('/webhook/abstrakt', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Abstrakt-Signature')
    
    if not verify_signature(request.data, signature):
        return jsonify({'error': 'Invalid signature'}), 401
    
    data = request.json
    
    if data['status'] == 'completed':
        # Process completed job
        save_result(data['request_id'], data['result'])
    
    return jsonify({'received': True})

Configuring Webhooks in API Calls

python
result = client.submit("fal-ai/minimax/video-01", {
    "input": {
        "prompt": "A beautiful sunset over the ocean"
    },
    "webhook_url": "https://your-server.com/webhook/abstrakt",
    "webhook_secret": "your_secret_here"
})

print(f"Job submitted: {result.request_id}")
# Result will be POSTed to your webhook when ready

Webhook Payload Format

json
{
  "request_id": "abc123",
  "status": "completed",
  "result": {
    "items": [
      {
        "url": "https://cdn.abstrakt.one/...",
        "content_type": "video/mp4"
      }
    ]
  },
  "metrics": {
    "inference_time": 45.2,
    "queue_time": 2.1
  },
  "timestamp": "2025-01-12T10:30:00Z"
}

Handling Failures

javascript
app.post('/webhook/abstrakt', (req, res) => {
  const { request_id, status, error } = req.body;
  
  if (status === 'failed') {
    console.error(`Job ${request_id} failed: ${error.message}`);
    // Retry logic, notify user, etc.
  }
  
  res.json({ received: true });
});

Retry Policy

If your webhook fails, we retry:

  1. Immediate retry
  2. 1 minute delay
  3. 5 minutes delay
  4. 15 minutes delay
  5. 1 hour delay (final)

Return 2xx status to acknowledge receipt.

Testing Webhooks Locally

Use ngrok for local development:

ngrok http 3000

Use the ngrok URL as your webhook endpoint during testing.

Dashboard Configuration

You can also configure webhooks in the dashboard:

  1. Go to SettingsWebhooks
  2. Add your endpoint URL
  3. Set a webhook secret
  4. Select events to receive

Best Practices

  1. Always verify signatures - Prevent spoofed requests
  2. Return quickly - Process async, respond fast
  3. Handle idempotency - Same webhook may arrive twice
  4. Log everything - Debug issues easily
  5. Use HTTPS - Secure transmission

Next Steps

#webhooks#async#api#integration