Recast sync wait caps at 30 seconds: use a webhook for long sources

Sync and subscribe modes wait at most 30 seconds, so submit h3-max-recast jobs as async or webhook and poll /v1/jobs/{id}/status as a fallback.

6 min readSume
All posts

A Recast job on Sume should be submitted as async or webhook, not sync. Every submit endpoint accepts a mode, but sync and subscribe block for at most wait_timeout_seconds, and that value is capped at 30 seconds (Jobs and results, read 2026-10-03). A 5 to 30 second source being rendered by a video model is not something to plan around finishing in half a minute, so design for the 202 that comes back when the wait runs out.

The wait is not a failure. The docs are explicit that a submit is accepted the moment Sume has a durable job id, so every mode returns a job you can follow. sync simply gives you one bounded chance to see a terminal state on the same response.

The four modes for a Recast job

Pick the mode by what your caller can do. A server with a public HTTPS endpoint should use webhook. A script on a laptop should use async and poll. A dashboard that wants a quick answer on a short clip can try sync with a short wait and fall back.

Communication modes for a Recast submit, read 2026-10-03
ModeWhat the client doesGood for Recast?
async (default)Gets the job id immediately, polls statusYes, the simplest
webhookSends webhook_url; Sume posts a terminal eventYes, best for servers
syncWaits up to 30 s on the submit callOnly as a bounded first look
subscribeAn alias of sync; no progress eventsSame as sync

Submit with a webhook and keep a polling fallback

The docs advise keeping the polling fallback alongside a webhook, because delivery can fail. The script submits in webhook mode, prints the job id and then polls the documented status route. The response envelope can nest the job, so it looks for job and falls back to data.

import asyncio
import os
import httpx

API = "https://api.sume.com"

async def main() -> None:
    key = os.environ.get("SUME_API_KEY")
    if not key:
        raise SystemExit("set SUME_API_KEY")
    headers = {"Authorization": f"Bearer {key}"}
    body = {
        "model": "h3-max-recast",
        "video_url": "https://example.com/source.mp4",
        "reference_image_urls": ["https://example.com/person.jpg"],
        "resolution": "768p",
        "mode": "webhook",
        "webhook_url": "https://example.com/hooks/sume",
    }
    async with httpx.AsyncClient(headers=headers, timeout=60) as c:
        r = await c.post(f"{API}/v1/video-router/generate", json=body,
                         headers={"Idempotency-Key": "recast-hook-001"})
        r.raise_for_status()
        env = r.json()
        data = env.get("data", env)
        job = data.get("job", data)
        print("job", job.get("id"), job.get("status"))
        s = await c.get(f"{API}/v1/jobs/{job['id']}/status")
        print(s.json())

asyncio.run(main())

Webhook hygiene

A webhook receiver should verify the signature before it trusts the body. Sume signs the raw JSON body and sends x-sume-webhook-timestamp and x-sume-webhook-signature headers; the webhooks guide lists the terminal events (job.completed, job.failed, job.canceled) and says there are no progress or partial deliveries. The webhook URL must be public HTTPS, so a localhost receiver is rejected at submit. If the receiver is down, the job still completes; you read the result by polling GET /v1/jobs/{id}/result.

Keep the endpoint idempotent too: a delivery can arrive more than once, so key your processing on the job id. See verify the signature before you download for the verifier.

What a failed webhook looks like in practice

Imagine your receiver returns a 500 for an hour because of a deploy. The Recast job did not care: it completed on its own clock, the result sits at /v1/jobs/{id}/result, and your poller can fetch it. What you lose is the push, not the data. That is why the docs recommend a polling fallback and why the script above prints the status immediately after submit: it proves the job exists before you rely on a callback that may never arrive.

The opposite failure is the more expensive one. If your code treats a missing callback as a failure and resubmits, you pay twice. Resubmitting with the same Idempotency-Key is safe, because a replay returns the original job, but a new key is a new job and a new reserve. Decide on the key before the first submit and store it next to your own record of the clip.

Finally, expect that a long source is more likely to hit your own timeouts than Sume's. A load balancer in front of your service may cut a connection at 30 or 60 seconds, which is another reason not to hold the submit open. Return to the caller as soon as you have the job id and let the webhook or the poller close the loop.

A rule of thumb

Use sync only when the answer is allowed to be "not yet". For Recast that is nearly always. Use a webhook when a server owns the workflow, and async plus polling when a person is watching a script. Whichever you choose, send an Idempotency-Key so a retry after a timeout returns the original job instead of charging a second time.

  • Never block a request thread on a Recast job.
  • Accept 202 as a normal outcome.
  • Verify before you download the result.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume