Webhook Events
Complete reference of all webhook event types and their payload structures.
Event Structure
All webhook events follow this structure:
interface WebhookEvent<T> {topic: string; // Event type (e.g., "render.completed")data: T; // Event-specific payload}
The webhook is delivered as an HTTP POST request with:
- Headers:
Content-Type: application/json,X-Webhook-Signature: <hmac> - Body: JSON-encoded webhook event
Render Events
render.created
Triggered when a render job is created.
interface RenderCreatedPayload {id: string; // Render UUIDstatus: "created"; // Always "created" for this eventcreated_at: string; // ISO 8601 timestampcompleted_at: null; // Not completed yetfailed_at: null; // Not failed yetwidth: number; // Video width in pixelsheight: number; // Video height in pixelsfps: number; // Frames per secondbyte_size: null; // Not available until completedduration_ms: null; // Not available until completedmd5: null; // Not available until completedmetadata: object | null; // Custom metadata from requestdownload_url: null; // Not available until completed}
Example:
{"topic": "render.created","data": {"id": "550e8400-e29b-41d4-a716-446655440000","status": "created","created_at": "2024-10-30T10:00:00Z","completed_at": null,"failed_at": null,"width": 1920,"height": 1080,"fps": 30,"byte_size": null,"duration_ms": null,"md5": null,"metadata": { "project_id": "abc123" },"download_url": null}}
render.pending
Triggered when a render is queued for processing.
Payload structure is identical to render.created, with status: "pending".
render.rendering
Triggered when a render begins active processing.
Payload structure is identical to render.created, with status: "rendering".
render.completed
Triggered when a render successfully finishes.
interface RenderCompletedPayload {id: string; // Render UUIDstatus: "complete"; // Always "complete" for this eventcreated_at: string; // ISO 8601 timestampcompleted_at: string; // ISO 8601 timestamp of completionfailed_at: null; // Not failedwidth: number; // Video width in pixelsheight: number; // Video height in pixelsfps: number; // Frames per secondbyte_size: number; // File size in bytesduration_ms: number; // Video duration in millisecondsmd5: string; // MD5 hash of video filemetadata: object | null; // Custom metadata from requestdownload_url: string; // Signed URL to download video}
Example:
{"topic": "render.completed","data": {"id": "550e8400-e29b-41d4-a716-446655440000","status": "complete","created_at": "2024-10-30T10:00:00Z","completed_at": "2024-10-30T10:01:23Z","failed_at": null,"width": 1920,"height": 1080,"fps": 30,"byte_size": 15728640,"duration_ms": 5000,"md5": "098f6bcd4621d373cade4e832627b4f6","metadata": { "project_id": "abc123" },"download_url": "https://editframe.com/api/v1/renders/550e8400-e29b-41d4-a716-446655440000/mp4"}}
Usage:
if (event.topic === "render.completed") {const { id, download_url, duration_ms, byte_size } = event.data;console.log(`Render ${id} completed:`);console.log(`- Duration: ${duration_ms / 1000}s`);console.log(`- Size: ${(byte_size / 1024 / 1024).toFixed(2)} MB`);// Download the videoconst response = await fetch(download_url);const videoBuffer = await response.arrayBuffer();// Save or upload to your storageawait saveVideo(id, videoBuffer);}
render.failed
Triggered when a render encounters an error.
interface RenderFailedPayload {id: string; // Render UUIDstatus: "failed"; // Always "failed" for this eventcreated_at: string; // ISO 8601 timestampcompleted_at: null; // Not completedfailed_at: string; // ISO 8601 timestamp of failurewidth: number; // Video width in pixelsheight: number; // Video height in pixelsfps: number; // Frames per secondbyte_size: null; // Not availableduration_ms: null; // Not availablemd5: null; // Not availablemetadata: object | null; // Custom metadata from requestdownload_url: null; // Not available}
Example:
{"topic": "render.failed","data": {"id": "550e8400-e29b-41d4-a716-446655440000","status": "failed","created_at": "2024-10-30T10:00:00Z","completed_at": null,"failed_at": "2024-10-30T10:00:45Z","width": 1920,"height": 1080,"fps": 30,"byte_size": null,"duration_ms": null,"md5": null,"metadata": { "project_id": "abc123" },"download_url": null}}
File Events
file.created
Triggered when a file record is created (before upload).
interface FileCreatedPayload {id: string; // File UUIDtype: "video" | "image" | "caption"; // File typestatus: "created"; // Always "created" for this eventfilename: string; // Original filenamebyte_size: null; // Not uploaded yetmd5: null; // Not uploaded yetmime_type: null; // Not available yetwidth: null; // Not available yet (images only)height: null; // Not available yet (images only)}
Example:
{"topic": "file.created","data": {"id": "660e8400-e29b-41d4-a716-446655440001","type": "video","status": "created","filename": "my-video.mp4","byte_size": null,"md5": null,"mime_type": null,"width": null,"height": null}}
file.uploading
Triggered when file upload is in progress.
Payload structure similar to file.created, with status: "uploading".
file.processing
Triggered when a video file is being processed to ISOBMFF format.
Only sent for type: "video" files.
interface FileProcessingPayload {id: string; // File UUIDtype: "video"; // Always "video" for processingstatus: "processing"; // Always "processing" for this eventfilename: string; // Original filenamebyte_size: number; // Uploaded file sizemd5: string; // MD5 hash of uploaded filemime_type: string; // MIME type (e.g., "video/mp4")width: number | null; // Video width in pixelsheight: number | null; // Video height in pixels}
file.ready
Triggered when a file is ready for use in compositions.
interface FileReadyPayload {id: string; // File UUIDtype: "video" | "image" | "caption"; // File typestatus: "ready"; // Always "ready" for this eventfilename: string; // Original filenamebyte_size: number; // File size in bytesmd5: string; // MD5 hash of filemime_type: string; // MIME typewidth: number | null; // Width in pixels (images/video only)height: number | null; // Height in pixels (images/video only)}
Example:
{"topic": "file.ready","data": {"id": "660e8400-e29b-41d4-a716-446655440001","type": "video","status": "ready","filename": "my-video.mp4","byte_size": 52428800,"md5": "5d41402abc4b2a76b9719d911017c592","mime_type": "video/mp4","width": 1920,"height": 1080}}
Usage:
if (event.topic === "file.ready") {const { id, type, filename } = event.data;console.log(`File ${filename} (${type}) is ready`);console.log(`Use in composition: <ef-${type} file-id="${id}"></${type}>`);// Update database recordawait db.updateFile(id, { status: "ready", processedAt: new Date() });}
file.failed
Triggered when file upload or processing fails.
Payload structure similar to other file events, with status: "failed".
Legacy Events
unprocessed_file.created
Deprecated: Use file.created instead.
Triggered when an unprocessed file is created.
interface UnprocessedFileCreatedPayload {id: string; // File UUIDbyte_size: number; // File size in bytesnext_byte: number; // Upload progressmd5: string | null; // MD5 hash if completedfilename: string; // Original filenamecompleted_at: string | null; // Completion timestamp}
Webhook Test Event
webhook.test
Sent when you trigger a test webhook from the dashboard.
{"topic": "webhook.test","data": {"id": "your-api-key-id","org_id": "your-org-id"}}
Use this to verify your webhook endpoint is correctly configured.
Event Ordering
Events are sent in chronological order, but network issues or retries may cause events to arrive out of order. Use timestamps (created_at, completed_at, etc.) to determine actual event sequence.
Example render lifecycle:
render.created(status: "created")render.pending(status: "pending")render.rendering(status: "rendering")render.completed(status: "complete") ORrender.failed(status: "failed")
Subscribing to Events
Configure which events you receive when creating or updating an API key:
const apiKey = await createApiKey({name: "Production",webhookUrl: "https://api.yourapp.com/webhooks",webhookEvents: ["render.completed","render.failed","file.ready","file.failed"]});
Recommendation: Subscribe only to events you need to reduce unnecessary webhook traffic.
Next Steps
- security.md — Verify webhook signatures
- testing.md — Test webhook payloads locally
- troubleshooting.md — Debug webhook issues