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

Payload shape

All payloads include topic and data containing the relevant resource.

File events include expires_at (null for permanent uploads, or an ISO 8601 datetime when the file is scheduled to expire). See Files — retention.

Render events also include expires_at (null for permanent renders, or an ISO 8601 datetime when the render is scheduled for deletion). See Renders — retention and deletion.

Example for render.completed:

json
{
  "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",
    "expires_at": null
  }
}

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).

js
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, 10 seconds apart, when your endpoint responds with a non-2xx status. Return 200 promptly and process the event asynchronously if needed.