Cloudflare Workers cron job: start an AI video on a schedule

Add a Cron Trigger and a scheduled() handler to run a Worker on a schedule. For an AI video, key the run to scheduledTime and return at once.

5 min readSume
All posts

A Cloudflare Workers cron job is a Cron Trigger: list a cron expression under triggers.crons in the Wrangler file, export a scheduled() handler, and Cloudflare runs that handler on the schedule, in UTC. To start an AI video from it, the handler sends one API call keyed to controller.scheduledTime, with a webhook URL, and returns; the finished video arrives later at a Worker route.

Cloudflare facts come from its Cron Triggers, Scheduled Handler, Limits, and Secrets pages; Sume facts come from Create a run and Runs and results. All were read on 2026-09-27. Sume has no Cloudflare connector: the Worker makes a plain HTTPS call. The same pattern on Vercel is Vercel Cron Jobs: call the Sume API daily.

How do I add a cron trigger to a Worker?

Two pieces: a handler and a schedule. Cloudflare calls scheduled(controller, env, ctx) on every trigger; controller.cron is the expression that fired, and controller.scheduledTime is the time the event was scheduled for, in milliseconds since the epoch, UTC. The schedule goes in the Wrangler file, for example [triggers] with crons = ["0 9 * * *"] for 09:00 UTC daily. If Wrangler manages the Worker, Cloudflare says to manage Cron Triggers only through that file, and each deploy replaces the previous triggers with the ones in the array.

From Cloudflare's Cron Triggers, Scheduled Handler, and Limits pages, read 2026-09-27.
PropertyCloudflare behavior
Time zoneUTC
ExpressionFive fields; weekdays run from 1 = Sunday to 7 = Saturday
Trigger changesUp to 15 minutes to propagate
Wall time per invocation15 minutes
CPU time per invocationFree: 10 ms. Paid: 30 seconds under an hourly interval, 15 minutes at an hour or more. Waiting on fetch() does not count.
Cron Triggers per account5 on Free, 250 on Paid
HistoryCron Events keeps the 100 most recent invocations

What should the scheduled handler send to the video API?

One POST /v1/formats/{handle}/{slug}/runs, which starts a run of a saved Sume recipe called a Format; What is a Sume Format? explains it. Store the key with npx wrangler secret put SUME_API_KEY; secrets are encrypted values read from env. Build both the Idempotency-Key and the body from the scheduled time, never from Date.now(), so a repeat for the same slot sends an identical request. The create answers at once: 202 for a new run, 200 for a replay.

export default {
  async scheduled(controller, env, ctx) {
    // The slot this event was scheduled for, not the moment it ran
    const slot = new Date(controller.scheduledTime).toISOString();
    const res = await fetch("https://api.sume.com/v1/formats/acme/daily-recap/runs", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${env.SUME_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `daily-recap-${slot}`,
      },
      body: JSON.stringify({
        input: { slot },
        communication: { webhook_url: "https://video.example.com/hooks/sume" },
      }),
    });
    const { data, error } = await res.json();
    if (!res.ok) throw new Error(`Sume answered ${res.status} ${error?.code}`);
    console.log(`run ${data.id}, replay: ${data.idempotency_hit}`);
  },
};

What happens if the handler runs twice for the same slot?

The key makes a repeat harmless, whatever caused it. You can even replay a slot on purpose: in local development, the /cdn-cgi/local/scheduled route takes a time parameter that overrides controller.scheduledTime, and the handler then calls the real API. Two of Sume's idempotency rules matter for a cron handler:

  • Same key, same body: 200 with the original receipt and idempotency_hit: true. No second run, no second charge.
  • Same key, different body: 409 idempotency_conflict, and nothing runs. communication.webhook_url is part of the body, so keep it fixed too.
  • The other cases, such as two requests at the same moment or a retry after a failed create, are in Idempotency keys for AI video APIs.

Should the Worker wait for the video to finish?

No. A cron invocation gets at most 15 minutes of wall time, and the runtime waits for the handler's promise only up to that limit. Long-form video is 15 to 30 minutes of work, and a Sume run can last until 90 minutes after created_at before it is force-finalized as failed. A Worker that stops watching does not stop the run or its spend.

Let Sume call you instead. communication.webhook_url gets one signed format.run.terminal POST when the run completes or fails; a canceled run sends none. Cloudflare Workers webhook to a Queue shows the fetch() side that verifies that POST and hands the work to a Queue.

If nothing but the clock drives the work and the run needs no data from your side, you may not need a Worker at all: Sume's own Scheduled feature runs a saved automation on a cadence, as Scheduled AI video agent runs explains.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume