Cloudflare Workflow schedules: one Sume Idempotency-Key per tick
A scheduled Cloudflare Workflow that submits a Sume job should derive the Idempotency-Key from the tick, so a replayed step returns the same job.

Build the Sume Idempotency-Key from the schedule tick, for example the hour the cron fired, and reuse that exact key every time the step runs. A retried or replayed submit then returns the original job instead of creating and billing a second one. The Sume docs say a retried submit must reuse the same key.
The schedule syntax is from Cloudflare's Workflows changelog; the key rules are from Sume's Jobs and results, both read 2026-09-30.
What does the Cloudflare change add?
The changelog says you can declare a Workflow in the exports field of your Wrangler config, keyed by the class that extends WorkflowEntrypoint, with type set to workflow, a name, optional limits such as steps, and a schedules array of cron strings. Its example is "0 * * * *", a cron string that fires at the top of each hour.
{
"exports": {
"MyWorkflow": {
"type": "workflow",
"name": "my-workflow",
"schedules": ["0 * * * *"]
}
}
}Why does the key come from the tick?
A Workflow step can run again after a failure. If each run made up a new random key, the replay would be a new paid request. A key built from the tick is the same on every attempt for that tick and different for the next one.
| Situation | What the docs say |
|---|---|
| Retried submit | Reuse the same Idempotency-Key; the retry returns the original job |
| Local timeout | Do not resubmit the original paid request |
| Default mode | Omit mode and you get async |
| Key shape in the docs | hero-shot-2026-08-03-001 |
| Failed webhook send | A failed delivery is not a failed job |
What does the step look like?
Compute the key outside the retried work, using the scheduled time rather than the current time. The changelog page does not say what a scheduled run receives, so take the tick from whatever scheduled time your Workflow exposes; the code below takes a timestamp and floors it to the hour.
export function tickKey(scheduledMs: number): string {
const hour = new Date(Math.floor(scheduledMs / 3600000) * 3600000)
.toISOString()
.slice(0, 13);
return "hero-shot-" + hour;
}
export async function submit(apiKey: string, scheduledMs: number) {
const res = await fetch("https://api.sume.com/v1/image-1.0/generate", {
method: "POST",
headers: {
Authorization: "Bearer " + apiKey,
"Content-Type": "application/json",
"Idempotency-Key": tickKey(scheduledMs),
},
body: JSON.stringify({ prompt: "Product hero shot on marble", mode: "async" }),
});
return res.json();
}How should the Workflow wait for the result?
The submit returns a job id first, and a 2xx means the job exists, not that it finished. Poll GET status_url with backoff, or receive a webhook and keep polling as a backup. A failed webhook delivery leaves a job that still reached its real state. Workers cron for AI video covers the older cron-trigger route.
Sources
Related posts
More in Developers
- Workflows keeps state 7 days now: store your Sume job ids
Cloudflare Workflows on Workers Paid now keeps finished instance state 7 days by default. Save the Sume job id and result URL in your own store.
- Workflows instance limits vs Sume queue headroom: which binds?
Cloudflare lists 50,000 concurrent Workflow instances (Paid, April entry). Sume reports your own generation headroom; size fan-out by that formula.
- Workflows .subscribe() vs Sume job events: push or pull?
Cloudflare Workflows can stream instance events with subscribe(). Sume has no SSE stream for jobs: use a signed webhook, or poll status and the events snapshot.
- Codex disabled_tools: block Sume's paid MCP tools
List Sume's paid tool ids under disabled_tools in the Codex config.toml so the agent cannot call them, even on an API-key session that sees every tool.
Written by Sume