Inngest step return 4 MiB limit: return the Sume artifact URL
Inngest caps one step return at 4 MiB and run state at 32 MiB. Return the media.sume.com artifact URL from step.run instead of the video bytes.

A step in an Inngest function can return at most 4 MiB, so never return a rendered video from step.run. Return the Sume artifact URL, a string of a few hundred bytes, and let the next step or the consumer fetch the file from media.sume.com when needed.
Limits are from the Inngest usage limits page, and job behavior from Sume's Core workflow and Jobs and results, read 2026-09-30.
What are the Inngest limits?
The page lists the step return cap and the run state cap, and gives the same advice for both: keep large data outside Inngest state.
| Limit | Value | Guidance on the page |
|---|---|---|
| Step return | 4 MiB returned by one step | Store large results outside step state and return a reference |
| Function run state | 32 MiB across event data, step data, return data, metadata | Keep large data outside run state |
| Event size | 256 KiB to 3 MiB depending on plan | Not stated on the page; keep payloads small and pass references |
What does a completed Sume job give me?
A completed job carries result.artifacts[] entries with id, url, type and content_type. The docs say Sume-owned artifact URLs under https://media.sume.com are the public contract, and that integrations should store the Sume URL rather than raw provider URLs. That URL is the reference Inngest asks you to return.
How do I avoid polling inside steps?
Submit with mode: "webhook" and wait for the event. Sume sends terminal job events only, and results are available only after completion, so a step should not fetch the result before the event arrives. The pattern is covered in Inngest: wait for an AI video webhook.
// inside an Inngest function
const url = await step.run("get-artifact-url", async () => {
const res = await fetch("https://api.sume.com/v1/jobs/" + jobId + "/result", {
headers: { Authorization: "Bearer " + process.env.SUME_API_KEY },
});
const body = await res.json();
const job = body.data ?? body; // the OpenAPI schema wraps the payload in data
return job.result.artifacts[0].url; // a string, not the video
});What should the next step do with the URL?
Pass the URL to whatever needs the file: a publish call, a Timeline input, a CMS field. If a step genuinely must read the bytes, stream them to your own storage inside that step and return only the new reference, so neither the step return nor the 32 MiB run state grows with video size.
Sources
Related posts
More in Developers
- Instagram API is_comment_prompt_used: comment prompts on Reels
Set is_comment_prompt_used to true on the media container to attach a comment prompt, then read replies from prompt_response. One add-on per media, no poll.
- Instagram API media_url missing: copyrighted audio and reels
Since 2026-07-30 Instagram omits media_url for video with copyrighted audio or reels with downloads off. Treat it as optional and import files you own.
- Instagram API poll_attachment on Reels: how the add-on works
Set poll_attachment when creating a Reel container: two to four options, 3 days by default, Facebook Login only. What Sume prepares before that call.
- Instagram publish error: linked Facebook account restricted
Meta added a publishing error: the linked Facebook account is restricted and cannot publish video. What it blocks and how to post stills from a clip meanwhile.
Written by Sume