516 lines
13 KiB
Markdown
516 lines
13 KiB
Markdown
# VFXReview External API Reference
|
|
|
|
Base URL: `https://review.twotalesvfx.com`
|
|
|
|
All requests require an `Authorization` header. Two formats are accepted:
|
|
|
|
```
|
|
Authorization: Bearer <API_SECRET_KEY>
|
|
X-Api-Key: <API_SECRET_KEY>
|
|
```
|
|
|
|
---
|
|
|
|
## Endpoints Overview
|
|
|
|
| Method | Path | Description |
|
|
|--------|------|-------------|
|
|
| `GET` | `/api/ext/projects` | List all projects with showIds |
|
|
| `GET` | `/api/ext/shots/lookup` | Look up a shot by code — primary pipeline endpoint |
|
|
| `GET` | `/api/ext/projects/{projectCode}/episodes` | List episodes for a project |
|
|
| `GET` | `/api/ext/projects/{projectCode}/shots` | List shots for a project by code |
|
|
| `GET` | `/api/ext/shots` | List shots by database project ID |
|
|
| `GET` | `/api/ext/shots/{shotId}` | Get a single shot by DB id or shot code |
|
|
| `POST` | `/api/ext/shots` | Create a new shot |
|
|
| `PATCH` | `/api/ext/shots/{shotId}` | Update shot fields (e.g. increment version) |
|
|
|
|
---
|
|
|
|
## Shot Status Values
|
|
|
|
| Value | Description |
|
|
|---|---|
|
|
| `WAITING` | Not yet started |
|
|
| `IN_PROGRESS` | Currently being worked on |
|
|
| `IN_REVIEW` | Submitted for review |
|
|
| `REVISIONS` | Changes requested |
|
|
| `COMPLETE` | Approved and complete |
|
|
|
|
---
|
|
|
|
## Shot Priority Values
|
|
|
|
| Value | Description |
|
|
|---|---|
|
|
| `LOW` | Low priority |
|
|
| `NORMAL` | Default |
|
|
| `HIGH` | High priority |
|
|
| `URGENT` | Urgent |
|
|
|
|
---
|
|
|
|
## Shot Code Format
|
|
|
|
```
|
|
{showId}_{episode}_{scene}_{shotNumber}
|
|
|
|
e.g. UNG_106_010_020
|
|
^^^ ^^^ ^^^ ^^^
|
|
| | | shot number (zero-padded)
|
|
| | scene
|
|
| episode
|
|
show ID
|
|
```
|
|
|
|
---
|
|
|
|
## 1. List Projects
|
|
|
|
```
|
|
GET /api/ext/projects
|
|
```
|
|
|
|
Returns all projects with their `showId`, `code`, and metadata. Useful for populating project selectors in pipeline tools or resolving a showId → projectCode mapping.
|
|
|
|
### Query Parameters
|
|
|
|
| Parameter | Required | Default | Description |
|
|
|---|---|---|---|
|
|
| `status` | No | `ACTIVE` | `ACTIVE`, `ARCHIVED`, or `ALL` |
|
|
|
|
### Response `200 OK`
|
|
|
|
```json
|
|
{
|
|
"projects": [
|
|
{
|
|
"id": "cmp6l5mzq0001ua0gz07bk72f",
|
|
"name": "Ungo Season 1",
|
|
"code": "UNG_S1",
|
|
"showId": "UNG",
|
|
"projectType": "EPISODIC",
|
|
"status": "ACTIVE",
|
|
"startDate": "2026-01-01T00:00:00.000Z",
|
|
"dueDate": "2026-12-01T00:00:00.000Z",
|
|
"_count": { "shots": 312 }
|
|
}
|
|
],
|
|
"total": 1
|
|
}
|
|
```
|
|
|
|
### Project Type Values
|
|
|
|
| Value | Description |
|
|
|---|---|
|
|
| `STANDARD` | Non-episodic project |
|
|
| `EPISODIC` | Series with episode numbers |
|
|
|
|
### Project Status Values
|
|
|
|
| Value | Description |
|
|
|---|---|
|
|
| `ACTIVE` | Currently in production |
|
|
| `ARCHIVED` | Completed or archived |
|
|
|
|
---
|
|
|
|
## 2. Look Up Shot by Code
|
|
|
|
**Primary endpoint used by pipeline tools (After Effects, Nuke, etc.)**
|
|
|
|
```
|
|
GET /api/ext/shots/lookup?shotCode={shotCode}&projectCode={projectCode}
|
|
```
|
|
|
|
### Query Parameters
|
|
|
|
| Parameter | Required | Description |
|
|
|---|---|---|
|
|
| `shotCode` | Yes | Human-readable shot code, e.g. `UNG_106_010_020` |
|
|
| `projectCode` | Recommended | Project code, e.g. `UNG_S1` — disambiguates if same shot code appears in multiple projects |
|
|
|
|
### Response `200 OK`
|
|
|
|
```json
|
|
{
|
|
"shot": {
|
|
"id": "clxxxxxxxxxxxxxx",
|
|
"shotCode": "UNG_106_010_020",
|
|
"scene": "010",
|
|
"episode": "106",
|
|
"sequence": "010",
|
|
"shotNumber": 20,
|
|
"description": "Hero establishing wide",
|
|
"notes": "Keep sky practical",
|
|
"status": "IN_PROGRESS",
|
|
"priority": "NORMAL",
|
|
"frameStart": 1001,
|
|
"frameEnd": 1120,
|
|
"fps": 24,
|
|
"dueDate": "2026-08-01T00:00:00.000Z",
|
|
"thumbnailUrl": "/api/files/thumb_abc123",
|
|
"createdAt": "2026-06-01T10:00:00.000Z",
|
|
"updatedAt": "2026-07-15T14:30:00.000Z",
|
|
|
|
"shotVersion": "v003",
|
|
"exrOutput": "UNG_106_010_020_cmp_TT_v003",
|
|
|
|
"sourceClip": "A106_C020_0601AB",
|
|
"timecodeStart": "01:00:10:00",
|
|
"timecodeEnd": "01:00:15:00",
|
|
"clipDuration": "00:00:05:00",
|
|
"seqTimecodeStart": "00:59:49:12",
|
|
"seqTimecodeEnd": "00:59:55:00",
|
|
|
|
"project": {
|
|
"id": "cmp6l5mzq0001ua0gz07bk72f",
|
|
"name": "Ungo Season 1",
|
|
"code": "UNG_S1",
|
|
"showId": "UNG"
|
|
},
|
|
"artist": {
|
|
"id": "clxxxxxxxxxxxxxx",
|
|
"name": "Jane Smith",
|
|
"email": "jane@studio.com"
|
|
},
|
|
"tasks": [
|
|
{
|
|
"id": "clxxxxxxxxxxxxxx",
|
|
"title": "Comp",
|
|
"type": "COMP",
|
|
"status": "IN_PROGRESS"
|
|
}
|
|
],
|
|
"versions": [
|
|
{
|
|
"id": "clxxxxxxxxxxxxxx",
|
|
"versionNumber": 3,
|
|
"approvalStatus": "PENDING",
|
|
"reviewStatus": "PENDING",
|
|
"fileUrl": "/api/files/vid_abc123",
|
|
"thumbnailUrl": "/api/files/thumb_abc123",
|
|
"createdAt": "2026-07-15T14:30:00.000Z"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pipeline-Specific Fields
|
|
|
|
| Field | Type | Description |
|
|
|---|---|---|
|
|
| `shotVersion` | `string` | Current version string, e.g. `v003`. Used for output naming. |
|
|
| `exrOutput` | `string` | Base filename for EXR renders, e.g. `UNG_106_010_020_cmp_TT_v003` |
|
|
| `seqTimecodeStart` | `string` | Shot in-point in the locked picture sequence (HH:MM:SS:FF) |
|
|
| `seqTimecodeEnd` | `string` | Shot out-point in the locked picture sequence (HH:MM:SS:FF) |
|
|
| `sourceClip` | `string` | Original camera roll/clip name from the EDL |
|
|
| `timecodeStart` | `string` | Source clip timecode in (HH:MM:SS:FF) |
|
|
| `timecodeEnd` | `string` | Source clip timecode out (HH:MM:SS:FF) |
|
|
| `clipDuration` | `string` | Clip duration (HH:MM:SS:FF) |
|
|
|
|
---
|
|
|
|
## 3. List Episodes
|
|
|
|
```
|
|
GET /api/ext/projects/{projectCode}/episodes
|
|
```
|
|
|
|
### Path Parameters
|
|
|
|
| Parameter | Description |
|
|
|---|---|
|
|
| `projectCode` | Human-readable project code, e.g. `UNG_S1` |
|
|
|
|
### Query Parameters
|
|
|
|
| Parameter | Required | Description |
|
|
|---|---|---|
|
|
| `shotNames` | No | Pass `1` to include full per-episode shot list (default off) |
|
|
|
|
### Response `200 OK`
|
|
|
|
```json
|
|
{
|
|
"project": {
|
|
"id": "cmp6l5mzq0001ua0gz07bk72f",
|
|
"name": "Ungo Season 1",
|
|
"code": "UNG_S1",
|
|
"showId": "UNG"
|
|
},
|
|
"episodes": [
|
|
{
|
|
"episode": "106",
|
|
"shotCount": 42,
|
|
"sequences": ["010", "020", "030"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
When `shotNames=1`, each episode entry also includes a `shots` array with:
|
|
`id`, `shotCode`, `episode`, `sequence`, `status`, `exrOutput`, `seqTimecodeStart`, `seqTimecodeEnd`, `sourceClip`, `timecodeStart`, `timecodeEnd`, `clipDuration`.
|
|
|
|
---
|
|
|
|
## 4. List Shots by Project Code
|
|
|
|
```
|
|
GET /api/ext/projects/{projectCode}/shots
|
|
```
|
|
|
|
### Path Parameters
|
|
|
|
| Parameter | Description |
|
|
|---|---|
|
|
| `projectCode` | Human-readable project code, e.g. `UNG_S1` |
|
|
|
|
### Query Parameters
|
|
|
|
| Parameter | Required | Default | Description |
|
|
|---|---|---|---|
|
|
| `episode` | No | — | Filter by episode, e.g. `106` |
|
|
| `sequence` | No | — | Filter by sequence, e.g. `010` |
|
|
| `status` | No | — | Filter by shot status |
|
|
| `page` | No | `1` | Page number |
|
|
| `limit` | No | `200` | Results per page (max `500`) |
|
|
|
|
### Response `200 OK`
|
|
|
|
```json
|
|
{
|
|
"project": { "id": "...", "name": "...", "code": "UNG_S1", "showId": "UNG" },
|
|
"pagination": { "page": 1, "limit": 200, "total": 42, "pages": 1 },
|
|
"shots": [
|
|
{
|
|
"id": "clxxxxxxxxxxxxxx",
|
|
"shotCode": "UNG_106_010_020",
|
|
"scene": "010",
|
|
"episode": "106",
|
|
"sequence": "010",
|
|
"shotNumber": 20,
|
|
"description": "Hero establishing wide",
|
|
"status": "IN_PROGRESS",
|
|
"priority": "NORMAL",
|
|
"frameStart": 1001,
|
|
"frameEnd": 1120,
|
|
"fps": 24,
|
|
"dueDate": "2026-08-01T00:00:00.000Z",
|
|
"thumbnailUrl": null,
|
|
"exrOutput": "UNG_106_010_020_cmp_TT_v003",
|
|
"seqTimecodeStart": "00:59:49:12",
|
|
"seqTimecodeEnd": "00:59:55:00",
|
|
"sourceClip": "A106_C020_0601AB",
|
|
"timecodeStart": "01:00:10:00",
|
|
"timecodeEnd": "01:00:15:00",
|
|
"clipDuration": "00:00:05:00",
|
|
"artist": { "id": "...", "name": "Jane Smith", "email": "jane@studio.com" },
|
|
"_count": { "versions": 3, "tasks": 2 }
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
> **Note:** `shotVersion` is not included in this response. Use the lookup endpoint to get it.
|
|
|
|
---
|
|
|
|
## 5. List Shots by Project ID
|
|
|
|
```
|
|
GET /api/ext/shots?projectId={projectId}
|
|
```
|
|
|
|
Legacy endpoint. Prefer `/api/ext/projects/{projectCode}/shots` for new integrations.
|
|
|
|
### Query Parameters
|
|
|
|
| Parameter | Required | Description |
|
|
|---|---|---|
|
|
| `projectId` | Yes | Database CUID of the project |
|
|
| `episode` | No | Filter by episode |
|
|
| `status` | No | Filter by shot status |
|
|
| `shotCode` | No | Return only the shot matching this exact code |
|
|
|
|
### Response `200 OK`
|
|
|
|
```json
|
|
{
|
|
"shots": [ /* array of shot objects */ ],
|
|
"total": 42
|
|
}
|
|
```
|
|
|
|
Shot objects include: `id`, `shotCode`, `scene`, `episode`, `shotNumber`, `description`, `status`, `priority`, `frameStart`, `frameEnd`, `fps`, `dueDate`, `createdAt`, `updatedAt`, `artist`, `_count`.
|
|
|
|
> **Note:** Pipeline-specific fields (`exrOutput`, `shotVersion`, `seqTimecodes`, etc.) are **not** included. Use the lookup endpoint.
|
|
|
|
---
|
|
|
|
## 6. Get Single Shot
|
|
|
|
```
|
|
GET /api/ext/shots/{shotId}
|
|
```
|
|
|
|
### By Database ID
|
|
|
|
```http
|
|
GET /api/ext/shots/clxxxxxxxxxxxxxx
|
|
```
|
|
|
|
### By Shot Code
|
|
|
|
```http
|
|
GET /api/ext/shots/UNG_106_010_020?byCode=1&projectId=cmp6l5mzq0001ua0gz07bk72f
|
|
```
|
|
|
|
| Query Param | Description |
|
|
|---|---|
|
|
| `byCode` | Set to `1` to treat the path segment as a shot code |
|
|
| `projectId` | Required when `byCode=1` to scope the lookup |
|
|
|
|
### Response `200 OK`
|
|
|
|
Returns the same full shot object as the lookup endpoint, including tasks and latest version. Pipeline-specific fields (`exrOutput`, `shotVersion`, `seqTimecodeStart`, `seqTimecodeEnd`, `sourceClip`, etc.) are included.
|
|
|
|
---
|
|
|
|
## 7. Create Shot
|
|
|
|
```
|
|
POST /api/ext/shots
|
|
Content-Type: application/json
|
|
```
|
|
|
|
Accepts either `application/json` or `multipart/form-data` (when uploading a thumbnail file).
|
|
|
|
### Request Body
|
|
|
|
| Field | Type | Required | Description |
|
|
|---|---|---|---|
|
|
| `projectId` | string (CUID) | Yes | Database project ID |
|
|
| `scene` | string | Yes | Scene identifier, alphanumeric + underscore, e.g. `010` |
|
|
| `episode` | string | No | Episode number. Required for `EPISODIC` projects |
|
|
| `description` | string | No | Shot description |
|
|
| `artistId` | string (CUID) | No | Assign to an artist by their DB id |
|
|
| `priority` | string | No | `LOW`, `NORMAL`, `HIGH`, or `URGENT` (default `NORMAL`) |
|
|
| `fps` | number | No | Frame rate (default `24`) |
|
|
| `frameStart` | integer | No | First frame number |
|
|
| `frameEnd` | integer | No | Last frame number |
|
|
| `dueDate` | string (ISO 8601) | No | Due date |
|
|
| `thumbnailUrl` | string (URL) | No | Pre-hosted thumbnail URL |
|
|
| `thumbnail` | File | No | Thumbnail image upload (multipart only, max 50 MB) |
|
|
| `shotGroupName` | string | No | Shot group name — created automatically if it doesn't exist |
|
|
| `shotCode` | string | No | Explicit shot code — bypasses auto-generation, must be unique in the project |
|
|
|
|
### Response `201 Created`
|
|
|
|
```json
|
|
{
|
|
"shot": {
|
|
"id": "clxxxxxxxxxxxxxx",
|
|
"shotCode": "UNG_106_010_0010",
|
|
"scene": "010",
|
|
"episode": "106",
|
|
"shotNumber": 10,
|
|
"description": null,
|
|
"status": "WAITING",
|
|
"priority": "NORMAL",
|
|
"fps": 24,
|
|
"frameStart": null,
|
|
"frameEnd": null,
|
|
"dueDate": null,
|
|
"thumbnailUrl": null,
|
|
"projectId": "cmp6l5mzq0001ua0gz07bk72f",
|
|
"artistId": null,
|
|
"shotGroupId": null,
|
|
"createdAt": "2026-07-21T09:00:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error `409 Conflict`
|
|
|
|
Returned when an explicit `shotCode` already exists in the project:
|
|
|
|
```json
|
|
{ "error": "A shot with that code already exists in this project", "shotCode": "UNG_106_010_020" }
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Update Shot
|
|
|
|
```
|
|
PATCH /api/ext/shots/{shotId}
|
|
Content-Type: application/json
|
|
```
|
|
|
|
Used by pipeline tools to write back metadata after a render/upload.
|
|
|
|
### Path Parameters
|
|
|
|
| Parameter | Description |
|
|
|---|---|
|
|
| `shotId` | Database ID of the shot (from the lookup response `shot.id`) |
|
|
|
|
### Request Body
|
|
|
|
| Field | Type | Required | Validation | Description |
|
|
|---|---|---|---|---|
|
|
| `shotVersion` | string | Yes* | Must match `v###`, e.g. `v004` | Sets the current version string |
|
|
|
|
*At least one field must be provided; currently only `shotVersion` is supported.
|
|
|
|
### Example Request
|
|
|
|
```http
|
|
PATCH /api/ext/shots/clxxxxxxxxxxxxxx
|
|
Authorization: Bearer your-api-key
|
|
Content-Type: application/json
|
|
|
|
{ "shotVersion": "v004" }
|
|
```
|
|
|
|
### Response `200 OK`
|
|
|
|
```json
|
|
{ "success": true, "shot": { "id": "clxxxxxxxxxxxxxx", "shotVersion": "v004" } }
|
|
```
|
|
|
|
---
|
|
|
|
## Error Responses
|
|
|
|
| Status | Meaning |
|
|
|---|---|
|
|
| `400` | Missing required parameter |
|
|
| `401` | Missing or invalid API key |
|
|
| `403` | Forbidden (session-auth routes only) |
|
|
| `404` | Resource not found |
|
|
| `409` | Conflict (duplicate shot code) |
|
|
| `413` | Payload too large (thumbnail > 50 MB) |
|
|
| `422` | Validation error |
|
|
| `500` | Internal server error |
|
|
|
|
```json
|
|
{ "error": "shotCode query param is required" }
|
|
{ "error": "Validation error", "details": [ ... ] }
|
|
```
|
|
|
|
---
|
|
|
|
## After Effects Connector — Endpoints Used
|
|
|
|
For reference, the endpoints called by `VFXReviewConnector.jsx`:
|
|
|
|
| Action | Endpoint |
|
|
|---|---|
|
|
| Populate Episode dropdown | `GET /api/ext/projects/{PROJECT_CODE}/episodes` |
|
|
| Populate Shot dropdown | `GET /api/ext/projects/{PROJECT_CODE}/shots?episode={value}` |
|
|
| All shot operations (overlay, queue, preview, delivery) | `GET /api/ext/shots/lookup?shotCode={code}&projectCode={PROJECT_CODE}` |
|
|
| Increment Shot Version | `PATCH /api/ext/shots/{shot.id}` with `{ "shotVersion": "v00X" }` |
|