Table of Contents
- Webhook Guide
- Overview
- Webhook Events
- Job Status Events
- Security
- Webhook Signatures
- Signature Verification
- Implementation Examples
- Rails Controller
- Express.js Handler
- Django View
- Best Practices
- Reliability
- Security
- Error Handling
- Testing Webhooks
- Development Setup
- Use ngrok for local webhook testing
- Your webhook URL becomes:
- https://abc123.ngrok.io/webhooks/mediaconvert
- Test Webhook Delivery
- Send test webhook to your endpoint
- Webhook Debugging
- Webhook Configuration
- Setting Webhook URL
- Global Webhook Settings
- Troubleshooting
- Common Issues
- Next Steps
- Support
Webhook Guide
Real-time notifications for MediaConvert.io job events. Webhooks allow you to build event-driven applications that respond immediately to conversion completions, failures, and progress updates.
Overview
MediaConvert.io sends HTTP POST requests to your specified endpoint when job events occur. This enables real-time user notifications, automated workflows, and immediate processing of converted files.
Webhook Events
Job Status Events
Job Completed
{
"job_id": "job_abc123xyz",
"status": "completed",
"job_type": "video",
"progress_percentage": 100,
"cost_micros": 1575000,
"cost_cents": 158,
"processing_time_seconds": 45,
"output_size_bytes": 8192000,
"input_format": "mp4",
"output_format": "webm",
"created_at": "2024-01-15T10:30:00Z",
"completed_at": "2024-01-15T10:31:15Z"
}
Job Failed
{
"job_id": "job_def456xyz",
"status": "failed",
"job_type": "video",
"progress_percentage": 25,
"error_message": "Input file corrupted or unreadable",
"error_code": "INVALID_INPUT",
"created_at": "2024-01-15T10:30:00Z",
"failed_at": "2024-01-15T10:30:45Z"
}
Job Progress (Optional)
{
"job_id": "job_ghi789xyz",
"status": "processing",
"progress_percentage": 67,
"estimated_completion": "2024-01-15T10:32:00Z"
}
Security
Webhook Signatures
All webhooks include a signature header for verification:
X-MediaConvert-Signature: sha256=abc123def456...
Signature Verification
Ruby Example:
```ruby
def verify_webhook_signature(payload, signature, secret)
expected = OpenSSL::HMAC.hexdigest('sha256', secret, payload)
expected_with_prefix = sha256=#{expected}
Rack::Utils.secure_compare(signature, expected_with_prefix)
end
```
Python Example:
```python
import hashlib
import hmac
def verify_webhook_signature(payload, signature, secret):
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
expected_with_prefix = fsha256={expected}
return hmac.compare_digest(signature, expected_with_prefix)
**JavaScript Example:**
```javascript
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const expectedWithPrefix = `sha256=${expected}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedWithPrefix)
);
}
Implementation Examples
Rails Controller
class WebhooksController < ApplicationController
protect_from_forgery except: [:mediaconvert]
def mediaconvert
unless verify_signature(request.raw_post, request.headers['X-MediaConvert-Signature'])
head :unauthorized
return
end
webhook_data = JSON.parse(request.raw_post)
case webhook_data['status']
when 'completed'
handle_completed_job(webhook_data)
when 'failed'
handle_failed_job(webhook_data)
when 'processing'
update_job_progress(webhook_data)
end
head :ok
end
private
def verify_signature(payload, signature)
return false unless signature
expected = OpenSSL::HMAC.hexdigest(
'sha256',
ENV['MEDIACONVERT_WEBHOOK_SECRET'],
payload
)
expected_with_prefix = "sha256=#{expected}"
Rack::Utils.secure_compare(signature, expected_with_prefix)
end
def handle_completed_job(data)
job = ConversionJob.find_by(external_id: data['job_id'])
return unless job
job.update!(
status: 'completed',
progress: 100,
cost_micros: data['cost_micros'],
completed_at: Time.current
)
# Notify user
ConversionCompleteJob.perform_later(job.user, job)
# Process converted file
ProcessConvertedFileJob.perform_later(job)
end
def handle_failed_job(data)
job = ConversionJob.find_by(external_id: data['job_id'])
return unless job
job.update!(
status: 'failed',
error_message: data['error_message'],
failed_at: Time.current
)
# Notify user of failure
ConversionFailedJob.perform_later(job.user, job, data['error_message'])
end
end
Express.js Handler
const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware to capture raw body for signature verification
app.use('/webhooks/mediaconvert', express.raw({type: 'application/json'}));
app.post('/webhooks/mediaconvert', (req, res) => {
const signature = req.get('X-MediaConvert-Signature');
const payload = req.body;
// Verify signature
if (!verifyWebhookSignature(payload, signature, process.env.MEDIACONVERT_WEBHOOK_SECRET)) {
return res.status(401).send('Unauthorized');
}
const data = JSON.parse(payload);
switch (data.status) {
case 'completed':
handleCompletedJob(data);
break;
case 'failed':
handleFailedJob(data);
break;
case 'processing':
updateJobProgress(data);
break;
}
res.status(200).send('OK');
});
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const expectedWithPrefix = `sha256=${expected}`;
return crypto.timingSafeEqual(
Buffer.from(signature || ''),
Buffer.from(expectedWithPrefix)
);
}
Django View
import json
import hashlib
import hmac
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
@csrf_exempt
@require_http_methods(["POST"])
def mediaconvert_webhook(request):
signature = request.META.get('HTTP_X_MEDIACONVERT_SIGNATURE')
payload = request.body
# Verify signature
if not verify_webhook_signature(payload, signature, settings.MEDIACONVERT_WEBHOOK_SECRET):
return HttpResponse(status=401)
data = json.loads(payload.decode('utf-8'))
if data['status'] == 'completed':
handle_completed_job(data)
elif data['status'] == 'failed':
handle_failed_job(data)
elif data['status'] == 'processing':
update_job_progress(data)
return HttpResponse(status=200)
def verify_webhook_signature(payload, signature, secret):
if not signature:
return False
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
expected_with_prefix = f"sha256={expected}"
return hmac.compare_digest(signature, expected_with_prefix)
Best Practices
Reliability
- Idempotency: Handle duplicate webhooks gracefully
- Timeouts: Respond within 10 seconds to avoid retries
- Status Codes: Return 2xx for successful processing
- Logging: Log all webhook events for debugging
Security
- Always verify signatures before processing webhooks
- Use HTTPS endpoints to protect webhook data in transit
- Validate payload structure before processing
- Rate limiting to prevent webhook abuse
Error Handling
- Return 2xx even for business logic failures to prevent retries
- Queue failed processing for later retry
- Monitor webhook endpoint health and failures
- Alert on signature verification failures
Testing Webhooks
Development Setup
# Use ngrok for local webhook testing
ngrok http 3000
# Your webhook URL becomes:
# https://abc123.ngrok.io/webhooks/mediaconvert
Test Webhook Delivery
# Send test webhook to your endpoint
curl -X POST "https://your-app.com/webhooks/mediaconvert" \
-H "Content-Type: application/json" \
-H "X-MediaConvert-Signature: sha256=test_signature" \
-d '{
"job_id": "job_test123",
"status": "completed",
"progress_percentage": 100,
"cost_micros": 150000,
"processing_time_seconds": 45
}'
Webhook Debugging
- Check logs for signature verification failures
- Validate JSON parsing doesn't fail on malformed data
- Test timeout handling with slow webhook endpoints
- Monitor delivery success rates in your application
Webhook Configuration
Setting Webhook URL
Specify webhook URL when creating conversion jobs:
json
{
"job_type": "video",
"input_url": "https://...",
"output_url": "https://...",
"webhook_url": "https://your-app.com/webhooks/mediaconvert"
}
Global Webhook Settings
Configure account-wide webhook preferences in your dashboard:
- Default webhook URL
- Event type preferences
- Retry configuration
- Delivery timeout settings
Troubleshooting
Common Issues
Signature Verification Failures
- Check webhook secret configuration
- Ensure raw request body is used for verification
- Verify HMAC calculation implementation
Missing Webhooks
- Check endpoint availability and response time
- Verify HTTPS certificate validity
- Monitor for rate limiting or blocking
Duplicate Processing
- Implement idempotency keys
- Check for webhook retry logic
- Use database constraints to prevent duplicates
Next Steps
- Code Examples - Ready-to-use webhook implementations
- API Reference - Complete webhook event documentation
- Authentication Guide - Secure API integration
Support
- Email Support: support@mediaconvert.io
- Webhook Testing: Use our webhook testing tools in the dashboard
- Status Page: status.mediaconvert.io for webhook delivery status