Webhooks

Receive real-time notifications when your tasks complete using webhooks. Learn how to configure, secure, and handle webhook deliveries from Runware.

Overview

Webhooks allow you to receive real-time HTTP POST notifications when your generation tasks complete, eliminating the need to poll for results. When a task finishes processing, Runware sends the complete response to your specified URL automatically.

This is especially useful for long-running operations like video generation, batch processing where tasks complete at different times, or integrations with external systems that need immediate notifications.

For an alternative approach using client-side polling, see Task Polling .

How it works

You provide a webhook URL when submitting a task. Runware processes your request asynchronously, and when the task completes, sends an HTTP POST request to your URL with the complete task response as JSON. Your endpoint must respond with a 2xx status code to confirm receipt. If it doesn't, Runware will retry delivery automatically (see Retry behavior ).

For batch requests with multiple results, each completed item triggers a separate webhook call as results become available.

Configuration

Include the webhookURL parameter in your API request:

{
  "taskType": "imageInference",
  "taskUUID": "a770f077-f413-47de-9dac-be0b26a35da6",
  "model": "runware:100@1",
  "positivePrompt": "a serene landscape",
  "width": 1024,
  "height": 1024,
  "webhookURL": "https://api.example.com/webhooks/runware"
}

Authentication

Webhooks support authentication through URL parameters. You can include tokens, API keys, or custom tracking parameters directly in the webhook URL:

https://api.example.com/webhooks/runware?token=your_auth_token
https://api.example.com/webhooks/runware?apiKey=sk_live_abc123
https://api.example.com/webhooks/runware?projectId=proj_789&userId=12345

Always use HTTPS for webhook URLs to ensure data transmission is encrypted. Validate authentication parameters in your endpoint before processing webhook data.

Receiving notifications

The webhook POST body contains the complete JSON response for your task. The structure matches the standard API response for the corresponding task type:

{
  "taskType": "imageInference",
  "taskUUID": "a770f077-f413-47de-9dac-be0b26a35da6",
  "imageUUID": "550e8400-e29b-41d4-a716-446655440000",
  "imageURL": "https://im.runware.ai/image/ws/0.5/ii/550e8400-e29b-41d4-a716-446655440000.jpg",
  "model": "runware:100@1",
  "width": 1024,
  "height": 1024,
  "cost": 0.004,
  "createdAt": "2026-01-16T10:30:45Z"
}

Building your endpoint

Your webhook endpoint must respond with an HTTP status code between 200-299 to confirm receipt. The response should be returned quickly, ideally within 5 seconds. If you need to perform heavy processing, respond immediately with a 200 status and handle the work asynchronously.

const express = require('express');
    const app = express();

    app.use(express.json());

    app.post('/webhooks/runware', (req, res) => {
      const webhookData = req.body;
      
      // Verify authentication
      if (req.query.token !== process.env.WEBHOOK_TOKEN) {
        return res.status(401).send('Unauthorized');
      }

      // Process asynchronously
      processWebhook(webhookData).catch(console.error);
      
      // Respond immediately
      res.status(200).send('OK');
    });

    async function processWebhook(data) {
      console.log('Task completed:', data.taskUUID);
      // Handle the completed task...
    }

    app.listen(3000);
from flask import Flask, request
    import os

    app = Flask(__name__)

    @app.route('/webhooks/runware', methods=['POST'])
    def webhook():
        if request.args.get('token') != os.environ.get('WEBHOOK_TOKEN'):
            return 'Unauthorized', 401
        
        webhook_data = request.json
        process_webhook(webhook_data)
        
        return 'OK', 200

    def process_webhook(data):
        print(f"Task completed: {data['taskUUID']}")
        # Handle the completed task...

    if __name__ == '__main__':
        app.run(port=3000)
<?php
    if ($_GET['token'] !== getenv('WEBHOOK_TOKEN')) {
        http_response_code(401);
        exit('Unauthorized');
    }

    $webhookData = json_decode(file_get_contents('php://input'), true);
    processWebhook($webhookData);

    http_response_code(200);
    echo 'OK';

    function processWebhook($data) {
        error_log("Task completed: " . $data['taskUUID']);
        // Handle the completed task...
    }

Retry behavior

If webhook delivery fails, Runware automatically retries with an exponential backoff strategy. The first retry happens after 250ms, with each subsequent retry doubling the previous delay. Random jitter up to 20% is added to prevent collision when multiple webhooks retry simultaneously. The delay increases until reaching a maximum of 8 seconds, and retries stop as soon as a 2xx response is received.

Example retry timing: 250ms, 500ms, 1s, 2s, 4s, then 8s for subsequent attempts.

Testing locally

For local development, use ngrok to expose your local server:

# Start your local server
npm start

# In another terminal, start ngrok
ngrok http 3000

# Use the ngrok URL as your webhook
https://abc123.ngrok.io/webhooks/runware

You can also simulate webhook delivery with curl:

curl -X POST "http://localhost:3000/webhooks/runware?token=test" \
-H "Content-Type: application/json" \
-d '{"taskType":"imageInference","taskUUID":"test-uuid"}'

Handling duplicates

Webhooks may occasionally be delivered more than once due to network issues or retries. Design your endpoint to handle duplicates by tracking processed task UUIDs:

const processed = new Set();

app.post('/webhooks/runware', (req, res) => {
const { taskUUID } = req.body;

if (processed.has(taskUUID)) {
  return res.status(200).send('Already processed');
}

processed.add(taskUUID);
processWebhook(req.body);
res.status(200).send('OK');
});

Troubleshooting

If webhooks aren't being received, verify your endpoint is publicly accessible and responding with 2xx status codes. Check server logs for incoming requests and ensure SSL certificates are valid for HTTPS endpoints. For local development, use ngrok to test connectivity.

If your endpoint times out, make sure you're responding immediately with a 200 status and moving heavy processing to background jobs. Webhook delivery is considered successful only when your endpoint returns a 2xx response within the timeout window.