Skip to main content
Most renders and tasks reference media via source_url. You have two options:
  1. Framelane upload — upload a local file with POST /v1/uploads, then use the returned source_url. The file is measured when it lands, so video and audio elements can omit out_point.
  2. Public URL — pass a direct, public link to the file. The render engine fetches it at render time; video and audio are probed for duration first unless you set out_point. See Using public URLs below.
This page covers both.

Upload flow

1. Request an upload URL

Response (201 Created):
The upload_url expires in 1 hour. uploaded: false is the normal answer here and means what it says — a slot exists, the file has not arrived yet. first_party_upload_url is the same session on a Framelane hostname. Prefer upload_url when you can reach Google storage directly; use first_party_upload_url when your network allows *.framelane.io but not storage.googleapis.com. Same protocol, same chunks, same expiry — the only difference is which name your firewall has to know about.

2. Upload the file bytes

PUT the 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). For a large file over an unreliable link, upload_url is a GCS resumable session: you can send it in chunks with Content-Range headers and resume after a failure. The SDKs and the console do this for you — upload_file / uploadFile send 8 MiB chunks and retry each on its own, so a dropped connection costs one chunk rather than the file.
If that PUT is refused before it leaves your machine, you are almost certainly in a sandbox with a per-host egress allow-list — an agent’s code-execution container, typically — and storage.googleapis.com is not on it. Two ways out. If the allow-list admits *.framelane.io (or an admin can add uploads.framelane.io, one domain), PUT the same bytes to first_party_upload_url instead. Otherwise, for a file under 256 KB, skip both steps and send the bytes inline:
The response comes back with "uploaded": true and a null upload_url: the file is stored and source_url works immediately. Over 256 KB the request is rejected with 413 inline_too_large, because the payload has to be encoded into the request body — fine for logos, crests and icons, wrong for video. MCP agents pass the same content_base64 argument to the create_upload tool.

3. Confirm it arrived (optional but cheap)

The bytes went straight to storage, so a 201 in step 1 says nothing about step 2. GET /v1/workspace/assets/{asset_id} — or the get_asset MCP tool — answers bytes_uploaded, which is the only reliable confirmation available.

4. 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.

Using public URLs

A render may reference a source_url that Framelane doesn’t host. Nothing is copied: the render engine fetches the file at render time, so the URL must be a direct, public https link to the media file (not a share page) that supports HTTP range requests and does not require cookies or a referer. Images and LUTs need nothing else. A video or audio element without an explicit out_point also needs the file’s duration. By default Framelane probes such files before rendering:
How it behaves:
  • The render is created in the ingesting state and is not sent to the render engine yet.
  • The render engine measures each public video/audio file (duration, dimensions). The same probe runs on the same fleet as renders, so the wait is the same wait a queued render has.
  • Once every probed file is ready, the render automatically transitions to queued and proceeds. out_point is filled in from the measured duration when omitted.
  • If a file can’t be probed (unreachable, not a media file, or the probe times out), the render fails with the ingest_failed error code. Set out_point explicitly to render such a file without a probe.
A public URL’s metadata is shared: once one render has probed it, every later render that references the same URL queues immediately. Files already hosted by Framelane (a cdn-user.framelane.io source_url from POST /v1/uploads) are never probed this way — uploads are measured when they land. Set ingest_external: false on a render to skip the probe and pass URLs straight through; video and audio then require an explicit out_point.
Share-page links (a Drive or Dropbox “view” link, a YouTube URL) are not direct file URLs and will not work. Use the service’s direct-download form, or upload the file.

Which route works where

Agents run in very different places, and the difference that matters is whether the place can reach an upload host at all. Routes: direct = PUT to upload_url or first_party_upload_url; panel = the file picker MCP hosts render alongside create_upload; inline = content_base64, for a file the agent produced itself (under ~150 KB); link = POST /v1/uploads/links, a page a person opens. The next_step text on every create_upload result walks these in order: panel, then upload_url / first_party_upload_url, then inline, then a link. It never asks the agent to run a command inside its sandbox.

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