Media and video
A post carries its media as URLs. The routes here put a file into the
media library and give back the id and path
to pass as image[] on Create a post.
POST /upload multipart uploadPOST /upload-from-url the server fetches the filePOST /generate-video AI video, saved to the libraryPOST /video/function a function of a video generator (voices…)Upload a file
Section titled “Upload a file”POST /uploadContent-Type: multipart/form-dataOne field, file. The type is read from the bytes, not from the name:
image/jpeg, image/png, image/gif, image/webp, image/avif,
image/bmp, image/tiff and video/mp4 are accepted.
curl -X POST "https://app.usenadia.com/api/public/v1/upload" \ -H "Authorization: your-api-key" \ -F "file=@reel.mp4"Response 201, the row of the media library:
{ "id": "cm4med0001", "name": "k3J9aQ2xZp.mp4", "originalName": "reel.mp4", "path": "https://media.usenadia.com/k3J9aQ2xZp.mp4", "thumbnail": null, "alt": null}| Field | Type | Description |
|---|---|---|
id |
string |
What a post takes as image[].id |
name |
string |
Stored file name |
originalName |
string | null |
The name the file was sent with |
path |
string |
Public URL, what a post takes as image[].path |
thumbnail |
string | null |
When one was generated |
alt |
string | null |
When one was set |
400 with No file provided when the field is missing, or when the bytes are
not one of the accepted types.
Import from a URL
Section titled “Import from a URL”POST /upload-from-url{ "url": "https://example.com/reel.mp4" }The server downloads the file itself and saves it. The URL must be a public
HTTPS address (no private network), and its path must end in .png, .jpg,
.jpeg, .gif, .webp or .mp4, before any query string. So in practice:
MP4, JPEG, PNG, WebP and GIF. A signed URL that expires (R2, S3) is fine as long
as the server can read it without signing in.
Response 201: the same media row as above. Errors, all 400:
msg |
When |
|---|---|
File must have a valid extension… |
The path does not end in an accepted extension |
Failed to fetch URL |
Network error, blocked address, or the origin answered non-2xx (an expired signature does that) |
Unsupported file type. |
The bytes are not an accepted type, whatever the extension says |
The file may weigh up to 500 MB and must arrive within 10 minutes. Beyond those, and when the workspace’s budget is spent, other statuses:
| Status | When |
|---|---|
413 |
The file is over 500 MB (refused on its Content-Length, or cut as it streams) |
429 |
60 imports per hour per workspace, or 2 already running at once: retry later (/upload and /upload-from-url also share a budget of 300 calls per hour) |
507 |
The server is short of disk space: retry later |
Direct upload from a sandbox
Section titled “Direct upload from a sandbox”An AI assistant’s code sandbox usually has a file on its disk and no public
URL. For that case there is a three-step flow, where the bytes go straight to
UseNadia with curl and never through a tool call:
createUploadUrl(name,Content-Type, exact size) answers a one-time URL underhttps://app.usenadia.com/api/uploads/…, valid 15 minutes, with the exactcurlcommand to run.- The sandbox runs that command: a
PUTof the file, with the declared type and size.201when the bytes arrived. confirmUploadchecks the bytes (type sniffed, a video must be readable with a duration) and saves the media; it answers the row plussize,duration,width,heightandcodec, so the client can compare with the file it meant to send.
Generate a video
Section titled “Generate a video”POST /generate-video| Field | Type | Required | Description |
|---|---|---|---|
type |
string |
Yes | The generator: image-text-slides, or veo3 when it is configured |
output |
string |
Yes | vertical or horizontal |
customParams |
object |
Yes | What the generator needs |
{ "type": "image-text-slides", "output": "vertical", "customParams": { "voice": "elevenlabs-voice-id", "prompt": "Three things nobody tells you about Malta" }}{ "type": "veo3", "output": "vertical", "customParams": { "prompt": "A scenic view of Valletta at sunset", "images": [] }}The video is rendered, then saved to the media library: the response is
201 with the same media row as an upload (id, name, originalName,
path, thumbnail, alt), ready for a post. Each generation uses one AI
video credit of the plan; 400 with type must be any of: … names the
generators available right now.
Call a video function
Section titled “Call a video function”POST /video/function| Field | Type | Required | Description |
|---|---|---|---|
identifier |
string |
Yes | The generator, e.g. image-text-slides |
functionName |
string |
Yes | The function to call |
params |
object |
No | Its arguments, when it takes some |
The one function today is loadVoices of image-text-slides, which lists the
voices a slideshow can be read with:
{ "identifier": "image-text-slides", "functionName": "loadVoices" }Response 201, the raw answer of the generator:
{ "voices": [ { "id": "21m00Tcm4TlvDq8ikWAM", "name": "Rachel", "preview_url": "https://…" } ] }400 when the function does not exist on that generator.