# Kaitoi REST API > Version: 1.0.0 Canonical OpenAPI schema: https://api.studio.kaitoi.io/api/v1/openapi.json This document is generated from the public OpenAPI schema. Prefer the OpenAPI JSON for exact enum values, schemas, and validation rules. ## Overview Kaitoi's REST API exposes a stable integration surface for projects, editable project documents, graph inspection, node discovery, files, runs, and future automation workflows. **Authentication** Send a programmatic API key as `Authorization: Bearer `. Keys can be scoped with endpoint-specific permissions such as `projects:read`, `projects:write`, `templates:read`, `templates:run`, `runs:execute`, `graphs:read`, `node_types:read`, `files:read`, and `files:write`. Account credit balance reads require `account_credits:read`. Kaitoi has two API key usage modes: - **Backend service keys** are the default for trusted code: your backend, CLI tools, desktop apps, scripts, workers, and CI jobs. These keys are bearer secrets and should not be shipped in browser JavaScript. - **Browser app keys** are for direct calls from frontend JavaScript. They must be attached to a Developer App with explicit allowed origins. The origin check lets browsers call the public REST API only from those registered origins, but it is not a substitute for keeping sensitive secrets on a trusted server. Browser keys are visible to end users, so prefer read-only or narrowly scoped permissions. For production web apps, the safest pattern is usually: browser frontend calls your backend, and your backend calls Kaitoi with a Backend service key. Use Browser app keys only when you intentionally want direct browser-to-Kaitoi calls and have chosen scopes and quotas accordingly. **Base URL** All REST API endpoints are served under `/api/v1`. The global FastAPI documentation endpoints remain disabled in production; this OpenAPI document is filtered to REST API routes only. **API Artifacts** The OpenAPI schema is available at `/api/v1/openapi.json`. A generated Postman collection is available at `/api/v1/postman.json` and stays in sync with this schema as the REST API grows. LLM-friendly documentation is available at `/api/v1/llms.txt`, with a fuller Markdown reference at `/api/v1/llms-full.txt`. **Rate Limits** Requests are rate limited per client IP before authentication and per API key after authentication. `429 RATE_LIMITED` responses include a `Retry-After` header and a JSON error body. Production/staging deployments enforce these limits through the shared Redis-backed limiter so quotas apply across backend instances. The default production behavior is fail-closed: if the shared limiter is unavailable, requests return `503 RATE_LIMIT_UNAVAILABLE` with a short `Retry-After` hint so clients back off instead of bypassing quotas. Operators can explicitly configure a per-instance local degradation mode for environments that prefer temporary availability over globally strict quotas. Local development uses an in-process fallback unless Redis rate limiting is explicitly enabled. **Errors** Errors use a consistent shape: `{ "error": { "code", "message", "details" } }`. When clients send `X-Request-Id`, the same identifier is echoed as `error.requestId` to simplify support and audit correlation. **Pagination** List endpoints use opaque keyset cursors. Treat `nextCursor` as an opaque token; do not parse or construct it client-side. **Project Documents** Use `/projects/{projectId}/document` as the canonical editable project surface. `GET /projects/{projectId}/graph` is a read-oriented view and is not intended to be round-tripped back into document writes. Document writes require `expectedVersion` for optimistic concurrency and support idempotent retries. **Lifecycle Deletes** Project deletion is version-gated and archives the project from the public REST surface. Completed file deletion is blocked while active projects reference the file, and fails closed when a project's public file-reference index is unknown. ## Integration Model - Backend service keys are bearer secrets for trusted code and should not be exposed in browsers. - Browser app keys are for direct frontend JavaScript calls, require a Developer App, and must match an allowed origin. - Direct browser keys should use narrow scopes; execute/write scopes are better handled by a developer backend relay. - File uploads use a create/upload/complete handshake. The signed upload URL is not a Kaitoi API endpoint and does not use the bearer key. - Runs execute either saved Kaitoi projects (`projectId`) or transient inline graphs (`graph`) through the same `/api/v1/runs` lifecycle. - Run events are available through polling and Server-Sent Events. ## Error Shape ```json { "error": { "code": "ERROR_CODE", "message": "Human-readable message.", "details": {} } } ``` ## Endpoint Reference ### Account #### `GET /api/v1/account/credits` Get account credits Returns the authenticated user's current spendable account credit balance and PAYG/subscription breakdown. Requires the `account_credits:read` scope. - Operation ID: `account.getCredits` - Parameters: - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Current account credit balance. - `401`: Authentication is required. - `403`: The API key is missing the required scope. - `422`: Validation Error - `503`: Managed billing is not enabled. - Example response (`200`): ```json { "balanceCents": 1250, "balanceDollars": 12.5, "balanceMicrocredits": "1250110000", "ledgerUnitVersion": 2, "managedProviderReceivableMicrocredits": "0", "paygBalanceCents": 750, "paygBalanceDollars": 7.5, "paygBalanceMicrocredits": "750110000", "subscriptionBalanceCents": 500, "subscriptionBalanceDollars": 5.0, "subscriptionBalanceMicrocredits": "500000000", "subscriptionMonthlyGrantCents": 1000, "subscriptionMonthlyGrantMicrocredits": "1000000000", "subscriptionPeriodEnd": "2026-06-30T00:00:00Z", "subscriptionPlanTier": "pro", "subscriptionRolloverCapCents": 2000, "subscriptionRolloverCapMicrocredits": "2000000000", "updatedAt": "2026-06-09T12:00:00Z" } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### Files #### `POST /api/v1/files` Upload small file Small-file convenience upload. This route is capped at 25 MB and is deprecated for developer-platform use; prefer POST /files/uploads. - Operation ID: `files.upload` - Parameters: - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `multipart/form-data`: `upload` - Responses: - `201`: Uploaded file metadata. - `400`: The uploaded file is empty or invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `409`: The Idempotency-Key conflicts with a different request. - `413`: The uploaded file exceeds the per-request size limit. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - `507`: The upload would exceed the account storage limit. - Example response (`201`): ```json { "contentType": "image/png", "createdAt": "2026-05-24T12:00:00+00:00", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "input.png", "sizeBytes": 12345, "updatedAt": "2026-05-24T12:00:00+00:00" } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`413`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`507`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/files/uploads` Create file upload session Creates a direct-to-storage upload session. This endpoint does not upload the file bytes. Use the returned instructions as follows: 1. Send a separate PUT request to `upload.url` using `upload.method` and every header in `upload.headers`. Do not send the API Bearer token to that URL; the signed URL is the temporary upload credential. 2. After the PUT succeeds, call `POST /files/uploads/{upload_id}/complete` with this response's `uploadId`. 3. Use the completed response's `fileId` as a file input in later API calls. For large files, `storageMethod` may be `multipart`; in that case, PUT each item in `parts` and pass the returned ETags to the complete endpoint. If the session was created with `sha256`, the API verifies the stored object checksum before completion; multipart completions must include `actualSha256`. Use Idempotency-Key so retries return the same reservation and signed URL. - Operation ID: `files.createUpload` - Parameters: - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicFileUploadCreateRequest` - Example: ```json { "contentType": "image/png", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "filename": "input.png", "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "sizeBytes": 12345 } ``` - Responses: - `201`: Upload session and signed upload instructions. - `400`: The upload session request is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `409`: The Idempotency-Key conflicts with a different request. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - `507`: The upload would exceed the account storage limit. - Example response (`201`): ```json { "expiresAt": "2026-05-24T13:00:00+00:00", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "storageMethod": "single_part", "upload": { "headers": { "Content-Type": "image/png" }, "method": "PUT", "url": "https://storage.example.com/signed-upload-url" }, "uploadId": "upl_8d7f7f7e1dd64d3e8e2d0d2e4b3c9a1f" } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`507`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `DELETE /api/v1/files/uploads/{upload_id}` Abort file upload Aborts a pending upload session and releases its storage reservation. - Operation ID: `files.abortUpload` - Parameters: - `upload_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `204`: Upload session aborted or already absent. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/files/uploads/{upload_id}/complete` Complete file upload Verifies that the bytes were uploaded to the signed URL returned by `Create file upload session`, then promotes the object to a durable REST API file record. This call is idempotent by `upload_id`; retrying after a successful completion returns the same file metadata. When the session was created with `sha256`, the API verifies the stored object's SHA-256 before completing it. - Operation ID: `files.completeUpload` - Parameters: - `upload_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicFileUploadCompleteRequest` - Example: ```json { "actualSha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "actualSizeBytes": 12345 } ``` - Responses: - `200`: Completed file metadata. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The upload session does not exist. - `410`: The upload session expired or was aborted. - `422`: The uploaded object failed completion verification. - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "contentType": "image/png", "createdAt": "2026-05-24T12:00:00+00:00", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "input.png", "sizeBytes": 12345, "updatedAt": "2026-05-24T12:00:00+00:00" } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`410`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`422`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/files/{file_id}` Get file Returns metadata for a REST API uploaded file owned by the authenticated account. - Operation ID: `files.get` - Parameters: - `file_id` in `path` (`string` required) - `externalUserId` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: File metadata. - `400`: The file id is malformed. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The file does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "contentType": "image/png", "createdAt": "2026-05-24T12:00:00+00:00", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "input.png", "sizeBytes": 12345, "updatedAt": "2026-05-24T12:00:00+00:00" } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `DELETE /api/v1/files/{file_id}` Delete file Deletes a completed REST API file owned by the authenticated account. Deletion is blocked while active saved projects reference the file, and fails closed when any plausible active project has not indexed file references yet. - Operation ID: `files.delete` - Parameters: - `file_id` in `path` (`string` required) - `externalUserId` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `204`: File deleted. - `400`: The file id is malformed. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The file does not exist or is not owned by this user. - `409`: The file is still referenced by active projects, or reference safety cannot be proven yet. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `500`: The file could not be deleted from storage. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`500`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/files/{file_id}/download-url` Get file download URL Returns a short-lived signed URL for downloading a REST API uploaded file. The URL is time-limited and should not be stored permanently. - Operation ID: `files.getDownloadUrl` - Parameters: - `file_id` in `path` (`string` required) - `expiresInSeconds` in `query` (`integer`) - `externalUserId` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Signed download URL. - `400`: The file id is malformed. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The file does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "downloadUrl": "https://example.com/data/2f8f8c5d.png?token=...", "expiresAt": "2026-05-24T13:00:00+00:00", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png" } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### Node Types #### `GET /api/v1/node-type-thumbnails/{node_type}` Get node type thumbnail Serves a short-lived signed node type thumbnail URL returned by the node type list and detail endpoints. The token in the URL authorizes only this thumbnail; do not send the REST API bearer token to this URL. - Operation ID: `nodeTypes.getThumbnail` - Parameters: - `node_type` in `path` (`string` required) - `token` in `query` (`string` required) - `index` in `query` (`integer`) - Responses: - `200`: Node type thumbnail image. - `400`: The node type path or thumbnail source is malformed. - `403`: The signed thumbnail URL is invalid or expired. - `404`: The node type or thumbnail was not found. - `422`: Validation Error - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/node-types` List node types Returns node types available to the authenticated user. Use this endpoint to discover compatible node identifiers before building automation against a graph. - Operation ID: `nodeTypes.list` - Parameters: - `limit` in `query` (`integer` | `null`) - `cursor` in `query` (`string` | `null`) - `search` in `query` (`string` | `null`) - `fields` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A page of node type summaries. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "data": [], "hasMore": false } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/node-types/{node_type}` Get node type Returns the public schema for one node type, including its exposed input and output pins. Node type paths are validated before lookup so user-node paths cannot traverse storage boundaries. - Operation ID: `nodeTypes.get` - Parameters: - `node_type` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Detailed node type metadata. - `400`: The node type path is malformed or unsafe. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The node type was not found. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "category": "Image", "description": "Load an image file into the graph.", "icon": "image", "inputs": [ { "exposed": true, "label": "Image", "name": "path", "type": "file" } ], "outputs": [ { "exposed": false, "label": "Image", "name": "image", "type": "image" } ], "source": "builtin", "tags": [ "image", "loader" ], "title": "Load Image", "type": "builtin/image/load_image" } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### Projects #### `POST /api/v1/projects` Create project Creates an empty saved project for the authenticated API key owner. Use Idempotency-Key so retries do not create duplicate projects. - Operation ID: `projects.create` - Parameters: - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicProjectCreateRequest` - Example: ```json { "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "metadata": { "plan": "pro" }, "name": "Jane's Workspace" } ``` - Responses: - `201`: Created project metadata. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `409`: The Idempotency-Key conflicts with a different request. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`201`): ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "id": "proj_123", "name": "Machine", "nodeCount": 12, "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7 } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/projects` List projects Returns projects visible to the authenticated API key owner. Results are ordered by most recently updated project first and paginated with opaque keyset cursors. - Operation ID: `projects.list` - Parameters: - `limit` in `query` (`integer` | `null`) - `cursor` in `query` (`string` | `null`) - `externalId` in `query` (`string` | `null`) - `externalUserId` in `query` (`string` | `null`) - `externalWorkspaceId` in `query` (`string` | `null`) - `search` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A page of project summaries. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "data": [], "hasMore": false } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/projects/{project_id}` Get project Returns public metadata for a single project, including the current project version used by future write endpoints for optimistic concurrency. - Operation ID: `projects.get` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Project metadata. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "id": "proj_123", "name": "Machine", "nodeCount": 12, "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7 } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `PATCH /api/v1/projects/{project_id}` Update project Updates project display metadata and developer-provided external identifiers using optimistic concurrency. - Operation ID: `projects.update` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicProjectUpdateRequest` - Example: ```json { "expectedVersion": 1, "externalUserId": "user_789", "metadata": { "plan": "enterprise" }, "name": "Jane's Renamed Workspace" } ``` - Responses: - `200`: Updated project metadata. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `409`: The expected project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "id": "proj_123", "name": "Machine", "nodeCount": 12, "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7 } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `DELETE /api/v1/projects/{project_id}` Delete project Deletes a project from the public REST API surface using optimistic concurrency. The project bundle and metadata are removed from project storage so the project no longer appears in Studio or public REST project lists. - Operation ID: `projects.delete` - Parameters: - `project_id` in `path` (`string` required) - `expectedVersion` in `query` (`integer` required) - `authorization` in `header` (`string` | `null`) - Responses: - `204`: Project deleted. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `409`: The expected project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/projects/{project_id}/document` Get project document Returns the canonical editable project document. This shape is designed for developer round trips and uses input maps plus from/to connection tuples, unlike the read-only /graph inspection shape. - Operation ID: `projects.getDocument` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: An editable project document. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "document": { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } }, "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "metadata": { "plan": "pro" }, "name": "Machine", "projectId": "proj_123", "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7, "warnings": [] } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `PUT /api/v1/projects/{project_id}/document` Replace project document Replaces the editable project document using optimistic concurrency. The public document graph is validated, translated into Studio's internal bundle shape, and saved as a normal project. Use Idempotency-Key so network retries can replay the saved response. - Operation ID: `projects.replaceDocument` - Parameters: - `project_id` in `path` (`string` required) - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicProjectDocumentReplaceRequest` - Example: ```json { "document": { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } }, "expectedVersion": 7, "metadata": { "plan": "pro" }, "name": "Machine" } ``` - Responses: - `200`: The saved editable project document. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `409`: The expected project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "document": { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } }, "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "metadata": { "plan": "pro" }, "name": "Machine", "projectId": "proj_123", "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7, "warnings": [] } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `PATCH /api/v1/projects/{project_id}/document` Patch project document Applies ordered semantic document operations atomically using optimistic concurrency. The resulting document is validated and saved through the same path as PUT /document. Use Idempotency-Key so network retries can replay the saved response. - Operation ID: `projects.patchDocument` - Parameters: - `project_id` in `path` (`string` required) - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicProjectDocumentPatchRequest` - Example: ```json { "expectedVersion": 7, "operations": [ { "node": { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" }, "op": "addNode" }, { "from": [ "node_123", "outputPrompt" ], "op": "connect", "to": [ "node_456", "inputPrompt" ] } ] } ``` - Responses: - `200`: The saved editable project document plus patch metadata. - `400`: A patch operation is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `409`: The expected project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "document": { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } }, "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "metadata": { "plan": "pro" }, "name": "Machine", "projectId": "proj_123", "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7, "warnings": [] } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/projects/{project_id}/graph` Get project graph Returns a sanitized graph snapshot for a project. Internal metadata, execution history, and private storage paths are intentionally omitted. - Operation ID: `projects.getGraph` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A sanitized project graph snapshot. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "connections": [], "nodes": [ { "id": "node_123", "inputs": [], "outputs": [ { "exposed": false, "name": "outputPrompt", "type": "string" } ], "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ], "projectId": "proj_123", "version": 7 } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/projects/{project_id}/graph/nodes/{node_id}` Get project graph node Returns one sanitized placed node from a project graph. Use fields=inputs,outputs for compact connection-pin discovery. Omit fields for the existing sanitized full graph-node shape. - Operation ID: `projects.getGraphNode` - Parameters: - `project_id` in `path` (`string` required) - `node_id` in `path` (`string` required) - `fields` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A sanitized placed graph node or compact pin projection. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project or graph node does not exist, or the project is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `PATCH /api/v1/projects/{project_id}/metadata` Update project metadata Updates project display metadata and developer-provided external identifiers using optimistic concurrency. Prefer this explicit route for metadata-only updates; PATCH /projects/{project_id} is kept as a compatibility alias. - Operation ID: `projects.updateMetadata` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicProjectUpdateRequest` - Example: ```json { "expectedVersion": 1, "externalUserId": "user_789", "metadata": { "plan": "enterprise" }, "name": "Jane's Renamed Workspace" } ``` - Responses: - `200`: Updated project metadata. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `409`: The expected project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "id": "proj_123", "name": "Machine", "nodeCount": 12, "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7 } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/projects/{project_id}/spend` Get project spend Returns actual billed credits from project-attributed ledger transactions. The response does not expose account balances, payment methods, or dollar amounts. - Operation ID: `projects.getSpend` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Project spend summary. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "byChargeType": { "provider": 24 }, "byChargeTypeMicrocredits": { "provider": "24110000" }, "byNode": [ { "billedRuns": 2, "credits": 24, "lastUsedAt": "2026-05-24T12:00:00Z", "microcredits": "24110000", "nodeId": "node_456", "nodeTitle": "Generate Image", "nodeType": "builtin/image/generate" } ], "byProvider": { "fal": 24 }, "byProviderMicrocredits": { "fal": "24110000" }, "projectId": "project_123", "recentRuns": [], "totalCredits": 42, "totalMicrocredits": "42110000" } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### Runs #### `POST /api/v1/runs` Create run Accepts a run request for either a saved project (`projectId`) or an inline transient graph (`graph`). The server validates file input references, target nodes, node types, and input pins; snapshots the selected graph source; creates an accepted run record; and dispatches execution asynchronously through the same run lifecycle. - Operation ID: `runs.create` - Parameters: - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicRunCreateRequest` - Examples: _Saved project run_ ```json { "externalUserId": "user_789", "inputOverrides": { "node_456": { "inputImage": { "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "type": "file" }, "prompt": { "type": "string", "value": "Describe this image" } } }, "projectId": "project_123", "targetNodeIds": [ "node_456" ] } ``` _Inline graph run_ ```json { "externalUserId": "user_789", "graph": { "connections": [ { "from": [ "prompt", "outputPrompt" ], "to": [ "echo", "inputText" ] } ], "nodes": [ { "id": "prompt", "inputs": { "inputPrompt": { "type": "string", "value": "Write a short caption" } }, "position": { "x": 0, "y": 0 }, "title": "Prompt", "type": "builtin/utils/text_prompt" }, { "id": "echo", "inputs": {}, "position": { "x": 360, "y": 0 }, "type": "builtin/utils/text" } ] }, "targetNodeIds": [ "echo" ] } ``` - Responses: - `202`: Accepted run record. - `400`: The run request or input references are invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The project or an input file was not found. - `409`: The Idempotency-Key conflicts with a different request. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The run could not be dispatched or the shared REST API rate limiter is temporarily unavailable. - Example response (`202`): ```json { "createdAt": "2026-01-01T00:00:00Z", "creditsUsed": 12, "executionTimeMs": 2143, "id": "run_abc123", "outputs": { "outputImage": { "contentType": "image/png", "downloadUrl": "https://files.example/d/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "random_image.png", "previewUrl": "https://files.example/p/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "sizeBytes": 482931, "type": "file" } }, "sourceType": "inline_graph", "spend": { "byChargeType": { "provider": 12 }, "byNode": [ { "billedRuns": 1, "credits": 12, "nodeId": "image", "nodeTitle": "Generate Image", "nodeType": "builtin/image/generate" } ], "byProvider": { "fal": 12 }, "totalCredits": 12 }, "status": "succeeded", "targetNodeIds": [ "image" ], "updatedAt": "2026-01-01T00:00:03Z" } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/runs/{run_id}` Get run Returns a REST API run record owned by the authenticated API key owner. - Operation ID: `runs.get` - Parameters: - `run_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Run record. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The run does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-01-01T00:00:00Z", "creditsUsed": 12, "executionTimeMs": 2143, "id": "run_abc123", "outputs": { "outputImage": { "contentType": "image/png", "downloadUrl": "https://files.example/d/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "random_image.png", "previewUrl": "https://files.example/p/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "sizeBytes": 482931, "type": "file" } }, "sourceType": "inline_graph", "spend": { "byChargeType": { "provider": 12 }, "byNode": [ { "billedRuns": 1, "credits": 12, "nodeId": "image", "nodeTitle": "Generate Image", "nodeType": "builtin/image/generate" } ], "byProvider": { "fal": 12 }, "totalCredits": 12 }, "status": "succeeded", "targetNodeIds": [ "image" ], "updatedAt": "2026-01-01T00:00:03Z" } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/runs/{run_id}/apply-to-project` Apply run outputs to project Persists outputs from a succeeded saved-project run back into that saved project. This is the public API equivalent of saving the server-owned run result state after execution; it does not accept client-supplied output values. The request uses optimistic concurrency and fails if the project changed after the run was created. - Operation ID: `runs.applyOutputsToProject` - Parameters: - `run_id` in `path` (`string` required) - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicRunApplyOutputsRequest` - Responses: - `200`: Project version and output keys that were applied. - `400`: The output selection is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The run, project, or run output file was not found. - `409`: The run is not applicable or the project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/runs/{run_id}/cancel` Cancel run Requests cancellation for a REST API run. Accepted or queued runs become terminal canceled immediately; running runs are marked cancelRequested for the execution layer to stop at the next safe point. - Operation ID: `runs.cancel` - Parameters: - `run_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Run record after the cancellation request. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The run does not exist or is not owned by this user. - `409`: The run is already terminal and cannot be canceled. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "createdAt": "2026-01-01T00:00:00Z", "creditsUsed": 12, "executionTimeMs": 2143, "id": "run_abc123", "outputs": { "outputImage": { "contentType": "image/png", "downloadUrl": "https://files.example/d/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "random_image.png", "previewUrl": "https://files.example/p/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "sizeBytes": 482931, "type": "file" } }, "sourceType": "inline_graph", "spend": { "byChargeType": { "provider": 12 }, "byNode": [ { "billedRuns": 1, "credits": 12, "nodeId": "image", "nodeTitle": "Generate Image", "nodeType": "builtin/image/generate" } ], "byProvider": { "fal": 12 }, "totalCredits": 12 }, "status": "succeeded", "targetNodeIds": [ "image" ], "updatedAt": "2026-01-01T00:00:03Z" } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/runs/{run_id}/events` List run events Returns persisted lifecycle, progress, and log events for a REST API run. Cursors are opaque and are only valid for the same run and API key owner that issued them. - Operation ID: `runs.listEvents` - Parameters: - `run_id` in `path` (`string` required) - `limit` in `query` (`integer` | `null`) - `cursor` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A page of run events. - `400`: The cursor or limit is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The run does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "data": [], "hasMore": false } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/runs/{run_id}/events/stream` Stream run events Streams the same persisted lifecycle, progress, and log events returned by the polling endpoint using Server-Sent Events. The SSE id is an opaque cursor and can be sent back as Last-Event-ID or as the cursor query parameter to resume the stream. - Operation ID: `runs.streamEvents` - Parameters: - `run_id` in `path` (`string` required) - `cursor` in `query` (`string` | `null`) - `Last-Event-ID` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A text/event-stream response containing PublicRunEvent payloads. - `400`: The cursor is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The run does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### Storyboards #### `GET /api/v1/projects/{project_id}/storyboard` Get project storyboard Returns the project's current storyboard as a Kaitoi Storyboard Document v1. This is a read-only export of storyboard state. - Operation ID: `projects.getStoryboard` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Project storyboard document. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: Project not found. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "projectId": "proj_123", "summary": { "mediaCount": 0, "sceneCount": 1, "shotCount": 1 }, "version": 7 } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `PUT /api/v1/projects/{project_id}/storyboard` Update project storyboard Applies a Kaitoi Storyboard Document v1 to a saved project using optimistic concurrency. Supports replacing the current storyboard or appending imported scenes. Use Idempotency-Key so network retries can replay the saved response. - Operation ID: `projects.updateStoryboard` - Parameters: - `project_id` in `path` (`string` required) - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicProjectStoryboardReplaceRequest` - Example: ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "expectedVersion": 7, "mode": "replace" } ``` - Responses: - `200`: Saved project storyboard document and apply summary. - `400`: The storyboard document is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: Project not found. - `409`: The expected project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "mode": "replace", "projectId": "proj_123", "summary": { "documentMedia": 0, "extraPanelMedia": 0, "mediaCount": 12, "mediaImported": 12, "mediaSkipped": 0, "sceneCount": 1, "shotCount": 12, "unresolvedMedia": 0, "urlMedia": 0, "warnings": [] }, "version": 8 } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/storyboards/schema/kaitoi-storyboard-document.v1` Get storyboard document schema Returns the canonical Kaitoi Storyboard Document v1 JSON Schema. This route is unauthenticated so public API clients can validate documents before sending them. - Operation ID: `storyboards.getSchema` - Responses: - `200`: Kaitoi Storyboard Document v1 JSON Schema. #### `POST /api/v1/storyboards/to-timeline` Convert storyboard document to timeline document Converts a Kaitoi Storyboard Document v1 into a Kaitoi Timeline Document v1 without mutating any project. Storyboard shots become sequential timeline clips; URL, document, unresolved, and unsupported media references are skipped with warnings instead of being fetched. - Operation ID: `storyboards.toTimeline` - Parameters: - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicStoryboardToTimelineRequest` - Example: ```json { "defaultDuration": 3, "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "frameRate": 30, "name": "Storyboard Timeline" } ``` - Responses: - `200`: Generated timeline document and non-fatal conversion warnings. - `400`: The storyboard document is invalid or cannot be converted. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "warnings": [] } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/storyboards/validate` Validate storyboard document Validates a Kaitoi Storyboard Document v1 against the same schema enforced by the backend. - Operation ID: `storyboards.validate` - Parameters: - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicStoryboardValidateRequest` - Example: ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 } } ``` - Responses: - `200`: Validated storyboard document and derived summary. - `400`: The storyboard document is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "summary": { "mediaCount": 0, "sceneCount": 1, "shotCount": 1 }, "valid": true } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### Templates #### `GET /api/v1/templates` List templates Returns user-owned templates visible to the authenticated API key owner. Use `hasEndpoint=true` to discover app-ready templates that already have at least one active endpoint. Use endpoint docs to inspect the external endpoint contract and the owner-authenticated Public API run path. - Operation ID: `templates.list` - Parameters: - `limit` in `query` (`integer` | `null`) - `cursor` in `query` (`string` | `null`) - `hasEndpoint` in `query` (`boolean` | `null`) - `search` in `query` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A page of template summaries. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "data": [], "hasMore": false } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/templates/{template_id}` Get template Returns public metadata for one user-owned template, including active endpoint summaries when the template has app-ready endpoints. - Operation ID: `templates.get` - Parameters: - `template_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Template metadata. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The template does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "category": "image", "createdAt": "2026-06-01T12:00:00+00:00", "description": "Creates a stylized portrait from a prompt.", "endpoints": [], "hasData": false, "hasEndpoint": true, "name": "Portrait Generator", "nodeCount": 4, "source": "user", "tags": [ "portrait" ], "templateId": "template_123", "updatedAt": "2026-06-02T12:00:00+00:00" } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/templates/{template_id}/endpoints` List template endpoints Returns active external endpoints for a user-owned template. Endpoint API secrets are never returned; use the endpoint docs response to learn the required Authorization header shape. - Operation ID: `templates.listEndpoints` - Parameters: - `template_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A list of active endpoint summaries. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The template does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "data": [], "hasMore": false } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/templates/{template_id}/endpoints/{endpoint_id}/docs` Get template endpoint docs Returns docs for an active external endpoint on a user-owned template: authentication header shape, submit/status paths, and exposed input and output fields. This does not return endpoint API keys and does not run the endpoint. - Operation ID: `templates.getEndpointDocs` - Parameters: - `template_id` in `path` (`string` required) - `endpoint_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Endpoint documentation for app builders. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The template endpoint does not exist or is not owned by this user. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "auth": { "apiKeyReturnedOnce": true, "header": "Authorization", "scheme": "bearer", "valueFormat": "Bearer " }, "description": "Creates a stylized portrait from a prompt.", "endpointId": "endpoint_123", "externalRun": { "contentTypes": [ "application/json", "multipart/form-data" ], "info": "/v1/run/endpoint_123/info", "method": "POST", "status": "/v1/run/endpoint_123/{task_id}", "statusMethod": "GET", "submit": "/v1/run/endpoint_123" }, "inputs": [ { "dataType": "string", "fieldName": "prompt", "kind": "primitive", "name": "Prompt" } ], "name": "Generate Portrait", "outputs": [ { "dataType": "image", "fieldName": "image", "kind": "file", "name": "Image" } ], "ownerRun": { "auth": "publicApiBearerToken", "available": true, "contentTypes": [ "application/json" ], "method": "POST", "note": "Owner-authenticated execution uses the public API key owner and may spend credits.", "status": "/api/v1/templates/template_123/endpoints/endpoint_123/runs/{taskId}", "statusMethod": "GET", "stream": "/api/v1/templates/template_123/endpoints/endpoint_123/runs/{taskId}/events/stream", "streamMethod": "GET", "submit": "/api/v1/templates/template_123/endpoints/endpoint_123/runs" }, "requestExample": { "inputs": { "prompt": "A cinematic portrait" } }, "templateId": "template_123" } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/templates/{template_id}/endpoints/{endpoint_id}/runs` Run template endpoint Submits an owner-authenticated run for an active endpoint on a user-owned template. The API key user must own the template and endpoint, the key must have `templates:run`, file inputs must reference Public API fileIds, and execution may spend the owner's credits. Use `Idempotency-Key` so network retries do not submit duplicate jobs. - Operation ID: `templates.run` - Parameters: - `template_id` in `path` (`string` required) - `endpoint_id` in `path` (`string` required) - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicTemplateRunCreateRequest` - Example: ```json { "externalUserId": "user_789", "inputs": { "image": { "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "type": "file" }, "prompt": { "type": "string", "value": "A cinematic robot portrait" } } } ``` - Responses: - `202`: Accepted template endpoint run. - `400`: The run inputs or file references are invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The template, endpoint, or input file was not found. - `409`: The Idempotency-Key conflicts with a different request. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/templates/{template_id}/endpoints/{endpoint_id}/runs/{task_id}` Get template endpoint run Returns a template endpoint run owned by the authenticated API key owner. Polling requires `templates:run` because completed results are part of the execution contract. - Operation ID: `templates.getRun` - Parameters: - `template_id` in `path` (`string` required) - `endpoint_id` in `path` (`string` required) - `task_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Template endpoint run status and outputs. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The template endpoint run was not found. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/templates/{template_id}/endpoints/{endpoint_id}/runs/{task_id}/events/stream` Stream template endpoint run events Streams compact lifecycle and progress events for an owner-authenticated template endpoint run using Server-Sent Events. This endpoint requires `templates:run` because progress and completed results are part of the execution contract. - Operation ID: `templates.streamRunEvents` - Parameters: - `template_id` in `path` (`string` required) - `endpoint_id` in `path` (`string` required) - `task_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: A text/event-stream response containing compact template run events. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: The template endpoint run was not found. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### Timelines #### `GET /api/v1/projects/{project_id}/timeline` Get project timeline Returns the project's current active timeline as a Kaitoi Timeline Document v1. This is a read-only export of timeline state. - Operation ID: `projects.getTimeline` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: Project timeline document. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: Project not found. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "projectId": "proj_123", "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "version": 7 } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `PUT /api/v1/projects/{project_id}/timeline` Update project timeline Replaces a saved project's active timeline with a Kaitoi Timeline Document v1 using optimistic concurrency. Use Idempotency-Key so network retries can replay the saved response. This replaces the whole project timeline; if the project currently has multiple timeline scenes, they are collapsed to the single timeline contained in the document. - Operation ID: `projects.updateTimeline` - Parameters: - `project_id` in `path` (`string` required) - `Idempotency-Key` in `header` (`string` | `null`) - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicProjectTimelineReplaceRequest` - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "expectedVersion": 7 } ``` - Responses: - `200`: Saved project timeline document and summary. - `400`: The timeline document is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: Project not found. - `409`: The expected project version is stale. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "mode": "replace", "projectId": "proj_123", "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "version": 8 } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`409`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/projects/{project_id}/timeline/export/otio` Export project timeline as OTIO Exports a saved project's current active timeline as an OpenTimelineIO ZIP bundle. Media references in the OTIO file are portable filenames, not server paths, storage roots, private bucket keys, or signed URLs. - Operation ID: `projects.exportTimelineOtio` - Parameters: - `project_id` in `path` (`string` required) - `authorization` in `header` (`string` | `null`) - Responses: - `200`: OTIO ZIP bundle. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `404`: Project not found. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `501`: OpenTimelineIO is unavailable. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`404`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`501`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/timelines/export/otio` Export timeline document as OTIO Converts a Kaitoi Timeline Document v1 to an OpenTimelineIO ZIP bundle. The ZIP contains `timeline.otio` and any media files the API can resolve from the authenticated account. OTIO media references are portable filenames, never server paths, storage roots, private bucket keys, or signed URLs. - Operation ID: `timelines.exportOtio` - Parameters: - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicTimelineOtioExportRequest` - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 } } ``` - Responses: - `200`: OTIO ZIP bundle. - `400`: The timeline document is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `501`: OpenTimelineIO is unavailable. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`501`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/timelines/import/otio` Import OTIO to timeline document Imports an `.otio` file or ZIP bundle into a Kaitoi Timeline Document v1. When media files are present in the ZIP, they are persisted to the authenticated account's library and the returned document references those library filenames. Missing media produces warnings instead of server path leaks. - Operation ID: `timelines.importOtio` - Parameters: - `authorization` in `header` (`string` | `null`) - Request body: - `multipart/form-data`: `importOtio` - Responses: - `200`: Timeline document converted from OTIO, with persisted media info. - `400`: The OTIO upload is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `413`: The upload exceeds the request size limit. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `501`: OpenTimelineIO is unavailable. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "files": [ { "dataType": "video", "filename": "hero.mp4", "storagePath": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.mp4" } ], "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "warnings": [] } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`413`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`501`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `POST /api/v1/timelines/import/otio/parse` Parse OTIO to timeline document Parses an `.otio` file or ZIP bundle into a Kaitoi Timeline Document v1 without persisting uploaded media. Returned media references are portable filenames and warnings explain that media was not stored. - Operation ID: `timelines.parseOtio` - Parameters: - `authorization` in `header` (`string` | `null`) - Request body: - `multipart/form-data`: `parseOtio` - Responses: - `200`: Timeline document converted from OTIO. - `400`: The OTIO upload is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `413`: The upload exceeds the request size limit. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `501`: OpenTimelineIO is unavailable. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "files": [ { "dataType": "video", "filename": "hero.mp4", "storagePath": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.mp4" } ], "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "warnings": [] } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`413`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`501`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` #### `GET /api/v1/timelines/schema/kaitoi-timeline-document.v1` Get timeline document schema Returns the canonical Kaitoi Timeline Document v1 JSON Schema. This route is unauthenticated so public API clients can validate documents before sending them. - Operation ID: `timelines.getSchema` - Responses: - `200`: Kaitoi Timeline Document v1 JSON Schema. #### `POST /api/v1/timelines/validate` Validate timeline document Validates a Kaitoi Timeline Document v1 against the same schema enforced by the backend. - Operation ID: `timelines.validate` - Parameters: - `authorization` in `header` (`string` | `null`) - Request body: - `application/json`: `PublicTimelineValidateRequest` - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 } } ``` - Responses: - `200`: Validated timeline document and derived summary. - `400`: The timeline document is invalid. - `401`: Missing, malformed, expired, or revoked bearer token. - `403`: The API key lacks the required scope or account capability. - `422`: Validation Error - `429`: The client or API key exceeded the REST API rate limit. - `503`: The shared REST API rate limiter is temporarily unavailable. - Example response (`200`): ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "valid": true } ``` - Example response (`400`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`401`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`403`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`429`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` - Example response (`503`): ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ## Schemas ### `HTTPValidationError` - Type: `object` - Fields: - `detail`: array of `ValidationError` ### `PublicAccountCredits` - Type: `object` - Fields: - `balanceCents`: `integer` required - `balanceDollars`: `number` required - `balanceMicrocredits`: `string` required - `ledgerUnitVersion`: `integer` required - `managedProviderReceivableMicrocredits`: `string` required - `paygBalanceCents`: `integer` required - `paygBalanceDollars`: `number` required - `paygBalanceMicrocredits`: `string` required - `subscriptionBalanceCents`: `integer` required - `subscriptionBalanceDollars`: `number` required - `subscriptionBalanceMicrocredits`: `string` required - `subscriptionMonthlyGrantCents`: `integer` required - `subscriptionMonthlyGrantMicrocredits`: `string` required - `subscriptionPeriodEnd`: `string` | `null` - `subscriptionPlanTier`: `string` | `null` - `subscriptionRolloverCapCents`: `integer` required - `subscriptionRolloverCapMicrocredits`: `string` required - `updatedAt`: `string` | `null` - Example: ```json { "balanceCents": 1250, "balanceDollars": 12.5, "balanceMicrocredits": "1250110000", "ledgerUnitVersion": 2, "managedProviderReceivableMicrocredits": "0", "paygBalanceCents": 750, "paygBalanceDollars": 7.5, "paygBalanceMicrocredits": "750110000", "subscriptionBalanceCents": 500, "subscriptionBalanceDollars": 5.0, "subscriptionBalanceMicrocredits": "500000000", "subscriptionMonthlyGrantCents": 1000, "subscriptionMonthlyGrantMicrocredits": "1000000000", "subscriptionPeriodEnd": "2026-06-30T00:00:00Z", "subscriptionPlanTier": "pro", "subscriptionRolloverCapCents": 2000, "subscriptionRolloverCapMicrocredits": "2000000000", "updatedAt": "2026-06-09T12:00:00Z" } ``` ### `PublicApiErrorBody` - Type: `object` - Fields: - `code`: `string` required - `details`: `object` - `message`: `string` required - `requestId`: `string` | `null` - Example: ```json { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found.", "requestId": "req_123" } ``` ### `PublicApiErrorResponse` - Type: `object` - Fields: - `error`: `PublicApiErrorBody` required - Example: ```json { "error": { "code": "PROJECT_NOT_FOUND", "details": { "projectId": "proj_123" }, "message": "Project not found." } } ``` ### `PublicConnection` - Type: `object` - Fields: - `sourceNodeId`: `string` required - `sourcePin`: `string` required - `targetNodeId`: `string` required - `targetPin`: `string` required - Example: ```json { "sourceNodeId": "node_123", "sourcePin": "text", "targetNodeId": "node_456", "targetPin": "prompt" } ``` ### `PublicEditableConnection` - Type: `object` - Fields: - `from`: `array` required - `to`: `array` required - Example: ```json { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ``` ### `PublicEditableGraph-Input` - Type: `object` - Fields: - `connections`: array of `PublicEditableConnection` - `nodes`: array of `PublicEditableNode` - Example: ```json { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } ``` ### `PublicEditableGraph-Output` - Type: `object` - Fields: - `connections`: array of `PublicEditableConnection` - `nodes`: array of `PublicEditableNode` - Example: ```json { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } ``` ### `PublicEditableInputValue` - Type: `object` - Fields: - `type`: `string` | `null` - `value`: object | `null` - Example: ```json { "type": "string", "value": "A cinematic wide shot" } ``` ### `PublicEditableNode` - Type: `object` - Fields: - `id`: `string` required - `inputs`: `object` - `position`: `PublicEditablePosition` | `null` - `title`: `string` | `null` - `type`: `string` required - Example: ```json { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ``` ### `PublicEditablePosition` - Type: `object` - Fields: - `x`: `number` required - `y`: `number` required - Example: ```json { "x": 120.0, "y": 240.0 } ``` ### `PublicFileDetail` - Type: `object` - Fields: - `contentType`: `string` required - `createdAt`: `string` required - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `fileId`: `string` required - `filename`: `string` required - `sizeBytes`: `integer` required - `updatedAt`: `string` required - Example: ```json { "contentType": "image/png", "createdAt": "2026-05-24T12:00:00+00:00", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "input.png", "sizeBytes": 12345, "updatedAt": "2026-05-24T12:00:00+00:00" } ``` ### `PublicFileDownloadUrl` - Type: `object` - Fields: - `downloadUrl`: `string` required - `expiresAt`: `string` required - `fileId`: `string` required - Example: ```json { "downloadUrl": "https://example.com/data/2f8f8c5d.png?token=...", "expiresAt": "2026-05-24T13:00:00+00:00", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png" } ``` ### `PublicFileOutput` - Type: `object` - Fields: - `contentType`: `string` required - `downloadUrl`: `string` | `null` - `expiresAt`: `string` | `null` - `fileId`: `string` required - `filename`: `string` required - `previewExpiresAt`: `string` | `null` - `previewUrl`: `string` | `null` - `sizeBytes`: `integer` required - `type`: `string` ### `PublicFileUploadCompleteRequest` - Type: `object` - Fields: - `actualSha256`: `string` | `null` - `actualSizeBytes`: `integer` | `null` - `multipartParts`: array of `PublicMultipartUploadPart` | `null` - Example: ```json { "actualSha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "actualSizeBytes": 12345 } ``` ### `PublicFileUploadCreateRequest` - Type: `object` - Fields: - `contentType`: `string` - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `filename`: `string` required - `sha256`: `string` | `null` - `sizeBytes`: `integer` required - Example: ```json { "contentType": "image/png", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "filename": "input.png", "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "sizeBytes": 12345 } ``` ### `PublicFileUploadInstruction` - Type: `object` - Fields: - `headers`: `object` - `method`: `string` required - `url`: `string` required ### `PublicFileUploadPartInstruction` - Type: `object` - Fields: - `headers`: `object` - `method`: `string` required - `partNumber`: `integer` required - `url`: `string` required ### `PublicFileUploadSession` - Type: `object` - Fields: - `expiresAt`: `string` required - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `fileId`: `string` required - `partCount`: `integer` | `null` - `partSizeBytes`: `integer` | `null` - `parts`: array of `PublicFileUploadPartInstruction` | `null` - `storageMethod`: `string` required - `upload`: `PublicFileUploadInstruction` | `null` - `uploadId`: `string` required - Example: ```json { "expiresAt": "2026-05-24T13:00:00+00:00", "externalId": "asset_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "storageMethod": "single_part", "upload": { "headers": { "Content-Type": "image/png" }, "method": "PUT", "url": "https://storage.example.com/signed-upload-url" }, "uploadId": "upl_8d7f7f7e1dd64d3e8e2d0d2e4b3c9a1f" } ``` ### `PublicGraph` - Type: `object` - Fields: - `connections`: array of `PublicConnection` required - `nodes`: array of `PublicGraphNode` required - `projectId`: `string` required - `version`: `integer` required - Example: ```json { "connections": [], "nodes": [ { "id": "node_123", "inputs": [], "outputs": [ { "exposed": false, "name": "outputPrompt", "type": "string" } ], "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ], "projectId": "proj_123", "version": 7 } ``` ### `PublicGraphNode` - Type: `object` - Fields: - `id`: `string` required - `inputs`: array of `PublicNodePin` required - `outputs`: array of `PublicNodePin` required - `position`: `PublicPosition` | `null` - `title`: `string` | `null` - `type`: `string` required - Example: ```json { "id": "node_123", "inputs": [ { "exposed": true, "label": "Prompt", "name": "inputPrompt", "type": "string", "value": "A cinematic wide shot" } ], "outputs": [ { "exposed": false, "name": "outputPrompt", "type": "string" } ], "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ``` ### `PublicGraphNodeProjection` - Type: `object` - Fields: - `inputs`: array of `PublicProjectedNodePin` | `null` - `outputs`: array of `PublicProjectedNodePin` | `null` ### `PublicInlineGraph` - Type: `object` - Fields: - `connections`: array of `PublicInlineGraphConnection` - `nodes`: array of `PublicInlineGraphNode` required ### `PublicInlineGraphConnection` - Type: `object` - Fields: - `from`: object required - `to`: object required ### `PublicInlineGraphNode` - Type: `object` - Fields: - `id`: `string` required - `inputs`: `object` - `position`: `PublicInlineGraphPosition` | `null` - `title`: `string` | `null` - `type`: `string` required ### `PublicInlineGraphPosition` - Type: `object` - Fields: - `x`: `number` | `integer` required - `y`: `number` | `integer` required ### `PublicListResponse_PublicNodeTypeSummary_` - Type: `object` - Fields: - `data`: array of `PublicNodeTypeSummary` required - `hasMore`: `boolean` required - `nextCursor`: `string` | `null` - Example: ```json { "data": [], "hasMore": false } ``` ### `PublicListResponse_PublicProjectSummary_` - Type: `object` - Fields: - `data`: array of `PublicProjectSummary` required - `hasMore`: `boolean` required - `nextCursor`: `string` | `null` - Example: ```json { "data": [], "hasMore": false } ``` ### `PublicListResponse_PublicRunEvent_` - Type: `object` - Fields: - `data`: array of `PublicRunEvent` required - `hasMore`: `boolean` required - `nextCursor`: `string` | `null` - Example: ```json { "data": [], "hasMore": false } ``` ### `PublicListResponse_PublicTemplateEndpointSummary_` - Type: `object` - Fields: - `data`: array of `PublicTemplateEndpointSummary` required - `hasMore`: `boolean` required - `nextCursor`: `string` | `null` - Example: ```json { "data": [], "hasMore": false } ``` ### `PublicListResponse_PublicTemplateSummary_` - Type: `object` - Fields: - `data`: array of `PublicTemplateSummary` required - `hasMore`: `boolean` required - `nextCursor`: `string` | `null` - Example: ```json { "data": [], "hasMore": false } ``` ### `PublicMultipartUploadPart` - Type: `object` - Fields: - `etag`: `string` required - `partNumber`: `integer` required ### `PublicNodePin` - Type: `object` - Fields: - `exposed`: `boolean` - `label`: `string` | `null` - `name`: `string` required - `type`: `string` | `null` - `value`: object | `null` - Example: ```json { "exposed": true, "label": "Prompt", "name": "prompt", "type": "string", "value": "A cinematic wide shot" } ``` ### `PublicNodeTypeDetail` - Type: `object` - Fields: - `category`: `string` | `null` - `description`: `string` | `null` - `icon`: `string` | `null` - `inputs`: array of `PublicNodeTypePin` - `outputs`: array of `PublicNodeTypePin` - `source`: `string` | `null` - `tags`: array of `string` - `thumbnailExpiresAt`: `string` | `null` - `thumbnailUrl`: `string` | `null` - `title`: `string` required - `type`: `string` required - Example: ```json { "category": "Image", "description": "Load an image file into the graph.", "icon": "image", "inputs": [ { "exposed": true, "label": "Image", "name": "path", "type": "file" } ], "outputs": [ { "exposed": false, "label": "Image", "name": "image", "type": "image" } ], "source": "builtin", "tags": [ "image", "loader" ], "title": "Load Image", "type": "builtin/image/load_image" } ``` ### `PublicNodeTypePin` - Type: `object` - Fields: - `default`: object | `null` - `exposed`: `boolean` - `label`: `string` | `null` - `name`: `string` required - `type`: `string` | `null` - Example: ```json { "default": "", "exposed": true, "label": "Prompt", "name": "prompt", "type": "string" } ``` ### `PublicNodeTypeSummary` - Type: `object` - Fields: - `category`: `string` | `null` - `description`: `string` | `null` - `icon`: `string` | `null` - `source`: `string` | `null` - `tags`: array of `string` - `thumbnailExpiresAt`: `string` | `null` - `thumbnailUrl`: `string` | `null` - `title`: `string` required - `type`: `string` required - Example: ```json { "category": "Image", "description": "Load an image file into the graph.", "icon": "image", "source": "builtin", "tags": [ "image", "loader" ], "title": "Load Image", "type": "builtin/image/load_image" } ``` ### `PublicPosition` - Type: `object` - Fields: - `x`: `number` required - `y`: `number` required - `z`: `number` | `null` - Example: ```json { "x": 120.0, "y": 240.0 } ``` ### `PublicPrimitiveOutput` - Type: `object` - Fields: - `type`: `string` required - `value`: object required ### `PublicProjectCreateRequest` - Type: `object` - Fields: - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `metadata`: `object` - `name`: `string` - Example: ```json { "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "metadata": { "plan": "pro" }, "name": "Jane's Workspace" } ``` ### `PublicProjectDetail` - Type: `object` - Fields: - `createdAt`: `string` | `null` - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `id`: `string` required - `name`: `string` required - `nodeCount`: `integer` required - `thumbnailExpiresAt`: `string` | `null` - `thumbnailUrl`: `string` | `null` - `updatedAt`: `string` | `null` - `version`: `integer` required - Example: ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "id": "proj_123", "name": "Machine", "nodeCount": 12, "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7 } ``` ### `PublicProjectDocument` - Type: `object` - Fields: - `createdAt`: `string` | `null` - `document`: `PublicProjectDocumentBody-Output` required - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `metadata`: `object` - `name`: `string` required - `projectId`: `string` required - `updatedAt`: `string` | `null` - `version`: `integer` required - `warnings`: array of `PublicProjectDocumentWarning` - Example: ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "document": { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } }, "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "metadata": { "plan": "pro" }, "name": "Machine", "projectId": "proj_123", "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7, "warnings": [] } ``` ### `PublicProjectDocumentBody-Input` - Type: `object` - Fields: - `graph`: `PublicEditableGraph-Input` required - Example: ```json { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } } ``` ### `PublicProjectDocumentBody-Output` - Type: `object` - Fields: - `graph`: `PublicEditableGraph-Output` required - Example: ```json { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } } ``` ### `PublicProjectDocumentPatchOperation` - Type: `object` - Fields: - `op`: `string` required - Example: ```json { "input": "prompt", "nodeId": "node_123", "op": "setNodeInput", "value": { "type": "string", "value": "A quieter variation" } } ``` ### `PublicProjectDocumentPatchRequest` - Type: `object` - Fields: - `expectedVersion`: `integer` required - `operations`: array of `PublicProjectDocumentPatchOperation` required - Example: ```json { "expectedVersion": 7, "operations": [ { "node": { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" }, "op": "addNode" }, { "from": [ "node_123", "outputPrompt" ], "op": "connect", "to": [ "node_456", "inputPrompt" ] } ] } ``` ### `PublicProjectDocumentPatchResponse` - Type: `object` - Fields: - `appliedOperations`: `integer` required - `createdAt`: `string` | `null` - `document`: `PublicProjectDocumentBody-Output` required - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `metadata`: `object` - `name`: `string` required - `projectId`: `string` required - `updatedAt`: `string` | `null` - `version`: `integer` required - `warnings`: array of `PublicProjectDocumentWarning` - Example: ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "document": { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } }, "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "metadata": { "plan": "pro" }, "name": "Machine", "projectId": "proj_123", "updatedAt": "2026-05-21T15:10:03.000000+00:00", "version": 7, "warnings": [] } ``` ### `PublicProjectDocumentReplaceRequest` - Type: `object` - Fields: - `document`: `PublicProjectDocumentBody-Input` required - `expectedVersion`: `integer` required - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `metadata`: `object` | `null` - `name`: `string` | `null` - Example: ```json { "document": { "graph": { "connections": [ { "from": [ "node_123", "text" ], "to": [ "node_456", "prompt" ] } ], "nodes": [ { "id": "node_123", "inputs": { "inputPrompt": { "type": "string", "value": "A cinematic wide shot" } }, "position": { "x": 120.0, "y": 240.0 }, "title": "Text Prompt", "type": "builtin/utils/text_prompt" } ] } }, "expectedVersion": 7, "metadata": { "plan": "pro" }, "name": "Machine" } ``` ### `PublicProjectDocumentWarning` - Type: `object` - Fields: - `code`: `string` required - `message`: `string` required - `path`: `string` required - Example: ```json { "code": "FILE_INPUT_VALUE_REDACTED", "message": "File-like input value was redacted because it is not a public REST file reference.", "path": "document.graph.nodes.node-a.inputs.inputImage.value" } ``` ### `PublicProjectSpendNode` - Type: `object` - Fields: - `billedRuns`: `integer` required - `credits`: `integer` required - `lastUsedAt`: `string` | `null` - `microcredits`: `string` required - `nodeId`: `string` required - `nodeTitle`: `string` required - `nodeType`: `string` | `null` ### `PublicProjectSpendRun` - Type: `object` - Fields: - `chargeType`: `string` required - `createdAt`: `string` | `null` - `credits`: `integer` required - `id`: `string` required - `kind`: `string` - `microcredits`: `string` required - `model`: `string` | `null` - `nodeExecutionId`: `string` | `null` - `nodeId`: `string` required - `nodeTitle`: `string` required - `nodeType`: `string` | `null` - `provider`: `string` | `null` - `reason`: `string` required ### `PublicProjectSpendSummary` - Type: `object` - Fields: - `byChargeType`: `object` required - `byChargeTypeMicrocredits`: `object` required - `byNode`: array of `PublicProjectSpendNode` required - `byProvider`: `object` required - `byProviderMicrocredits`: `object` required - `projectId`: `string` required - `recentRuns`: array of `PublicProjectSpendRun` required - `totalCredits`: `integer` required - `totalMicrocredits`: `string` required - Example: ```json { "byChargeType": { "provider": 24 }, "byChargeTypeMicrocredits": { "provider": "24110000" }, "byNode": [ { "billedRuns": 2, "credits": 24, "lastUsedAt": "2026-05-24T12:00:00Z", "microcredits": "24110000", "nodeId": "node_456", "nodeTitle": "Generate Image", "nodeType": "builtin/image/generate" } ], "byProvider": { "fal": 24 }, "byProviderMicrocredits": { "fal": "24110000" }, "projectId": "project_123", "recentRuns": [], "totalCredits": 42, "totalMicrocredits": "42110000" } ``` ### `PublicProjectStoryboard` - Type: `object` - Fields: - `document`: `object` required - `projectId`: `string` required - `summary`: `PublicStoryboardSummary` required - `version`: `integer` required - Example: ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "projectId": "proj_123", "summary": { "mediaCount": 0, "sceneCount": 1, "shotCount": 1 }, "version": 7 } ``` ### `PublicProjectStoryboardApplyResponse` - Type: `object` - Fields: - `document`: `object` required - `mode`: `string` required - `projectId`: `string` required - `summary`: `PublicStoryboardApplySummary` required - `version`: `integer` required - Example: ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "mode": "replace", "projectId": "proj_123", "summary": { "documentMedia": 0, "extraPanelMedia": 0, "mediaCount": 12, "mediaImported": 12, "mediaSkipped": 0, "sceneCount": 1, "shotCount": 12, "unresolvedMedia": 0, "urlMedia": 0, "warnings": [] }, "version": 8 } ``` ### `PublicProjectStoryboardReplaceRequest` - Type: `object` - Fields: - `document`: `object` required - `expectedVersion`: `integer` required - `mode`: `string` - Example: ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "expectedVersion": 7, "mode": "replace" } ``` ### `PublicProjectSummary` - Type: `object` - Fields: - `createdAt`: `string` | `null` - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `id`: `string` required - `name`: `string` required - `nodeCount`: `integer` required - `thumbnailExpiresAt`: `string` | `null` - `thumbnailUrl`: `string` | `null` - `updatedAt`: `string` | `null` - Example: ```json { "createdAt": "2026-05-21T14:21:52.152049+00:00", "externalId": "workspace_123", "externalUserId": "user_789", "externalWorkspaceId": "team_456", "id": "proj_123", "name": "Machine", "nodeCount": 12, "updatedAt": "2026-05-21T15:10:03.000000+00:00" } ``` ### `PublicProjectTimeline` - Type: `object` - Fields: - `document`: `object` required - `projectId`: `string` required - `summary`: `PublicTimelineSummary` required - `version`: `integer` required - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "projectId": "proj_123", "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "version": 7 } ``` ### `PublicProjectTimelineApplyResponse` - Type: `object` - Fields: - `document`: `object` required - `mode`: `string` required - `projectId`: `string` required - `summary`: `PublicTimelineSummary` required - `version`: `integer` required - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "mode": "replace", "projectId": "proj_123", "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "version": 8 } ``` ### `PublicProjectTimelineReplaceRequest` - Type: `object` - Fields: - `document`: `object` required - `expectedVersion`: `integer` required - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "expectedVersion": 7 } ``` ### `PublicProjectUpdateRequest` - Type: `object` - Fields: - `expectedVersion`: `integer` required - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `metadata`: `object` | `null` - `name`: `string` | `null` - Example: ```json { "expectedVersion": 1, "externalUserId": "user_789", "metadata": { "plan": "enterprise" }, "name": "Jane's Renamed Workspace" } ``` ### `PublicProjectedNodePin` - Type: `object` - Fields: - `name`: `string` required - `type`: `string` | `null` ### `PublicRunApplyOutputsRequest` - Type: `object` - Fields: - `expectedVersion`: `integer` required - `outputKeys`: array of `string` | `null` ### `PublicRunApplyOutputsResponse` - Type: `object` - Fields: - `appliedOutputs`: `integer` required - `outputKeys`: array of `string` required - `projectId`: `string` required - `runId`: `string` required - `version`: `integer` required ### `PublicRunBooleanInput` - Type: `object` - Fields: - `type`: `string` required - `value`: `boolean` required ### `PublicRunCreateRequest` - Type: `object` - Fields: - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `graph`: `PublicInlineGraph` | `null` - `inputOverrides`: `object` - `persistInputOverrides`: `boolean` - `projectId`: `string` | `null` - `targetNodeIds`: array of `string` - Examples: _Saved project run_ ```json { "externalUserId": "user_789", "inputOverrides": { "node_456": { "inputImage": { "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "type": "file" }, "prompt": { "type": "string", "value": "Describe this image" } } }, "projectId": "project_123", "targetNodeIds": [ "node_456" ] } ``` _Inline graph run_ ```json { "externalUserId": "user_789", "graph": { "connections": [ { "from": [ "prompt", "outputPrompt" ], "to": [ "echo", "inputText" ] } ], "nodes": [ { "id": "prompt", "inputs": { "inputPrompt": { "type": "string", "value": "Write a short caption" } }, "position": { "x": 0, "y": 0 }, "title": "Prompt", "type": "builtin/utils/text_prompt" }, { "id": "echo", "inputs": {}, "position": { "x": 360, "y": 0 }, "type": "builtin/utils/text" } ] }, "targetNodeIds": [ "echo" ] } ``` ### `PublicRunDetail` - Type: `object` - Fields: - `cancelRequested`: `boolean` - `createdAt`: `string` required - `creditsUsed`: `integer` | `null` - `creditsUsedMicrocredits`: `string` | `null` - `error`: `object` | `null` - `executionTimeMs`: `integer` | `null` - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `id`: `string` required - `inputFiles`: array of `PublicRunFileInputReference` - `outputs`: `object` - `projectId`: `string` | `null` - `projectVersion`: `integer` | `null` - `sourceType`: `string` - `spend`: `PublicRunSpendSummary` | `null` - `status`: `string` required - `targetNodeIds`: array of `string` - `updatedAt`: `string` required - Example: ```json { "createdAt": "2026-01-01T00:00:00Z", "creditsUsed": 12, "executionTimeMs": 2143, "id": "run_abc123", "outputs": { "outputImage": { "contentType": "image/png", "downloadUrl": "https://files.example/d/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "filename": "random_image.png", "previewUrl": "https://files.example/p/2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png?sig=...", "sizeBytes": 482931, "type": "file" } }, "sourceType": "inline_graph", "spend": { "byChargeType": { "provider": 12 }, "byNode": [ { "billedRuns": 1, "credits": 12, "nodeId": "image", "nodeTitle": "Generate Image", "nodeType": "builtin/image/generate" } ], "byProvider": { "fal": 12 }, "totalCredits": 12 }, "status": "succeeded", "targetNodeIds": [ "image" ], "updatedAt": "2026-01-01T00:00:03Z" } ``` ### `PublicRunEvent` - Type: `object` - Fields: - `createdAt`: `string` required - `data`: `object` - `id`: `string` required - `message`: `string` | `null` - `nodeId`: `string` | `null` - `progress`: `number` | `null` - `runId`: `string` required - `type`: `string` required ### `PublicRunFileInput` - Type: `object` - Fields: - `fileId`: `string` required - `type`: `string` required ### `PublicRunFileInputReference` - Type: `object` - Fields: - `contentType`: `string` required - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `fileId`: `string` required - `filename`: `string` required - `inputName`: `string` required - `nodeId`: `string` required - `sizeBytes`: `integer` required ### `PublicRunJsonInput` - Type: `object` - Fields: - `type`: `string` required - `value`: object required ### `PublicRunNullInput` - Type: `object` - Fields: - `type`: `string` required - `value`: `null` ### `PublicRunNumberInput` - Type: `object` - Fields: - `type`: `string` required - `value`: `number` | `integer` required ### `PublicRunSpendNode` - Type: `object` - Fields: - `billedRuns`: `integer` required - `credits`: `integer` required - `microcredits`: `string` | `null` - `nodeId`: `string` required - `nodeTitle`: `string` required - `nodeType`: `string` | `null` ### `PublicRunSpendSummary` - Type: `object` - Fields: - `byChargeType`: `object` - `byChargeTypeMicrocredits`: `object` - `byNode`: array of `PublicRunSpendNode` - `byProvider`: `object` - `byProviderMicrocredits`: `object` - `totalCredits`: `integer` required - `totalMicrocredits`: `string` | `null` ### `PublicRunStringInput` - Type: `object` - Fields: - `type`: `string` required - `value`: `string` required ### `PublicStoryboardApplySummary` - Type: `object` - Fields: - `documentMedia`: `integer` required - `extraPanelMedia`: `integer` required - `mediaCount`: `integer` required - `mediaImported`: `integer` required - `mediaSkipped`: `integer` required - `sceneCount`: `integer` required - `shotCount`: `integer` required - `unresolvedMedia`: `integer` required - `urlMedia`: `integer` required - `warnings`: array of `PublicStoryboardWarning` - Example: ```json { "documentMedia": 0, "extraPanelMedia": 0, "mediaCount": 12, "mediaImported": 12, "mediaSkipped": 0, "sceneCount": 1, "shotCount": 12, "unresolvedMedia": 0, "urlMedia": 0, "warnings": [] } ``` ### `PublicStoryboardSummary` - Type: `object` - Fields: - `mediaCount`: `integer` required - `sceneCount`: `integer` required - `shotCount`: `integer` required - Example: ```json { "mediaCount": 12, "sceneCount": 1, "shotCount": 12 } ``` ### `PublicStoryboardToTimelineRequest` - Type: `object` - Fields: - `defaultDuration`: `number` | `null` - `document`: `object` required - `frameRate`: `integer` - `name`: `string` | `null` - `projectResolution`: `object` | `null` - Example: ```json { "defaultDuration": 3, "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "frameRate": 30, "name": "Storyboard Timeline" } ``` ### `PublicStoryboardToTimelineResponse` - Type: `object` - Fields: - `document`: `object` required - `summary`: `PublicTimelineSummary` required - `warnings`: array of `PublicStoryboardWarning` - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "warnings": [] } ``` ### `PublicStoryboardValidateRequest` - Type: `object` - Fields: - `document`: `object` required - Example: ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 } } ``` ### `PublicStoryboardValidationResponse` - Type: `object` - Fields: - `document`: `object` required - `summary`: `PublicStoryboardSummary` required - `valid`: `boolean` required - Example: ```json { "document": { "kaitoiType": "storyboard", "name": "Storyboard", "sequences": [ { "id": "scene-1", "name": "Scene 1", "order": 0, "shots": [ { "description": "A quiet establishing shot.", "durationSeconds": 3, "id": "frame-1", "order": 0, "transition": "cut" } ] } ], "settings": { "aspectRatio": "16:9", "defaultFrameDuration": 3 }, "version": 1 }, "summary": { "mediaCount": 0, "sceneCount": 1, "shotCount": 1 }, "valid": true } ``` ### `PublicStoryboardWarning` - Type: `object` - Fields: - `code`: `string` required - `message`: `string` required - `path`: `string` required - Example: ```json { "code": "URL_MEDIA_SKIPPED", "message": "URL media references are not fetched or imported by the public API.", "path": "document.sequences.0.shots.0.panels.0.media" } ``` ### `PublicTemplateDetail` - Type: `object` - Fields: - `category`: `string` | `null` - `createdAt`: `string` | `null` - `description`: `string` | `null` - `endpoints`: array of `PublicTemplateEndpointSummary` - `hasData`: `boolean` - `hasEndpoint`: `boolean` required - `name`: `string` required - `nodeCount`: `integer` - `source`: `string` - `tags`: array of `string` - `templateId`: `string` required - `updatedAt`: `string` | `null` - Example: ```json { "category": "image", "createdAt": "2026-06-01T12:00:00+00:00", "description": "Creates a stylized portrait from a prompt.", "endpoints": [], "hasData": false, "hasEndpoint": true, "name": "Portrait Generator", "nodeCount": 4, "source": "user", "tags": [ "portrait" ], "templateId": "template_123", "updatedAt": "2026-06-02T12:00:00+00:00" } ``` ### `PublicTemplateEndpointAuthDocs` - Type: `object` - Fields: - `apiKeyReturnedOnce`: `boolean` - `header`: `string` - `scheme`: `string` - `valueFormat`: `string` ### `PublicTemplateEndpointDocs` - Type: `object` - Fields: - `auth`: `PublicTemplateEndpointAuthDocs` - `description`: `string` | `null` - `endpointId`: `string` required - `externalRun`: `PublicTemplateEndpointRunDocs` required - `inputs`: array of `PublicTemplateEndpointPin` required - `name`: `string` required - `outputs`: array of `PublicTemplateEndpointPin` required - `ownerRun`: `PublicTemplateEndpointOwnerRunDocs` - `requestExample`: `object` required - `templateId`: `string` required - Example: ```json { "auth": { "apiKeyReturnedOnce": true, "header": "Authorization", "scheme": "bearer", "valueFormat": "Bearer " }, "description": "Creates a stylized portrait from a prompt.", "endpointId": "endpoint_123", "externalRun": { "contentTypes": [ "application/json", "multipart/form-data" ], "info": "/v1/run/endpoint_123/info", "method": "POST", "status": "/v1/run/endpoint_123/{task_id}", "statusMethod": "GET", "submit": "/v1/run/endpoint_123" }, "inputs": [ { "dataType": "string", "fieldName": "prompt", "kind": "primitive", "name": "Prompt" } ], "name": "Generate Portrait", "outputs": [ { "dataType": "image", "fieldName": "image", "kind": "file", "name": "Image" } ], "ownerRun": { "auth": "publicApiBearerToken", "available": true, "contentTypes": [ "application/json" ], "method": "POST", "note": "Owner-authenticated execution uses the public API key owner and may spend credits.", "status": "/api/v1/templates/template_123/endpoints/endpoint_123/runs/{taskId}", "statusMethod": "GET", "stream": "/api/v1/templates/template_123/endpoints/endpoint_123/runs/{taskId}/events/stream", "streamMethod": "GET", "submit": "/api/v1/templates/template_123/endpoints/endpoint_123/runs" }, "requestExample": { "inputs": { "prompt": "A cinematic portrait" } }, "templateId": "template_123" } ``` ### `PublicTemplateEndpointOwnerRunDocs` - Type: `object` - Fields: - `auth`: `string` - `available`: `boolean` - `contentTypes`: array of `string` - `method`: `string` - `note`: `string` - `status`: `string` | `null` - `statusMethod`: `string` - `stream`: `string` | `null` - `streamMethod`: `string` - `submit`: `string` | `null` ### `PublicTemplateEndpointPin` - Type: `object` - Fields: - `dataType`: `string` | `null` - `fieldName`: `string` required - `kind`: `string` - `name`: `string` required - `nodeId`: `string` | `null` - `pinName`: `string` | `null` - Example: ```json { "dataType": "string", "fieldName": "prompt", "kind": "primitive", "name": "Prompt", "nodeId": "node_prompt", "pinName": "text" } ``` ### `PublicTemplateEndpointRunDocs` - Type: `object` - Fields: - `contentTypes`: array of `string` - `info`: `string` required - `method`: `string` - `status`: `string` required - `statusMethod`: `string` - `submit`: `string` required ### `PublicTemplateEndpointSummary` - Type: `object` - Fields: - `apiKeyPrefix`: `string` | `null` - `callCount`: `integer` - `createdAt`: `string` | `null` - `description`: `string` | `null` - `endpointId`: `string` required - `inputCount`: `integer` required - `isActive`: `boolean` - `lastCalled`: `string` | `null` - `name`: `string` required - `outputCount`: `integer` required - `templateId`: `string` required - `updatedAt`: `string` | `null` - Example: ```json { "apiKeyPrefix": "wf_sk_abcd", "callCount": 0, "createdAt": "2026-06-01T12:00:00+00:00", "description": "Creates a stylized portrait from a prompt.", "endpointId": "endpoint_123", "inputCount": 1, "isActive": true, "name": "Generate Portrait", "outputCount": 1, "templateId": "template_123", "updatedAt": "2026-06-01T12:00:00+00:00" } ``` ### `PublicTemplateRunCreateRequest` - Type: `object` - Fields: - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `inputs`: `object` - Example: ```json { "externalUserId": "user_789", "inputs": { "image": { "fileId": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.png", "type": "file" }, "prompt": { "type": "string", "value": "A cinematic robot portrait" } } } ``` ### `PublicTemplateRunDetail` - Type: `object` - Fields: - `createdAt`: `string` | `null` - `currentNode`: `string` | `null` - `endpointId`: `string` required - `error`: `object` | `null` - `executionTimeMs`: `integer` | `null` - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `inputFiles`: array of `PublicRunFileInputReference` - `logs`: array of `object` - `nodeIndex`: `integer` | `null` - `nodesCount`: `integer` | `null` - `outputs`: `object` - `progress`: `integer` | `null` - `progressMessage`: `string` | `null` - `sourceType`: `string` - `status`: `string` required - `taskId`: `string` required - `templateId`: `string` required - `updatedAt`: `string` | `null` ### `PublicTemplateSummary` - Type: `object` - Fields: - `category`: `string` | `null` - `createdAt`: `string` | `null` - `description`: `string` | `null` - `endpoints`: array of `PublicTemplateEndpointSummary` - `hasData`: `boolean` - `hasEndpoint`: `boolean` required - `name`: `string` required - `nodeCount`: `integer` - `source`: `string` - `tags`: array of `string` - `templateId`: `string` required - `updatedAt`: `string` | `null` - Example: ```json { "category": "image", "createdAt": "2026-06-01T12:00:00+00:00", "description": "Creates a stylized portrait from a prompt.", "endpoints": [], "hasData": false, "hasEndpoint": true, "name": "Portrait Generator", "nodeCount": 4, "source": "user", "tags": [ "portrait" ], "templateId": "template_123", "updatedAt": "2026-06-02T12:00:00+00:00" } ``` ### `PublicTimelineOtioExportRequest` - Type: `object` - Fields: - `document`: `object` required - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 } } ``` ### `PublicTimelineOtioImportResponse` - Type: `object` - Fields: - `document`: `object` required - `files`: array of `PublicTimelineOtioImportedFile` - `summary`: `PublicTimelineSummary` required - `warnings`: array of `PublicTimelineWarning` - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "files": [ { "dataType": "video", "filename": "hero.mp4", "storagePath": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.mp4" } ], "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "warnings": [] } ``` ### `PublicTimelineOtioImportedFile` - Type: `object` - Fields: - `dataType`: `string` required - `filename`: `string` required - `storagePath`: `string` required - Example: ```json { "dataType": "video", "filename": "hero.mp4", "storagePath": "2f8f8c5df80e4f76a5b7d1bbd6fae0ab.mp4" } ``` ### `PublicTimelineSummary` - Type: `object` - Fields: - `clipCount`: `integer` required - `durationSeconds`: `number` required - `trackCount`: `integer` required - Example: ```json { "clipCount": 12, "durationSeconds": 60, "trackCount": 1 } ``` ### `PublicTimelineValidateRequest` - Type: `object` - Fields: - `document`: `object` required - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 } } ``` ### `PublicTimelineValidationResponse` - Type: `object` - Fields: - `document`: `object` required - `summary`: `PublicTimelineSummary` required - `valid`: `boolean` required - Example: ```json { "document": { "kaitoiType": "timeline", "name": "Timeline", "timeline": { "clips": [ { "duration": 3, "filename": "hero.png", "id": "clip-1", "name": "Hero", "opacity": 100, "startTime": 0, "storageType": "data", "trackId": "track-1", "transform": { "rotation": 0, "scale": 100, "x": 0, "y": 0 }, "trimEnd": 0, "trimStart": 0, "type": "image", "volume": 100 } ], "currentTime": 0, "frameRate": 30, "playheadPosition": 0, "projectDuration": 60, "projectResolution": { "height": 1080, "isAutoDetected": true, "width": 1920 }, "selectedClipIds": [], "selectedTrackId": "track-1", "tracks": [ { "height": 60, "id": "track-1", "locked": false, "mutedAudio": false, "mutedVideo": false, "name": "Track 1", "volume": 1 } ], "zoom": 100 }, "version": 1 }, "summary": { "clipCount": 1, "durationSeconds": 60, "trackCount": 1 }, "valid": true } ``` ### `PublicTimelineWarning` - Type: `object` - Fields: - `code`: `string` required - `message`: `string` required - `path`: `string` | `null` - Example: ```json { "code": "MEDIA_FILE_MISSING", "message": "A referenced media file was not included in the OTIO upload.", "path": "media.hero.mp4" } ``` ### `ValidationError` - Type: `object` - Fields: - `ctx`: `object` - `input`: object - `loc`: array of `string` | `integer` required - `msg`: `string` required - `type`: `string` required ### `importOtio` - Type: `object` - Fields: - `file`: `string` required ### `parseOtio` - Type: `object` - Fields: - `file`: `string` required ### `upload` - Type: `object` - Fields: - `externalId`: `string` | `null` - `externalUserId`: `string` | `null` - `externalWorkspaceId`: `string` | `null` - `file`: `string` required