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.

5 min readSume
All posts

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.

Cloudflare Workers limits relevant to a Sume call (read 2026-10-02)
LimitValue
Work after response (waitUntil)Up to 30 seconds
Subrequests per invocation50 Free, 10,000 Paid
CPU time per HTTP request10 ms Free, 30 s default Paid, 5 min max Paid
Connections waiting for headers6 per invocation
Cron, Queue consumer, Durable Object alarm duration15 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

All Integrations posts

Written by Sume