Heroku H12 at 30 seconds: call Sume async, not sync
Heroku's router ends a request at 30 s (H12). Sume sync mode waits up to 30 s too. Submit async, return 202 to the browser, then poll or take a webhook.

Why does a sync Sume call hit H12?
Heroku's Dev Center says the router terminates a request after 30 seconds (error H12), and the dyno keeps processing it anyway. There is also a rolling 55 second window after the first byte. Sume's sync mode waits at most 30 seconds for a job, so a dyno that submits and waits has no budget left for its own work, and a slower job returns a non-terminal envelope.
Heroku's own advice is to move long work to a background task and poll for it.
| Limit | Value | Source |
|---|---|---|
| Router timeout (H12) | 30 seconds | Heroku |
| Rolling window after first byte | 55 seconds | Heroku |
| Sume sync wait | At most 30 seconds | Sume jobs docs |
| Avatar clip render | Often longer than a web request | Sume avatar docs |
What is the safe pattern?
Leave mode unset (async is the default). The request returns a 202 with a job envelope that has status_url, result_url and events_url. Hand the job id to the browser and return at once. The page then polls your endpoint, which reads GET /v1/jobs/:id/status and obeys next_poll_after_seconds when it is present.
Do not resubmit the paid request because your own call timed out. Poll the job you already have.
import json, os, urllib.request
def post(path, body, key):
req = urllib.request.Request(
"https://api.sume.com" + path,
data=json.dumps(body).encode(),
headers={
"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": key,
},
)
with urllib.request.urlopen(req) as r:
return json.load(r)
job = post("/v1/avatar-1.0/talking-video", {
"avatar_handle": "your_avatar_handle",
"script": "Your order shipped. Here is what to expect.",
}, "order-1001-shipped-clip")
print(job["status_url"]) # return this id to the browser, 202Where does a webhook beat polling?
On a dyno, a webhook means no polling loop to keep alive. Sume signs it with HMAC-SHA256 over the timestamp and body, and the docs set a 5 minute replay tolerance; verify before acting. The Netlify version of this problem is in the background function post.
Sources
Related posts
More in Developers
- Does Sume hosted MCP match the HTTP API? Where parity stops
Not fully. Hosted MCP covers most generation families, but Image 1.0 and Video 1.0 are REST-only, and schedules have no MCP tool. Use the HTTP API for those.
- How long can an AI video Format run take? 90-minute limit
A Sume Format run expires 90 minutes after creation, sooner if silent. How to poll with backoff, when to use a webhook, and what expired means.
- How many 30-second videos can a Sume Pro key run at once? Wave math
Pro runs 4 generations at once and queues 20 more, so 24 accepted jobs. Use the plan table and a Python function to plan a 50-clip batch in waves.
- How many cuts fit in a 3-minute Short? Timeline caps slots at 200
A 180-second Short built with Sume Timeline can have at most 200 video slots, each at least 0.2 seconds. That averages 0.9 seconds a cut. Here is the math.
Written by Sume