Webhooks

Receive notifications when renders complete and files finish processing.

Webhooks push event notifications to your server so you don't need to poll.

Configure a webhook

Webhooks are configured in Dashboard → Settings → Webhooks. Add your endpoint URL and select the events you want to receive.

Supported events

EventFired when
render.createdA render job is accepted and queued
render.pendingA render job is waiting to start
render.renderingFrame capture has begun
render.completedA render job finishes successfully
render.failedA render job fails
file.createdA file record is created
file.uploadingFile upload is in progress
file.processingFile is being processed
file.readyFile finishes processing successfully
file.failedFile processing fails
file.updatedA file's status changes to an unlisted value
unprocessed_file.createdA raw upload is received

Payload shape

All payloads include topic and data containing the relevant resource. Example for render.completed:

{
  "topic": "render.completed",
  "data": {
    "id": "rnd_abc123",
    "status": "completed",
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "created_at": "2024-01-15T10:30:00Z",
    "completed_at": "2024-01-15T10:30:00Z",
    "failed_at": null,
    "byte_size": 15728640,
    "duration_ms": 5000,
    "md5": "d41d8cd98f00b204e9800998ecf8427e",
    "metadata": null,
    "download_url": "https://cdn.editframe.com/renders/rnd_abc123/output.mp4"
  }
}

Verifying signatures

Always verify the signature before processing webhook payloads.

Every webhook request includes an X-Webhook-Signature header. The value is an HMAC-SHA256 of the raw request body, hex-encoded, keyed with your webhook secret (shown in the Dashboard when you create the webhook).

import crypto from "crypto";

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex")
  );
}

// Express example
app.post("/webhooks/editframe", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.headers["x-webhook-signature"];
  const valid = verifyWebhook(req.body, sig, process.env.EDITFRAME_WEBHOOK_SECRET);

  if (!valid) return res.status(401).send("Invalid signature");

  const payload = JSON.parse(req.body.toString());
  console.log(payload.topic, payload.data?.id);

  res.sendStatus(200);
});

Use express.raw() (not express.json()) to preserve the exact bytes used to compute the signature.

Retry behavior

Editframe retries webhook deliveries up to 3 times with exponential backoff when your endpoint responds with a non-2xx status. Return 200 promptly and process the event asynchronously if needed.