OyeFilmy Logo
personFreeUpgrade
bolt0cr
U

Get started

Webhooks & Polling

OyeFilmy AI generation is asynchronous. Collect your results by webhook, or by polling.

How async processing works

When you call POST /api/generate, the server immediately returns a predictionId and begins processing in the background. Video typically takes 30–120 seconds; images are much quicker.

A background worker settles every job whether or not anyone is watching, so you can submit work and disconnect. Pick whichever collection method suits you:

Receive a callback

Pass a webhookUrl when you start the generation. We POST to it once the job reaches a terminal state.

const res = await fetch("https://api.oyefilmy.com/api/generate", {
  method: "POST",
  headers: {
    "Authorization": "Bearer of_live_YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    modelKey: "seedance_2_0_fast_t2v",
    prompt: "A drone shot flying over misty mountains at sunrise",
    aspectRatio: "16:9",
    duration: 5,
    webhookUrl: "https://your-app.example.com/hooks/oyefilmy",
  }),
});
const { predictionId } = await res.json();
// Nothing more to do — the result arrives at your endpoint.

Requirements. webhookUrl must be https and a public host, and it only works on an API-key request — a dashboard session has a browser polling for it. An invalid URL is rejected at submit time rather than silently dropped.

Payload

Sent as application/json. The version field lets the shape grow without breaking your receiver.

{
  "version": 1,
  "event": "generation.completed",   // or "generation.failed"
  "jobId": "665f1c...",
  "predictionId": "atlas-video-___abc123",
  "status": "completed",
  "modelKey": "seedance_2_0_fast_t2v",
  "credits": 42,                      // what you were actually charged
  "assetId": "665f20...",
  "url": "https://cdn.oyefilmy.com/...mp4",
  "type": "Video",
  "error": null,                      // set on generation.failed
  "createdAt": "2026-09-03T09:14:02.001Z",
  "deliveredAt": "2026-09-03T09:15:11.442Z"
}

Verifying the signature

Every delivery carries an OyeFilmy-Signature header. Your webhook secret is shown once, when you create the API key, on the API Keys page.

OyeFilmy-Signature: t=1772534111,v1=5f8b...64-hex

Compute HMAC-SHA256 over the string `${t}.${rawBody}` using your webhook secret, and compare with v1 in constant time. Use the raw body — a re-serialised JSON object will not match.

import crypto from "node:crypto";

app.post("/hooks/oyefilmy",
  express.raw({ type: "application/json" }),   // raw bytes, not a parsed object
  (req, res) => {
    const header = req.get("OyeFilmy-Signature") ?? "";
    const parts = new Map(header.split(",").map(p => {
      const i = p.indexOf("=");
      return [p.slice(0, i), p.slice(i + 1)];
    }));
    const t = Number(parts.get("t"));
    const v1 = parts.get("v1") ?? "";

    // Reject anything older than five minutes, so a captured delivery
    // cannot be replayed later.
    if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) {
      return res.sendStatus(400);
    }

    const expected = crypto
      .createHmac("sha256", process.env.OYEFILMY_WEBHOOK_SECRET)
      .update(`${t}.${req.body}`)
      .digest("hex");

    const a = Buffer.from(v1);
    const b = Buffer.from(expected);
    if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
      return res.sendStatus(400);
    }

    const event = JSON.parse(req.body.toString("utf8"));
    // ... do your work, then acknowledge
    res.sendStatus(200);
  });

Delivery behaviour

Your responseWhat we do
2xxDelivered. Never sent again.
5xx, 429, timeout, connection errorRetried with exponential backoff, up to 5 attempts.
Any other 4xxTreated as permanent — the URL is wrong, so we stop rather than spend four more attempts on it.

Answer within 10 seconds; acknowledge first and do your work afterwards. We do not follow redirects. If delivery is exhausted the result is unaffected — it stays on your account and remains available via GET /api/generate/status/:predictionId and GET /api/generate/history. A callback is a convenience on top of a stored result, never the only copy of it.

Which should I use?

Use webhooksfor anything that runs without a person watching — a server, a queue worker, a scheduled job, an agent that submits a batch and moves on. Use polling when you are already waiting synchronously and a callback endpoint would be more trouble than it is worth: a script, a notebook, a local experiment.

There is no need to choose exclusively. A webhookUrl does not disable polling, and the two are consistent because both read the same settled job.