To integrate our Webhook service into your system, you need to provide a Webhook URL, a specific endpoint where we will send notifications when a video generation task completes. Each notification requires a signature to verify the data’s security and authenticity.

1. Getting Your Secret Key

You need a Secret key for signature verification. Follow these steps to get it:

  1. Go to the Webhook page and create a new webhook key.
  2. On the Webhook page, create a new webhook key.
  3. Copy and save this key. It will be used as the secret for verifying Webhook signatures.

2. Steps to Integrate Webhooks

  1. Configure Your Webhook URL In your video generation API request Post, provide your webhookUrl, for example:

      {
          "input": {}
          "webhookUrl": "https://url.to.your.app/api/pollo/webhook"
      }
    

    Please note that input is video generation parameters, which vary by model and should not be changed.

  2. Verify Webhook Signatures Each Webhook message will include the following HTTP header, which you need to use to verify the authenticity of the message:

    • X-Webhook-Id:A unique identifier for the Webhook message.
    • X-Webhook-Timestamp:The timestamp of when the message was sent.
    • X-Webhook-Signature:A Base64 encoded signature that you need to use to verify that the message has not been tampered with.
  3. Signature Algorithm The signature for the Webhook message is calculated using the HMAC-SHA-256 algorithm. Here’s how it works:

    • The content to be signed consists of webhook_idwebhook_timestamp and the request body,each separated by .
    • Use the Base64 encoded Secret along with the content to calculate the HMAC-SHA-256 signature.

    Code Implementation:

    const crypto = require('crypto');
    
    // Get the fields from the Header and the request body
    const webhookId = "xxx";
    const webhookTimestamp = "xxx";
    const body = JSON.stringify({}); // Request body content
    
    // Build the content to be signed
    const signedContent = `${webhookId}.${webhookTimestamp}.${body}`;
    
    // Get the Secret(Base64 encoded)
    const secret = "xxx";  // Obtain from https://pollo.ai/api-platform
    
    // Signing process
    const secretBytes = Buffer.from(secret, 'base64');
    const computedSigBase64 = crypto
      .createHmac('sha256', secretBytes)
      .update(signedContent)
      .digest('base64');
    
  4. Signature Verification

    Compare X-Webhook-Signature with computedSigBase64 to ensure the message’s signature is correct. The verification code is as follows:

    const expectedSignatures = ['xxx'];  // Get `X-Webhook-Signature` from Header
    const computedSigBase64 = "xxx";  // The signature calculated above
    
    const isValid = expectedSignatures.some(expectedSig => {
      if (expectedSig.length !== computedSigBase64.length) {
          return false;
      }
      const expectedBuffer = Buffer.from(expectedSig, 'base64');
      const computedBuffer = Buffer.from(computedSigBase64, 'base64');
      return crypto.timingSafeEqual(expectedBuffer, computedBuffer);
    });
    
    if (!isValid) {
      // Invalid signature, reject processing
      console.log("Invalid signature");
    }
    
  5. Event Notification Structure

    The structure of the event notification sent by the Webhook is as follows:

    {
      "taskId": "<string>",
      "status": "succeed/failed"
    }
    
    • taskId: A unique identifier for the task.
    • status: The status of the task, which can be either succeed or failed.
  6. Retry Mechanism

    If the received Webhook notification shows a processing failure, the system will automatically retry. The retries will be performed at certain intervals, up to a maximum of 10 attempts. If all attempts fail, the system will stop retrying until you intervene manually.

3. Example Code

3.1 Using JavaScript to Integrate Webhook

const http = require('http');
const crypto = require('crypto');

http.createServer((req, res) => {
  const webhookId = req.headers['x-webhook-id'];
  const webhookTimestamp = req.headers['x-webhook-timestamp'];
  const signature = req.headers['x-webhook-signature'];
  let body = '';

  req.on('data', chunk => {
    body += chunk;
  });

  req.on('end', () => {
    // Obtain Secret
    const secret = "xxx"; // Obtain from https://pollo.ai/api-platform
    const signedContent = `${webhookId}.${webhookTimestamp}.${body}`;

    // Calculate the signature
    const secretBytes = Buffer.from(secret, 'base64');
    const computedSigBase64 = crypto
      .createHmac('sha256', secretBytes)
      .update(signedContent)
      .digest('base64');

    // Verify the signature
    if (crypto.timingSafeEqual(Buffer.from(signature, 'base64'), Buffer.from(computedSigBase64, 'base64'))) {
      // Signature is valid, process the message
      const event = JSON.parse(body);
      if (event.status === 'succeed') {
        // Handle success event
        console.log('Task succeeded:', event.taskId);
      } else {
        // Handle success event
        console.log('Task failed:', event.taskId);
      }
    } else {
      // Signature verification failed, reject processing
      console.log('Invalid signature');
    }
    res.end('OK');
  });
}).listen(8080);

3.2 Using Python to Integrate Webhook

import hmac
import hashlib
import base64
from http.server import BaseHTTPRequestHandler, HTTPServer

class WebhookHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        webhook_id = self.headers['X-Webhook-Id']
        webhook_timestamp = self.headers['X-Webhook-Timestamp']
        signature = self.headers['X-Webhook-Signature']
        content_length = int(self.headers['Content-Length'])
        body = self.rfile.read(content_length).decode('utf-8')

        # Get the Secret
        secret = "xxx"  # Obtain from https://pollo.ai/api-platform
        signed_content = f"{webhook_id}.{webhook_timestamp}.{body}"

        # Calculate the signature
        secret_bytes = base64.b64decode(secret)
        computed_sig_base64 = base64.b64encode(
            hmac.new(secret_bytes, signed_content.encode('utf-8'), hashlib.sha256).digest()
        ).decode('utf-8')

        # Verify the signature
        if hmac.compare_digest(signature, computed_sig_base64):
            # Signature is valid, process the message
            event = json.loads(body)
            if event['status'] == 'succeed':
                print(f"Task succeeded: {event['taskId']}")
            else:
                print(f"Task failed: {event['taskId']}")
        else:
            print("Invalid signature")

        self.send_response(200)
        self.end_headers()

if __name__ == '__main__':
    server = HTTPServer(('localhost', 8080), WebhookHandler)
    server.serve_forever()