Webhooks

Learn how to receive real-time notifications when your jobs complete.

What are webhooks?

Webhooks allow offload.run to notify your application when a job completes. Instead of polling for job status, you provide a URL endpoint and we'll send an HTTP POST request with the job results when processing finishes.

This is ideal for long-running tasks where you don't want to keep a connection open or repeatedly check job status.

Setting up webhooks

To receive webhook notifications, provide a webhookUrl and optionally a webhookSecret when enqueueing a job:

import { OffloadClient } from '@offload-run/sdk';

const offload = new OffloadClient({
  apiKey: process.env.OFFLOAD_API_KEY!,
});

await offload.enqueueJob({
  type: 'compressVideo',
  params: {
    url: 'https://example.com/video.mp4',
    quality: 'high',
  },
  webhookUrl: 'https://yourapp.com/api/webhooks/offload',
  webhookSecret: process.env.WEBHOOK_SECRET, // Optional, for signature verification
});

Webhook payload

When a job completes, we'll send a POST request to your webhook URL with the following JSON payload:

{
  "jobId": "job_abc123",
  "type": "compress-video",
  "status": "completed",
  "result": {
    "url": "https://storage.offload.run/compressed-video.mp4",
    "size": 1024000,
    "duration": 120
  },
  "createdAt": "2024-01-15T10:30:00Z",
  "completedAt": "2024-01-15T10:32:30Z"
}

If the job fails, the payload will include an error field:

{
  "jobId": "job_abc123",
  "type": "compress-video",
  "status": "failed",
  "error": {
    "code": "PROCESSING_ERROR",
    "message": "Failed to process video"
  },
  "createdAt": "2024-01-15T10:30:00Z",
  "failedAt": "2024-01-15T10:32:30Z"
}

Creating a webhook endpoint

The SDK provides a handleWebhook helper that automatically verifies signatures and parses the payload. Here's an example using Next.js:

// app/api/webhooks/offload/route.ts
import { NextResponse } from 'next/server';
import { handleWebhook } from '@offload-run/sdk';

export async function POST(request: Request) {
  try {
    // Verify signature and parse payload
    const payload = await handleWebhook(
      request,
      process.env.WEBHOOK_SECRET!
    );

    console.log('Job completed:', payload.id);

    if (payload.status === 'completed') {
      // Handle successful job - result fields are at root level
      console.log('Output URL:', payload.url);
      console.log('Compression ratio:', payload.compressionRatio);
      // Save to database, notify user, etc.
    } else if (payload.status === 'failed') {
      // Handle failed job
      console.error('Job failed:', payload.error);
    }

    return NextResponse.json({ received: true });
  } catch (error) {
    // Invalid signature or parsing error
    return NextResponse.json(
      { error: 'Invalid webhook' },
      { status: 401 }
    );
  }
}

Security

To verify that webhook requests are coming from offload.run, we include a signature in the x-offload-signature header and a timestamp in x-offload-timestamp. The SDK's handleWebhook function handles this automatically, but you can also verify manually:

import { verifyWebhookSignature } from '@offload-run/sdk';

// Manual verification (handleWebhook does this automatically)
const isValid = verifyWebhookSignature(
  rawBody,                              // Raw request body as string
  headers['x-offload-signature'],       // Signature header
  headers['x-offload-timestamp'],       // Timestamp header
  process.env.WEBHOOK_SECRET!           // Your webhook secret
);

The SDK also validates the timestamp (must be within 5 minutes) and uses constant-time comparison to prevent timing attacks.

Retry behavior

If your webhook endpoint returns a non-2xx status code or times out, we'll automatically retry the webhook delivery:

  • First retry: after 1 minute
  • Second retry: after 5 minutes
  • Third retry: after 30 minutes
  • Final retry: after 2 hours

After 4 failed attempts, we'll stop retrying. You can always retrieve the job result using the API.

Testing webhooks

During development, you can use tools like ngrok to expose your local server to the internet and receive webhook notifications.

# Start ngrok
ngrok http 3000

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