Upload a reference image to Sume with curl: three requests, one URL
Get a durable HTTPS URL for a local image before a video call: reserve a presigned PUT, upload the bytes, complete the asset. A short curl and jq script.

How do I turn a local image into a URL for a Sume video request?
Make three requests: reserve an upload with POST /v1/assets/upload-url, PUT the bytes to the presigned URL it returns, then call POST /v1/assets/{id}/complete. Completion is the step that returns the asset's public URL. Put that URL in frame_images or input_references on POST /v1/videos.
The bytes go straight to storage and never through the Sume API, so the upload speed depends on your link to storage. The SDK's uploadFile does the same three steps in TypeScript and throws SumeUploadError with a step of create, put or complete.
The three steps
Public HTTPS URLs are the simplest input, and Sume prefers them in generation requests. Use this flow only when the file is on your disk. These asset routes work today but are kept out of the public OpenAPI document.
| Step | Request | What you keep |
|---|---|---|
| 1. Reserve | POST /v1/assets/upload-url with content_type, size_bytes, filename | asset.id, upload.url, upload.headers |
| 2. Upload | PUT bytes to the presigned URL with exactly the presigned headers plus content-type | Nothing |
| 3. Complete | POST /v1/assets/{id}/complete with size_bytes | asset.url, the durable HTTPS URL |
The script
It needs bash 4 for mapfile, curl and jq. Do not send extra headers on the PUT: the presigned signature covers the headers it names, and adding others breaks it.
set -euo pipefail
API=https://api.sume.com
F=hero.png; TYPE=image/png
SIZE=$(wc -c < "$F" | tr -d ' ')
auth=(-H "x-api-key: $SUME_API_KEY" -H "content-type: application/json")
R=$(curl -sf "$API/v1/assets/upload-url" "${auth[@]}" \
-d "{\"content_type\":\"$TYPE\",\"size_bytes\":$SIZE,\"filename\":\"$F\"}")
ID=$(jq -r .data.asset.id <<<"$R")
URL=$(jq -r .data.upload.url <<<"$R")
METHOD=$(jq -r '.data.upload.method // "PUT"' <<<"$R")
mapfile -t HDRS < <(jq -r '(.data.upload.headers // {}) | to_entries[] | "-H", "\(.key): \(.value)"' <<<"$R")
curl -sf -X "$METHOD" "$URL" ${HDRS[@]+"${HDRS[@]}"} \
-H "content-type: $TYPE" --data-binary @"$F"
curl -sf "$API/v1/assets/$ID/complete" "${auth[@]}" \
-d "{\"size_bytes\":$SIZE}" | jq -r .data.asset.urlUsing the URL, and the CLI shortcut
Do not hand a signed link from the download-url route to a generation request: signed and private URLs are rejected, and the completed asset URL is the one to use. If the image is already public, skip the upload and pass its URL directly, or register it with sume assets create --source-url <url> --confirm-submit.
Input media must be public HTTPS. Localhost, private-network, signed and non-HTTPS links are rejected.
Check the size you declare. The reserve call takes size_bytes and so does the complete call, and both should be the real byte length of the file, which the script gets from wc -c. If the complete step fails after a clean PUT, compare the size you declared with the real file size. Keep the returned asset.id too: it is the handle for sume assets complete if a script dies between steps two and three.
Sources
Related posts
More in Developers
- Uptime monitor for the Sume API: probe GET /v1/me, spend reads
A 30 second probe of GET /v1/me costs two reads a minute against a 4,800 read Free budget. Python probe and a status table for 401, 429 and 5xx.
- What did one transcription job cost? GET /v1/usage by job_id
Read GET /v1/usage?job_id= and sum data.summary.debited_usd_micros to see what one Sume STT job really cost. Holds and refunds are not counted as spend.
- User closed the tab mid-render: cancel the Sume job or let it finish?
Cancel works only before generation starts; after that you get 409 job_generation_already_started and the job bills. A tested tab-close handler.
- callback_url on Sume /v1/videos: HTTPS only, not OpenRouter events
POST /v1/videos takes callback_url. It must be HTTPS, and Sume sends its own job.completed envelope, not OpenRouter's video.generation events.
Written by Sume