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.

4 min readSume
All posts

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.

Sume asset upload flow, from the SDK source and CLI docs (read 2026-10-06)
StepRequestWhat you keep
1. ReservePOST /v1/assets/upload-url with content_type, size_bytes, filenameasset.id, upload.url, upload.headers
2. UploadPUT bytes to the presigned URL with exactly the presigned headers plus content-typeNothing
3. CompletePOST /v1/assets/{id}/complete with size_bytesasset.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.url

Using 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

All Developers posts

Written by Sume