Legacy
These endpoints live under /api/v1 and cover clip metadata only. The primary API reference is API (/api/v2), which adds jobs, single-document routes, and the wire format used there.
Requests to /api/v1/.../jobs/... return 404; job metadata is only available under /api/v2.
Migrating from v1 to v2
| Topic | v1 | v2 |
|---|---|---|
| Base path (same host) | /api/v1/... |
/api/v2/... — see API |
| Clips GET shape | data.message is an object keyed by clip name |
data is a sorted array of clip objects |
| Clips wire keys | Mixed: many stored lowercase names in JSON (e.g. reelname, librarypath) |
camelCase only (e.g. reelName, libraryPath); unknown fields → 400 |
| Clips PATCH | {"data":[{...}]}; permissive |
Same top-level {"data":[...]} pattern, but camelCase fields only; duplicates in one request → 400; response includes updated, projectId, names |
| Single clip by URL | Use bulk endpoints only | GET / PATCH …/clips/{project}/{clip_name}/{token} |
| Jobs | Not supported on v1 | GET / PATCH …/jobs/{project}/{token} (bulk) and …/jobs/{project}/{job_id}/{token} (single) — see API |
| API tokens | Same tokens; clip read/write as today | Same token flags: Read / Write / Upload; read covers GET jobs, write covers PATCH jobs |
| Upload (signed URLs) | GET https://odailies.com/api/v1/upload/{project_id}/{token}/{path} — see below |
After moving clips/jobs to the current API, see API for how upload signing is documented there. |
After migration, follow API for clips (arrays, camelCase), jobs, and any upload URL changes you adopt with the newer contract.
Clip metadata (v1)
Endpoint: /api/v1/clips/{project_id}/{token} (or legacy paths where the URL ends with /{project_id}/{token} and the segment before the project is not jobs — same behavior as before).
Get clip metadata
GET /api/v1/clips/{project_id}/{token}
Retrieves metadata for all clips in the project.
Response:
{
"data": {
"message": {
"clip_name": {
"name": "string",
"scene": "string",
"shot": "string",
"take": "string",
"description": "string",
"circled": boolean,
"rating": number,
"inPoint": number,
"camera": "string",
"timecode": "string",
"fps": number,
"date": "string",
"duration": number,
"reelname": "string",
"whitepointkelvin": number,
"whitepointccshift": number,
"shutter": number,
"filter": "string",
"ndfilter": "string",
"lens": "string",
"focallength": number,
"focusdistance": number,
"asa": number,
"sensorFps": number,
"fStop": number,
"tStop": number,
"filesize": number,
"librarypath": "string"
}
}
}
}
The date field must use the yyyyMMdd format (e.g. 20260422), same as API v2.
Update clip metadata
PATCH /api/v1/clips/{project_id}/{token}
Content-Type: application/json
{
"data": [
{
"name": "A001C001_260422_R3CK",
"scene": "22",
"shot": "4",
"take": "1"
},
{
"name": "A001C002_260422_R3CK",
"scene": "22",
"shot": "4",
"take": "2",
"circled": true
}
]
}
Updates metadata for specified clips. Only the fields you include will be updated. Each clip in the list must include a name field to identify the clip to update.
Upload (signed URLs)
GET https://odailies.com/api/v1/upload/{project_id}/{token}/{path}
Returns JSON with a time-limited url (send the file body with PUT to that URL), expiresAt (ISO 8601, UTC), and the same semantics as v2 upload. Requires the same API token upload permission as other tools. The path value follows the uploading directory layout (for example _graded/… or _thumbnails/ClipName.jpg). {path} may include slashes.
Code example (Python, v1 only)
import requests
class oDailiesClientV1:
"""Legacy v1 clips client (map GET, stored field names on the wire)."""
def __init__(self, project_id: str, token: str):
self.project_id = project_id
self.token = token
self.base_url = "https://odailies.com/api/v1"
self.clips_url = f"{self.base_url}/clips/{self.project_id}/{self.token}"
def get_clips(self) -> dict:
response = requests.get(self.clips_url)
response.raise_for_status()
return response.json()["data"]["message"]
def update_clip(self, clip_name: str, metadata: dict):
response = requests.patch(
self.clips_url,
json={"data": [{"name": clip_name, **metadata}]},
headers={"Content-Type": "application/json"},
)
response.raise_for_status()
return response.json()
def update_clips(self, clips: list):
response = requests.patch(
self.clips_url,
json={"data": clips},
headers={"Content-Type": "application/json"},
)
response.raise_for_status()
return response.json()
A Python example for /api/v2 is on API.