Vercel Cron Jobs: call the Sume API daily without duplicates

A Vercel cron job sends a GET to your route, which calls the Sume API with a date-based Idempotency-Key, so a duplicate invocation cannot bill twice.

5 min readSume
All posts

To call an API from a Vercel cron job, add a crons entry to vercel.json; Vercel then sends an HTTP GET to that path on your production deployment, and your route makes the API call. For a daily Sume video, the route POSTs one Format run with an Idempotency-Key built from the date, so when Vercel delivers the same scheduled run twice, the second POST cannot start or bill a second run.

Vercel facts come from its Cron Jobs, Managing Cron Jobs, and Usage & Pricing pages; Sume facts come from Create a run and Scheduled. All were read on 2026-09-27. Sume has no Vercel connector: the cron route makes a plain HTTPS call. If nothing but the clock drives the work, Sume's own Scheduled feature runs a saved automation on a cadence without your code; see Scheduled AI video agent runs.

How does a Vercel cron job call my route?

Each cron entry names a path and a schedule, for example { "crons": [{ "path": "/api/cron/daily-video", "schedule": "0 5 * * *" }] } for 05:00 UTC every day. Each request carries vercel-cron/1.0 as the user agent and the triggering expression in an x-vercel-cron-schedule header.

From Vercel's Cron Jobs, Managing Cron Jobs, and Usage & Pricing for Cron Jobs, read 2026-09-27.
PropertyVercel behavior
RequestHTTP GET to the path, on your production deployment URL
TimezoneAlways UTC
HobbyAt most once per day, or deployment fails; fires anywhere inside the scheduled hour
Pro and EnterpriseDown to once per minute; fires inside the scheduled minute
DurationSame limits as Vercel Functions
Failed invocationNot retried
DeliveryBest effort: a run can be missed, or invoked more than once
RedirectsNot followed

How do I keep strangers from calling the cron route?

Add a CRON_SECRET environment variable to the project; Vercel recommends a random string of at least 16 characters. When Vercel invokes the job it sends that value in the Authorization header with a Bearer prefix, so the route compares the two and answers 401 on a mismatch. Keep SUME_API_KEY in the project's environment variables too, on the server only, never in client JavaScript or a NEXT_PUBLIC_* variable.

What does the route send to the Sume API?

One POST /v1/formats/{handle}/{slug}/runs. The body must name at least one of instruction, input, previous_run_id, or attachments. Build both the key and the body from the date alone, so that a repeat invocation on the same day sends the identical request.

// app/api/cron/daily-video/route.ts
export async function GET(request: Request) {
  const cronSecret = process.env.CRON_SECRET;
  if (!cronSecret || request.headers.get("authorization") !== `Bearer ${cronSecret}`) {
    return new Response("Unauthorized", { status: 401 });
  }
  const day = new Date().toISOString().slice(0, 10); // UTC, like the schedule
  const res = await fetch("https://api.sume.com/v1/formats/acme/product-promo/runs", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SUME_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": `daily-promo-${day}`, // one run per UTC day
    },
    body: JSON.stringify({
      instruction: `Make the daily promo video for ${day}.`, // date only
      generation_spend_cap_usd: 3,
      communication: { webhook_url: "https://example.com/api/sume-webhook" },
    }),
  });
  const { data, error } = await res.json(); // 202 new run, 200 replay
  if (!res.ok) return Response.json(error, { status: res.status });
  await saveDailyRun(day, data.id); // your database
  return Response.json({ runId: data.id, replay: data.idempotency_hit });
}

What happens when Vercel invokes the same run twice?

Vercel says cron delivery can occasionally invoke the same scheduled run more than once, and asks for idempotent jobs; the date key makes the Sume create idempotent. Idempotency keys for AI video APIs covers keys in full. Because the route reads the date when it runs, keep the schedule away from midnight UTC: on Hobby, the invocation can land anywhere in the scheduled hour.

From Vercel's Managing Cron Jobs and Sume's Create a run, read 2026-09-27.
What happensWhat Sume answers
Vercel invokes the day's run a second timeSame key, same body: 200 with the original run and idempotency_hit: true. No second run, no second charge.
Both invocations reach Sume at the same momentOne wins; the other gets 409 idempotency_key_in_use, which is retryable. Wait about a second and resend to receive the original run.
The body carries a timestamp or random valueSame key, different body: 409 idempotency_conflict. Nothing runs.
The first create failed (402, 503, …)The key was released, so the next call with it can start the day's run.

What if Vercel misses a day?

Vercel does not retry a failed cron invocation, and a transient network error can keep a scheduled request from reaching your function at all. Vercel's advice is reconciliation: each run processes the outstanding work since the last successful run.

  • Store each day's run id (data.id) against the date, as the route above does.
  • On each invocation, look for days with no stored run, and submit any that still needs its video with that day's own key and body.
  • Let the stored id, not the key, decide whether a day already ran. A day whose run failed needs a new key, such as the date plus a version you bump, because the old key is bound to the receipt you already have.

How do I get the video without waiting in the cron function?

Cron jobs share the Vercel Functions duration limits, 300 seconds by default, and runs that make video take minutes, not seconds, so the route returns as soon as Sume accepts the run. The communication.webhook_url in the body asks for one signed format.run.terminal POST when the run completes or fails; Vercel function timeout on video generation shows the receiving route, and a read of the run's result_url is the backup.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume