Skip to main content
Most renders and tasks reference media via source_url. You have three options:
  1. Public URL you control — host the file yourself (S3, GCS, CDN, etc.) and pass that URL directly in render/task requests. The renderer fetches it as-is. For video/audio referenced this way you must set out_point (Framelane can’t probe the duration of a file it doesn’t host).
  2. Framelane upload — upload a local file with POST /v1/uploads, then use the returned source_url.
  3. Auto-ingest a public URL — set ingest_external: true on a render and Framelane copies any non-Framelane source_url into your storage before rendering. See Importing public URLs below.
This page covers local uploads (option 2) and auto-ingest (option 3).

Upload flow

1. Request an upload URL

Response (201 Created):
The upload_url expires in 1 hour.

2. Upload the file bytes

PUT the entire file to upload_url with the same Content-Type you declared in step 1. Bytes go directly to GCS — they never pass through the Framelane API.
Use a single PUT request (not multipart form upload).

3. Use source_url in your job

Pass source_url from the upload response as source_url in any render element or task body:
For video and audio uploads, Framelane extracts duration and codecs asynchronously after the PUT completes. If you submit a render that references such a file without an explicit out_point before extraction finishes, the request is rejected with 422 asset_not_ready and nothing is queued. Wait for the asset.ready webhook and resubmit, poll GET /v1/workspace/assets for ready assets, or set out_point yourself to skip the duration lookup entirely.

Importing public URLs

If a render references a source_url that Framelane doesn’t host, set ingest_external: true on the render and Framelane copies each such file into your storage before rendering. This gives you the reliability and metadata of a Framelane-hosted file (including automatic out_point for video/audio) without a separate upload step.
How it behaves:
  • The render is created in the ingesting state and is not sent to the render engine yet.
  • Framelane copies each non-Framelane source file into your storage. You receive an asset.ready webhook per file as it completes.
  • Once every imported file is ready, the render automatically transitions to queued and proceeds. out_point is filled in from the imported file’s duration when omitted.
  • If a file can’t be imported (unreachable, too large, or unsupported), the render fails with the ingest_failed error code.
Files already hosted by Framelane (a cdn-user.framelane.io source_url from POST /v1/uploads) are never re-copied. Omit ingest_external (or set it to false) to keep the legacy pass-through behavior described in option 1 above.

Supported content types

Unsupported content_type values return 422 with an unsupported_content_type error (details.allowed lists the accepted types).
Uploadable is not the same as renderable. video/x-msvideo (.avi), video/x-matroska (.mkv) and video/mpeg (.mpg) upload fine, but a video render element rejects them with 422 — its source_url must end in .mp4, .mov or .webm. Re-encode before referencing the file in a render.
.svg sources rasterize in-process on the renderer: flat fills and stroke icons (Lucide/Feather-style) render; gradients, text and filters inside the SVG do not, so pre-rasterize those to PNG.

Errors