Cloudflare Workers waitUntil: 30 seconds to submit a Sume run
ctx.waitUntil extends a Worker up to 30 seconds past the response: enough to submit a Sume job with an Idempotency-Key, not to wait on sync mode.

Use ctx.waitUntil() to start a Sume run after your Worker has already answered the caller, and use it only with async or webhook mode. Cloudflare says waitUntil() can extend execution for up to 30 seconds after the response or disconnect, and Sume's sync mode can itself wait up to 30 seconds, so the two budgets collide.
What does waitUntil actually give a Worker?
Cloudflare's limits page says to use ctx.waitUntil() to perform work after returning a response, and that it can extend execution for up to 30 seconds after the response is returned or the client disconnects. HTTP requests have no wall-clock limit while the client stays connected, but that is not the case after a disconnect.
The same page gives other numbers worth keeping next to it: 50 subrequests per invocation on the Free plan against 10,000 on Paid, and at most 6 simultaneous connections waiting for response headers per invocation.
| Limit | Value |
|---|---|
| Work after response (waitUntil) | Up to 30 seconds |
| Subrequests per invocation | 50 Free, 10,000 Paid |
| CPU time per HTTP request | 10 ms Free, 30 s default Paid, 5 min max Paid |
| Connections waiting for headers | 6 per invocation |
| Cron, Queue consumer, Durable Object alarm duration | 15 minutes |
Why not use Sume's sync mode inside waitUntil?
Sume's sync mode holds the submit request for up to wait_timeout_seconds, with a maximum of 30. The jobs page says the wait can be shorter when waiter capacity is unavailable, and that when the job is not terminal you poll rather than resubmit.
A 30-second sync wait started late in a waitUntil window has no headroom. If Cloudflare ends the invocation first, you have a paid job and no job id in your records, which is the worst case. Async returns a durable job id on a fast 202, so the id is stored before the window can close.
What does the safe pattern look like?
Reply to the caller at once, then submit with mode: "webhook", a webhook_url, and an Idempotency-Key derived from your own request. A retry of the caller's request replays the original job instead of creating a second.
Keep the submit short: one fetch, no polling loop. The terminal result arrives on Sume's signed webhook, or you can read status_url later.
export default {
async fetch(req: Request, env: { SUME_API_KEY: string }, ctx: ExecutionContext) {
const { orderId, brief } = (await req.json()) as { orderId: string; brief: string };
ctx.waitUntil(
fetch("https://api.sume.com/v1/image-1.0/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${env.SUME_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${orderId}-v1`,
},
body: JSON.stringify({
prompt: brief,
mode: "webhook",
webhook_url: "https://example.com/hooks/sume",
}),
}).then(async (r) => {
if (!r.ok) console.error("sume submit failed", r.status, await r.text());
}),
);
return new Response(null, { status: 202 });
},
};What if the submit fails after I already replied?
Then the caller was told 202 and nothing started. Log the failure with the x-sume-request-id from the error envelope, and put the order on a retry path: a Queue or a scheduled sweep that resubmits with the same key. Because the key is stable, resubmitting is safe even if the first attempt actually succeeded.
A 4xx at create means nothing ran and nothing was charged, so retrying it unchanged will not help; the errors page lists the codes. Only the retryable ones, such as idempotency_key_in_use or a 429 with retry-after, deserve a loop.
What does Sume not do?
Sume cannot know that your Worker was cut off. It has no hook into Cloudflare's invocation lifecycle, so the durable record of your intent has to live on your side, in a Queue, a Durable Object or a database row written before you reply.
Sources
Related posts
More in Integrations
- codex mcp add --oauth-client-secret: do you need it for Sume?
Codex CLI 0.158.0 added --oauth-client-secret for MCP servers that require a pre-registered client secret. Sume's OAuth uses public clients, so you can skip it.
- Codex env_http_headers: send the Sume key as x-api-key
Codex can send a Sume API key from an environment variable as an x-api-key header with env_http_headers. Config, caveats on auth order, and how to verify it.
- Codex enabled_tools: allowlist only read-only Sume MCP tools
Codex's enabled_tools setting limits which tools a Sume MCP server exposes. Here is an allowlist of Sume's read tools, and why it matters most for API keys.
- Codex MCP required = true: fail fast when the Sume server is down
Codex's required = true makes startup fail if an enabled MCP server cannot initialize. When to set it for Sume, and how the 1000 ms grace and 10 s timeout work.
Written by Sume