Sora input_reference image: upload with uploadFile, use frame_images
OpenAI took the first-frame image as multipart input_reference. Sume wants an HTTPS URL: upload with the SDK's uploadFile, then pass it as frame_images.

If your Sora code sent a local JPEG, PNG or WebP as input_reference, the Sume version is two steps: upload the bytes with uploadFile from @sume-com/sdk to get a durable HTTPS URL, then send that URL in frame_images with frame_type first_frame. Sume's /v1/videos takes JSON and URLs, not a multipart file part.
OpenAI's video guide, read 2026-10-08, describes input_reference as an image that becomes the first frame, and the same guide now says the Videos API shuts down on September 24, 2026.
Field by field
Sume also has input_references, with an s, which conditions the whole clip rather than pinning the opening frame. If your Sora call was a first frame, use frame_images. If you send both fields, frame_images wins.
| Concern | OpenAI guide | Sume docs |
|---|---|---|
| Where the image goes | input_reference, multipart file part | frame_images[].image_url.url, JSON |
| Role of the image | first frame | frame_type: first_frame or last_frame |
| Count | one image | frame_images up to 2; input_references up to 12 |
| Formats named | JPEG, PNG, WebP | an HTTPS image URL; the model row lists its supported types |
| Where limits live | the guide page | the model row in GET /v1/videos/models |
The upload and the call
uploadFile reserves a presigned PUT, sends the bytes straight to storage, then completes the asset and returns the durable URL. It reuses the client's key and base URL, which defaults to https://api.sume.com. Run this as an ES module on Node 18 or later with SUME_API_KEY set.
import { readFile } from "node:fs/promises";
import { createSumeClient, createVideoGeneration, uploadFile } from "@sume-com/sdk";
const client = createSumeClient({ apiKey: process.env.SUME_API_KEY });
const bytes = await readFile("start.png");
const asset = await uploadFile({
client,
file: new Uint8Array(bytes),
contentType: "image/png",
filename: "start.png",
});
const { data, error } = await createVideoGeneration({
client,
headers: { "Idempotency-Key": "start-png-1" },
body: {
model: "sume/auto",
prompt: "The camera pulls back from the product on the desk",
frame_images: [{
type: "image_url",
image_url: { url: asset.url },
frame_type: "first_frame",
}],
},
});
console.log(error ?? data);Things to check
The asset endpoints behind uploadFile are kept out of the public OpenAPI document, which is why the helper exists; do not hand-roll them from the spec. Generated SDK calls return an object with data, error and response instead of throwing, so the last line prints whichever one is set.
Not every catalog model accepts a first frame. Read supported_frame_images on the model row before you pin sume/auto in code, and see the first-frame size post for the size question.
Sources
Related posts
More in Developers
- Sora jobs in flight at shutdown: reconcile, then resubmit to Sume
OpenAI's page gives the removal date but not in-flight jobs. Mark every unfinished Sora row, then resubmit its prompt to Sume with a key built from the old id.
- Measure your own render time on Sume from job event timestamps
No published Sora timing carries over. Read GET /v1/jobs/{id}/events, diff created, started and completed, and keep your own queue and render split per model.
- Sora to Sume pull request review: eight lines to check before merge
A reviewer's checklist for a Sora-to-Sume PR: duration and resolution types, status words, the 302 download, the webhook body, idempotency and removed fields.
- Sora API gone: port to Sume with Python urllib, no extra packages
Replace the OpenAI videos calls with a stdlib-only Python script: submit to Sume /v1/videos, poll, then fetch the 302 artifact URL without sending your key.
Written by Sume