> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.videogen.io/file-uploads/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.videogen.io/_mcp/server. # File uploads > Create a presigned upload URL, PUT file bytes, and poll until processing completes. Use uploaded files as inputs for generate-image, generate-video-clip, and other transformation tools. Some generation tools accept an existing file as input (for example, animating an image into a video clip or transforming a video with a text prompt). To use your own files, upload them through the API first. Upload flow: `POST /v1/files/upload` with `{ displayName }` returns `{ fileId, uploadUrl }`. PUT raw bytes to `uploadUrl`. Poll `GET /v1/files/{fileId}` until a source has `status: "ready"`. The `uploadFile` SDK helper wraps all three steps. The `type` field is optional; when omitted, it is inferred after upload processing completes. For an audio or video upload you may also pass an optional `transcript` (`{ languageCode?, words: [{ startSeconds, endSeconds, word }] }`) to skip re-transcription and pin caption timing to your own transcript; words must be sorted and non-overlapping. `GET /v1/files/{fileId}` returns the same timed-`words` `transcript` object plus a plain `transcriptText` string. ## How it works ``` POST /v1/files/upload → { "fileId": "vg_file_...", "uploadUrl": "https://..." } PUT → (raw file bytes) GET /v1/files/{id} → poll until sources are ready ``` 1. Create a pending file and get a presigned upload URL 2. PUT the raw file bytes to the presigned URL 3. Poll until the file is processed and ready to use The presigned URL goes directly to the storage provider, so no additional authentication is needed for the PUT request. It expires shortly after creation, so upload promptly. ## Upload with the SDK The `uploadFile` helper handles all three steps in a single call: #### TypeScript ```typescript import { VideoGen, uploadFile } from "@videogen/sdk"; import { readFileSync } from "node:fs"; const client = new VideoGen({ apiKey: "sk_videogen_live_..." }); const file = await uploadFile({ client, data: readFileSync("./photo.png"), type: "IMAGE", displayName: "My photo", }); console.log(file.fileId); // "vg_file_..." ``` #### cURL ```bash # 1. Create the upload curl -X POST https://api.videogen.io/v1/files/upload \ -H "Authorization: Bearer sk_videogen_live_..." \ -H "Content-Type: application/json" \ -d '{"displayName": "My photo"}' # Response: { "fileId": "vg_file_...", "uploadUrl": "https://..." } # 2. PUT the file bytes to the presigned URL curl -X PUT "" \ --data-binary @./photo.png # 3. Poll until ready curl https://api.videogen.io/v1/files/vg_file_... \ -H "Authorization: Bearer sk_videogen_live_..." ``` ## Request body | Field | Type | Required | Description | | ------------- | ------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `displayName` | `string` | Yes | A display name for the file. | | `type` | `"IMAGE"` \| `"VIDEO"` \| `"AUDIO"` \| `"LOTTIE"` | No | The type of file you're uploading. When omitted, the type is inferred after upload processing completes. Set `"LOTTIE"` to upload a Lottie animation (Bodymovin JSON, e.g. exported from After Effects): JSON is not inferred as Lottie automatically, so declare it explicitly. | | `isTemporary` | `boolean` | No | When `true`, the file is temporary. Temporary files are guaranteed to be available for 24 hours, after which they may be archived at any time. Temporary files are not analyzed (no description, transcript, or embedding will be generated), so they will not appear in search results. Defaults to `false`. | | `transcript` | `object` | No | A pre-computed transcript for an audio or video upload, as `{ languageCode?, words: [{ startSeconds, endSeconds, word }] }`. When provided, the file is not re-transcribed, so caption timing matches your transcript exactly. Words must be sorted by `startSeconds` and non-overlapping. Ignored for non-audio/video files. | ## Using uploaded files as tool inputs Once the file is ready, pass its `fileId` to any tool that accepts a file input: #### TypeScript ```typescript import { VideoGen, uploadFile, pollExecutedTool } from "@videogen/sdk"; import { readFileSync } from "node:fs"; const client = new VideoGen({ apiKey: "sk_videogen_live_..." }); const uploaded = await uploadFile({ client, data: readFileSync("./photo.png"), type: "IMAGE", displayName: "Source image", }); const { toolExecutionId } = await client.tools.generateVideoClip({ fileId: uploaded.fileId, }); const result = await pollExecutedTool({ client, toolExecutionId }); console.log(result); ``` #### cURL ```bash curl -X POST https://api.videogen.io/v1/tools/generate-video-clip \ -H "Authorization: Bearer sk_videogen_live_..." \ -H "Content-Type: application/json" \ -d '{"fileId": "vg_file_..."}' ``` ## SDK options The `uploadFile` helper accepts these options: | Option | Type | Default | Description | | ---------------- | ------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `type` | `"IMAGE"` \| `"VIDEO"` \| `"AUDIO"` \| `"LOTTIE"` | – | File type. Optional; inferred when omitted. Lottie animations (Bodymovin JSON) must set `"LOTTIE"` explicitly. | | `displayName` | `string` | – | Display name (required). | | `temporary` | `boolean` | `false` | Only guaranteed to be available for 24 hours; may be archived after that. Temporary files are not analyzed (no description, transcript, or embedding). | | `pollIntervalMs` | `number` | `2000` | How often to check if processing is complete. | | `timeoutMs` | `number` | `3600000` | Maximum time to wait before throwing. | | `signal` | `AbortSignal` | – | Cancel the upload. | ## Using webhooks instead of polling Instead of polling for file readiness, you can subscribe to file upload lifecycle webhooks. These are only fired for API uploads. | Event | Meaning | | ------------------------- | --------------------------------------------------------------------------------------------------------------- | | `file.upload.completed` | File bytes received and stored. | | `file.playback_ready` | HLS streaming is available (private `hlsSource` and public HLS if enabled). | | `file.download_ready` | Static rendition download URL is ready. | | `file.analysis_completed` | Description, transcript, and embedding are ready. The file is now searchable. Never fired for temporary files. | | `file.analysis_failed` | Analysis failed. The file is still usable but may lack description/transcript. Never fired for temporary files. | Register for these events when creating a webhook endpoint: ```bash curl -X POST https://api.videogen.io/v1/webhooks/endpoints \ -H "Authorization: Bearer sk_videogen_live_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhooks/videogen", "events": ["file.upload.completed", "file.download_ready", "file.analysis_completed"] }' ``` Each event payload includes a hydrated `file` object with the latest file state, so no extra API call is needed. See [Webhooks guide](/handling-async-tasks/webhooks) for details on verifying webhook signatures. > HTTP API with TypeScript and Python SDKs for generating image, video, and audio assets with VideoGen.