Core concepts
Media inputs
Any field that takes an image, video or audio file accepts three forms. Pick whichever fits how your files are stored.
Three ways to pass media
| Form | Example | Best for |
|---|---|---|
| Upload ID | "upl_4f1c9e…" | Large files, files reused across requests, private files. |
| Public https URL | "https://cdn.example.com/face.jpg" | Files you already host publicly. |
| Data URL | "data:image/jpeg;base64,/9j/4AAQ…" | Small files generated on the fly (up to 20 MB). |
URLs and data URLs are copied to Orior storage first, then used — so a URL only has to be reachable at request time. Each copy counts as an upload and follows the same retention.
Types and limits
| Kind | Content types | Max size |
|---|---|---|
| Image | png, jpeg, webp, heic/heif, gif | 20 MB |
| Video | mp4, quicktime (mov), webm, m4v | 200 MB (signed upload or URL) |
| Audio | mpeg (mp3), mp4/m4a, aac, wav, ogg, webm | 50 MB |
Request bodies are limited to 30 MB, so send large files as uploads or URLs, not data URLs.
Large files: signed uploads
Ask for a signed URL, PUT the bytes directly to storage, then use the upload ID.
# 1. Reserve an upload
curl https://api.oriorai.com/v1/uploads \
-H "Authorization: Bearer $ORIOR_API_KEY" -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
-d '{"content_type":"video/mp4","size":48123904}'
# → {"id":"upl_…","upload_url":"https://storage.googleapis.com/…","upload_method":"PUT",…}
# 2. Upload the file (within 15 minutes)
curl -X PUT -H "Content-Type: video/mp4" --data-binary @dance.mp4 "<upload_url>"
# 3. Use it
curl https://api.oriorai.com/v1/videos … -d '{"model":"kling-3.0-motion-control","motion_video":"upl_…","character_id":"…"}'import { readFile } from 'node:fs/promises'
const auth = { Authorization: `Bearer ${process.env.ORIOR_API_KEY}` }
const file = await readFile('dance.mp4')
const upload = await (await fetch('https://api.oriorai.com/v1/uploads', {
method: 'POST',
headers: { ...auth, 'Idempotency-Key': crypto.randomUUID(), 'Content-Type': 'application/json' },
body: JSON.stringify({ content_type: 'video/mp4', size: file.length }),
})).json()
await fetch(upload.upload_url, { method: 'PUT', headers: upload.upload_headers, body: file })
// upload.id is now usable as motion_video, start_frame, …import os, uuid, requests
auth = {"Authorization": f"Bearer {os.environ['ORIOR_API_KEY']}"}
data = open("dance.mp4", "rb").read()
upload = requests.post("https://api.oriorai.com/v1/uploads", headers={**auth, "Idempotency-Key": str(uuid.uuid4())},
json={"content_type": "video/mp4", "size": len(data)}).json()
requests.put(upload["upload_url"], headers=upload["upload_headers"], data=data).raise_for_status()
# upload["id"] is now usableAlternatively send {"url": "https://…"} or {"data": "data:…"} to POST /uploads to store a file once and reuse its ID. See Uploads reference.
URL rules
- Only
https://on the default port, without credentials in the URL. - The host must resolve to public addresses — private, loopback, link-local and cloud-metadata addresses are rejected.
- Up to 3 redirects are followed, each checked the same way. Downloads time out after 60 seconds.
- The response content type must match the field (an image field needs an
image/*response).
Rejected URL
{"error":{"code":"VALIDATION_ERROR","message":"start_frame must be publicly reachable.","request_id":"req_…"}}Retention
- Uploads and copied inputs are deleted after 7 days (
expires_at). Using an expired upload returnsVALIDATION_ERROR. - Photos you use to create a character or product are copied into it and kept with it.
- Generation outputs are kept until you delete them — see Generations.
Only upload media you have the rights to use, and photos of real people only with their consent.