> ## Documentation Index
> Fetch the complete documentation index at: https://developer.pixelbyte.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload File

> Generate a presigned URL to upload a temporary file. Files are automatically deleted after 60 minutes.

## Authentication

This endpoint requires an API key.

```
Authorization: Bearer YOUR_API_KEY
```

## Request Body

<ParamField body="filename" type="string" required>
  Name of the file to upload. Example: `"my-image.png"`
</ParamField>

<ParamField body="contentType" type="string" required>
  MIME type of the file. Allowed types: `image/jpeg`, `image/png`, `image/webp`, `image/gif`, `video/mp4`, `video/webm`, `audio/mpeg`, `audio/wav`.
</ParamField>

## Response

<ResponseField name="uploadUrl" type="string">
  Presigned PUT URL. Upload your file here with a PUT request within 15 minutes.
</ResponseField>

<ResponseField name="fileUrl" type="string">
  CDN URL where the file will be accessible after upload. Use this URL in job inputs.
</ResponseField>

<ResponseField name="fileId" type="string">
  Unique identifier for the upload. Use to check status.
</ResponseField>

<ResponseField name="expiresAt" type="string">
  ISO 8601 timestamp when the file will be automatically deleted (60 minutes from creation).
</ResponseField>

<ResponseField name="maxSize" type="number">
  Maximum allowed file size in bytes. Images: 10MB, Videos: 100MB, Audio: 50MB.
</ResponseField>

## How It Works

1. Call this endpoint to get a presigned upload URL
2. Upload your file via PUT to the `uploadUrl` (include `Content-Type` header)
3. Use the `fileUrl` in your job submission input
4. File is automatically deleted after 60 minutes

## Error Codes

| Code                   | Description                              |
| ---------------------- | ---------------------------------------- |
| `VALIDATION_ERROR`     | Missing or invalid filename/contentType. |
| `INVALID_CONTENT_TYPE` | Content type not in the allowed list.    |

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  # Step 1: Get presigned URL
  curl -X POST https://api.muvi.video/v1/uploads/presign \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "filename": "my-image.png",
      "contentType": "image/png"
    }'

  # Step 2: Upload file to the presigned URL
  curl -X PUT "UPLOAD_URL_FROM_RESPONSE" \
    -H "Content-Type: image/png" \
    --data-binary "@my-image.png"

  # Step 3: Use fileUrl in job submission
  curl -X POST https://api.muvi.video/v1/jobs/submit \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "google/nano-banana-pro/edit-image",
      "input": {
        "image": "FILE_URL_FROM_RESPONSE",
        "prompt": "Add a sunset background"
      }
    }'
  ```

  ```python Python theme={null}
  import requests

  # Step 1: Get presigned URL
  response = requests.post(
      "https://api.muvi.video/v1/uploads/presign",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={
          "filename": "my-image.png",
          "contentType": "image/png"
      }
  )

  data = response.json()["data"]
  print(f"File URL: {data['fileUrl']}")
  print(f"Expires at: {data['expiresAt']}")

  # Step 2: Upload file
  with open("my-image.png", "rb") as f:
      requests.put(
          data["uploadUrl"],
          headers={"Content-Type": "image/png"},
          data=f
      )

  # Step 3: Use in job submission
  job = requests.post(
      "https://api.muvi.video/v1/jobs/submit",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={
          "model": "google/nano-banana-pro/edit-image",
          "input": {
              "image": data["fileUrl"],
              "prompt": "Add a sunset background"
          }
      }
  )
  ```

  ```javascript JavaScript theme={null}
  // Step 1: Get presigned URL
  const response = await fetch("https://api.muvi.video/v1/uploads/presign", {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      filename: "my-image.png",
      contentType: "image/png"
    })
  });

  const { data } = await response.json();
  console.log(`File URL: ${data.fileUrl}`);

  // Step 2: Upload file
  const file = await fs.readFile("my-image.png");
  await fetch(data.uploadUrl, {
    method: "PUT",
    headers: { "Content-Type": "image/png" },
    body: file
  });

  // Step 3: Use in job submission
  const job = await fetch("https://api.muvi.video/v1/jobs/submit", {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "google/nano-banana-pro/edit-image",
      input: {
        image: data.fileUrl,
        prompt: "Add a sunset background"
      }
    })
  });
  ```
</CodeGroup>

### Example Response

```json theme={null}
{
  "success": true,
  "data": {
    "uploadUrl": "https://storage.muvi.video/temp/...",
    "fileUrl": "https://assetsv1.cdn.muvi.video/temp/user_abc123/uuid_my-image.png",
    "fileId": "3f44e3c6-acd9-4cc3-a91b-e63ec517b383",
    "expiresAt": "2026-02-25T11:42:21.949Z",
    "maxSize": 10485760
  },
  "requestId": "req_abc123"
}
```
