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

# Update Project

> Update an existing project using its unique project ID. Requires the complete updated project object in the request body.

## Overview

Update an existing project by sending the complete updated project object. This endpoint allows you to modify scene visuals, subtitle text content, scene settings, and other project attributes. The entire project object must be included in the request; partial updates are not supported.

<Warning>
  **Complete Object Required**: You must send the entire project object in the request body, even if you are only changing a few fields. Omitting fields may result in data loss or project corruption.
</Warning>

<Note>
  You need a valid API key to use this endpoint. Get your API key from the [API Access page](https://app.pictory.ai/api-access) in your Pictory dashboard.
</Note>

***

## API Endpoint

```http theme={null}
PUT https://api.pictory.ai/pictoryapis/v2/projects/{projectid}
```

***

## Request Parameters

### Path Parameters

<ParamField path="projectid" type="string" required>
  The unique identifier of the project to update. Can be either a string (for v3 schema projects) or an integer (for v2 schema and earlier projects).

  Example: `20251222191648030d7df02f5b4054d4ca8831f1369459e25`
</ParamField>

### Headers

<ParamField header="Authorization" type="string" required>
  API key for authentication (starts with `pictai_`)

  ```
  Authorization: YOUR_API_KEY
  ```
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be set to `application/json`

  ```
  Content-Type: application/json
  ```
</ParamField>

### Request Body

The request body must contain the complete project object retrieved from the [Get Project by ID](/api-reference/projects/get-project-by-id) endpoint. Modify only the fields you need to change while preserving all other fields.

<Warning>
  **Do not modify project fields if their usage is unclear.** Incorrect modifications can cause unexpected behavior or corrupt your project. If you have questions about specific fields or their intended use, please contact our support team at [support@pictory.ai](mailto:support@pictory.ai) before making changes.
</Warning>

***

## Updatable Video Elements

The following sections describe the video element fields that can be safely modified. These structures match the response from the Get Project by ID endpoint.

### Project-Level Fields

These top-level fields can be safely updated:

```json theme={null}
{
  "projectName": "My Updated Video",
  "audioSpeed": 100,
  "videoVolume": 50
}
```

| Field         | Type   | Description                                    |
| ------------- | ------ | ---------------------------------------------- |
| `projectName` | string | Name of the project                            |
| `audioSpeed`  | number | Audio playback speed percentage (100 = normal) |
| `videoVolume` | number | Background video volume level (0-100)          |

### Scene Text (`scenes[].text` and `scenes[].sentence`)

Update the display text and narration for each scene:

```json theme={null}
{
  "scenes": [
    {
      "text": "Your new scene text here",
      "sentence": [
        {
          "text": "Your new ",
          "decoration": ["decor-bold"]
        },
        {
          "text": "scene text",
          "highlight": true,
          "decoration": ["decor-underline"]
        },
        {
          "text": " here"
        }
      ],
      "keywords": ["scene text"]
    }
  ]
}
```

| Field                   | Type    | Description                                       |
| ----------------------- | ------- | ------------------------------------------------- |
| `text`                  | string  | Plain text version of the scene narration         |
| `sentence`              | array   | Array of text segments with formatting            |
| `sentence[].text`       | string  | Text content of the segment                       |
| `sentence[].highlight`  | boolean | Apply keyword highlighting                        |
| `sentence[].decoration` | array   | Text decorations: `decor-bold`, `decor-underline` |
| `sentence[].case`       | string  | Text case: `none`, `uppercase`, `lowercase`       |
| `keywords`              | array   | Array of highlighted keyword strings              |

### Scene Text Styling (`scenes[].styleData`)

Modify text appearance for each scene:

```json theme={null}
{
  "scenes": [
    {
      "styleData": {
        "fontName": "Arial",
        "fontDisplayName": "Arial",
        "fontSize": "42",
        "fontColor": "rgb(255,255,255)",
        "fontWeight": 400,
        "textAlign": "left",
        "keywordColor": "rgba(141, 208, 229, 1)",
        "textBackgroundColor": "rgba(0,0,0,0.35)",
        "textShadowColor": "rgba(0,0,0,0)",
        "textShadowWidthFr": 0.03,
        "maxLines": 2,
        "width": 0.7,
        "preset": "bottom-left"
      }
    }
  ]
}
```

| Field                 | Type   | Description                                                     |
| --------------------- | ------ | --------------------------------------------------------------- |
| `fontName`            | string | Font family name                                                |
| `fontSize`            | string | Font size in pixels                                             |
| `fontColor`           | string | Text color (RGB format)                                         |
| `fontWeight`          | number | Font weight (400=normal, 700=bold)                              |
| `textAlign`           | string | Alignment: `left`, `center`, `right`                            |
| `keywordColor`        | string | Highlight color (RGBA format)                                   |
| `textBackgroundColor` | string | Background color (RGBA format)                                  |
| `maxLines`            | number | Maximum display lines                                           |
| `width`               | number | Container width (0-1)                                           |
| `preset`              | string | Position: `bottom-left`, `bottom-center`, `center-center`, etc. |

### Scene Settings (`scenes[].settings`)

Control scene playback behavior:

```json theme={null}
{
  "scenes": [
    {
      "settings": {
        "hideText": false
      }
    }
  ]
}
```

| Field      | Type    | Description                               |
| ---------- | ------- | ----------------------------------------- |
| `hideText` | boolean | Hide subtitle/caption text for this scene |

### Background Image (`scenes[].background` with `type: "image"`)

Update the background image for a scene:

```json theme={null}
{
  "scenes": [
    {
      "background": {
        "type": "image",
        "elementData": {
          "url": "https://your-image-url.com/image.jpg",
          "preview_jpg": "https://your-image-url.com/preview.jpg",
          "thumb": "https://your-image-url.com/thumb.jpg",
          "thumb_jpg": "https://your-image-url.com/thumb.jpg",
          "library": "uploads",
          "libraryItemId": "your-item-id",
          "duration": 0
        },
        "styleData": {
          "kenBurns": "kb-364683-oqt0",
          "imageZoomPan": true,
          "width": 0.7,
          "aspectRatio": 1.78,
          "preset": "center-center",
          "colorOverlay": {
            "hide": false,
            "bgColor": "rgb(0,37,60)",
            "opacity": 0.5
          }
        }
      }
    }
  ]
}
```

| Field                     | Type    | Description                                            |
| ------------------------- | ------- | ------------------------------------------------------ |
| `type`                    | string  | Must be `"image"`                                      |
| `elementData.url`         | string  | Full resolution image URL                              |
| `elementData.preview_jpg` | string  | Preview image URL                                      |
| `elementData.thumb`       | string  | Thumbnail URL                                          |
| `elementData.library`     | string  | Source: `uploads`, `unsplash`, `story_blocks`, `getty` |
| `styleData.imageZoomPan`  | boolean | Enable Ken Burns zoom/pan effect                       |
| `styleData.colorOverlay`  | object  | Color overlay settings                                 |

### Background Video (`scenes[].background` with `type: "video"`)

Update the background video for a scene:

```json theme={null}
{
  "scenes": [
    {
      "background": {
        "type": "video",
        "elementData": {
          "url": "https://your-video-url.com/video.mp4",
          "preview_jpg": "https://your-video-url.com/preview.jpg",
          "thumb": "https://your-video-url.com/thumb.mp4",
          "thumb_jpg": "https://your-video-url.com/thumb.jpg",
          "library": "uploads",
          "libraryItemId": "your-item-id",
          "duration": 10,
          "description": "Video description"
        },
        "styleData": {
          "imageZoomPan": true
        },
        "settings": {
          "loopVideo": true
        }
      }
    }
  ]
}
```

| Field                  | Type    | Description                                          |
| ---------------------- | ------- | ---------------------------------------------------- |
| `type`                 | string  | Must be `"video"`                                    |
| `elementData.url`      | string  | Video file URL                                       |
| `elementData.duration` | number  | Video duration in seconds                            |
| `elementData.library`  | string  | Source: `uploads`, `story_blocks`, `getty`, `pexels` |
| `settings.loopVideo`   | boolean | Loop video playback                                  |

### Solid Color Background (`scenes[].background` with `type: "solid"`)

Set a solid color background:

```json theme={null}
{
  "scenes": [
    {
      "background": {
        "type": "solid",
        "elementData": {
          "color": "rgb(69,123,113)"
        },
        "styleData": {}
      }
    }
  ]
}
```

### Video Element Overlay (`scenes[].elements[]` with `type: "video"`)

Update video clip overlays within a scene:

```json theme={null}
{
  "scenes": [
    {
      "elements": [
        {
          "elementId": "existing-element-id",
          "type": "video",
          "isLogo": false,
          "hide": false,
          "settings": {
            "loopVideo": true,
            "muteClipAudio": true
          },
          "elementData": {
            "url": "https://your-video-url.com/clip.mp4",
            "preview_jpg": "https://your-video-url.com/preview.jpg",
            "thumb": "https://your-video-url.com/thumb.mp4",
            "thumb_jpg": "https://your-video-url.com/thumb.jpg",
            "library": "uploads",
            "libraryItemId": "your-item-id",
            "duration": 16
          },
          "styleData": {
            "top": 4.89,
            "left": 3.96,
            "width": 0.92,
            "aspectRatio": 1.78,
            "preset": null,
            "colorOverlay": {
              "hide": false,
              "bgColor": "rgb(110,111,132)",
              "opacity": 0.3
            }
          }
        }
      ]
    }
  ]
}
```

| Field                    | Type    | Description                                   |
| ------------------------ | ------- | --------------------------------------------- |
| `elementId`              | string  | Unique element identifier (preserve existing) |
| `type`                   | string  | Must be `"video"`                             |
| `hide`                   | boolean | Hide this element                             |
| `settings.loopVideo`     | boolean | Loop the video clip                           |
| `settings.muteClipAudio` | boolean | Mute the video's audio                        |
| `elementData.url`        | string  | Video file URL                                |
| `styleData.top`          | number  | Vertical position (%)                         |
| `styleData.left`         | number  | Horizontal position (%)                       |
| `styleData.width`        | number  | Element width (0-1)                           |
| `styleData.colorOverlay` | object  | Color overlay settings                        |

### Text Element Overlay (`scenes[].elements[]` with `type: "text"`)

Update text overlay elements within a scene:

```json theme={null}
{
  "scenes": [
    {
      "elements": [
        {
          "elementId": "existing-element-id",
          "type": "text",
          "componentName": "main_title",
          "settings": {
            "textMode": "writeAnything"
          },
          "elementData": {
            "sentence": [
              {
                "text": "Your Custom Text",
                "decoration": ["decor-bold", "decor-underline"]
              }
            ]
          },
          "styleData": {
            "fontName": "Arial",
            "fontSize": "42",
            "fontColor": "rgb(255,255,255)",
            "textAlign": "center",
            "preset": "center-center",
            "width": 0.7,
            "animation": {
              "textAnimation": [
                {
                  "name": "Typewriter",
                  "type": ["entry"],
                  "writingStyle": "character",
                  "direction": "up",
                  "speed": { "value": 1 }
                },
                {
                  "name": "None",
                  "type": ["exit"]
                }
              ],
              "textBgAnimation": []
            }
          }
        }
      ]
    }
  ]
}
```

| Field                  | Type   | Description                                   |
| ---------------------- | ------ | --------------------------------------------- |
| `elementId`            | string | Unique element identifier (preserve existing) |
| `type`                 | string | Must be `"text"`                              |
| `settings.textMode`    | string | `useStoryText` or `writeAnything`             |
| `elementData.sentence` | array  | Text segments with formatting                 |
| `styleData.fontName`   | string | Font family                                   |
| `styleData.fontSize`   | string | Font size in pixels                           |
| `styleData.fontColor`  | string | Text color (RGB)                              |
| `styleData.animation`  | object | Entry/exit animation settings                 |

***

## Quick Reference: Safe Updates

<AccordionGroup>
  <Accordion title="Scene Background Image" icon="image">
    **Field**: `scenes[].background` (type: image)

    Replace background image with Ken Burns effect and color overlay.

    ```json theme={null}
    {
      "background": {
        "type": "image",
        "elementData": {
          "url": "https://images.unsplash.com/photo-example",
          "preview_jpg": "https://images.unsplash.com/photo-example",
          "thumb": "https://images.unsplash.com/photo-example?w=200",
          "library": "unsplash",
          "libraryItemId": "mR1CIDduGLc",
          "duration": 0
        },
        "styleData": {
          "kenBurns": "kb-364683-oqt0",
          "imageZoomPan": true,
          "aspectRatio": 1.78,
          "colorOverlay": {
            "hide": false,
            "bgColor": "rgb(0,37,60)",
            "opacity": 0.5
          }
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Scene Background Video" icon="video">
    **Field**: `scenes[].background` (type: video)

    Replace background with a video clip.

    ```json theme={null}
    {
      "background": {
        "type": "video",
        "elementData": {
          "url": "https://video-url.com/video.mp4",
          "preview_jpg": "https://video-url.com/preview.jpg",
          "thumb": "https://video-url.com/thumb.mp4",
          "library": "story_blocks",
          "libraryItemId": "120818",
          "duration": 10
        },
        "styleData": {
          "imageZoomPan": true
        },
        "settings": {
          "loopVideo": true
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Scene Text & Keywords" icon="text">
    **Fields**: `scenes[].text`, `scenes[].sentence`, `scenes[].keywords`

    Update display text, formatting, and highlighted keywords.

    ```json theme={null}
    {
      "text": "Sweeping views with year-round sunsets",
      "sentence": [
        {
          "text": "Sweeping views",
          "highlight": true,
          "decoration": ["decor-bold", "decor-underline"]
        },
        {
          "text": " with year-round "
        },
        {
          "text": "sunsets",
          "highlight": true
        }
      ],
      "keywords": ["Sweeping views", "sunsets"]
    }
    ```
  </Accordion>

  <Accordion title="Scene Text Styling" icon="palette">
    **Field**: `scenes[].styleData`

    Update text appearance, fonts, colors, and positioning.

    ```json theme={null}
    {
      "styleData": {
        "fontName": "Arial",
        "fontDisplayName": "Arial",
        "fontSize": "42",
        "fontColor": "rgb(255,255,255)",
        "fontWeight": 400,
        "textAlign": "left",
        "keywordColor": "rgba(141, 208, 229, 1)",
        "textBackgroundColor": "rgba(0,0,0,0.35)",
        "textShadowColor": "rgba(0,0,0,0)",
        "maxLines": 2,
        "width": 0.7,
        "preset": "bottom-left"
      }
    }
    ```
  </Accordion>

  <Accordion title="Scene Settings" icon="sliders">
    **Field**: `scenes[].settings`

    Control text visibility for the scene.

    ```json theme={null}
    {
      "settings": {
        "hideText": false
      }
    }
    ```
  </Accordion>

  <Accordion title="Video Element Overlay" icon="film">
    **Field**: `scenes[].elements[]` (type: video)

    Update video clips placed as overlays within scenes.

    ```json theme={null}
    {
      "elements": [{
        "type": "video",
        "isLogo": false,
        "hide": false,
        "elementData": {
          "url": "https://video-url.com/clip.mp4",
          "preview_jpg": "https://video-url.com/preview.jpg",
          "library": "story_blocks",
          "duration": 16
        },
        "settings": {
          "loopVideo": true,
          "muteClipAudio": true
        },
        "styleData": {
          "top": 4.89,
          "left": 3.96,
          "width": 0.92,
          "aspectRatio": 1.78,
          "colorOverlay": {
            "hide": false,
            "bgColor": "rgb(110,111,132)",
            "opacity": 0.3
          }
        }
      }]
    }
    ```
  </Accordion>

  <Accordion title="Text Element Overlay" icon="font">
    **Field**: `scenes[].elements[]` (type: text)

    Update text overlays and their styling/animations.

    ```json theme={null}
    {
      "elements": [{
        "type": "text",
        "componentName": "main_title",
        "settings": {
          "textMode": "writeAnything"
        },
        "elementData": {
          "sentence": [{
            "text": "A Stunning Masterpiece",
            "decoration": ["decor-underline", "decor-bold"]
          }]
        },
        "styleData": {
          "fontName": "Arial",
          "fontSize": "42",
          "fontColor": "rgb(255,255,255)",
          "textAlign": "left",
          "preset": "bottom-left",
          "animation": {
            "textAnimation": [{
              "name": "Typewriter",
              "type": ["entry"],
              "writingStyle": "character",
              "speed": { "value": 1 }
            }]
          }
        }
      }]
    }
    ```
  </Accordion>

  <Accordion title="Project Metadata" icon="file-lines">
    **Fields**: `projectName`, `audioSpeed`, `videoVolume`

    Update basic project configuration.

    ```json theme={null}
    {
      "projectName": "AI Video Creation Guide",
      "audioSpeed": 100,
      "videoVolume": 50
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Fields to Avoid Modifying

<Warning>
  **Do Not Modify These Fields** unless you fully understand their structure and dependencies:

  * `sceneId`, `elementId`, `projectId` - Unique identifiers
  * `partitionKey`, `sortKey` - Database keys
  * `audio_id`, `voiceOverId`, `audioId` - Audio reference IDs
  * `assetResponseId` - Asset tracking identifiers
  * `schemaVersion` - Project schema version
  * `approximateCreationDateTime` - System timestamps
  * `createdBy`, `modifiedBy`, `cognito_id` - User identifiers
  * `audios` - Audio configuration (complex dependencies)
  * `word_markers`, `time_markers`, `subScenes` - Audio timing data

  Modifying these fields without proper understanding can cause project corruption or rendering failures. Contact [support@pictory.ai](mailto:support@pictory.ai) if you need guidance.
</Warning>

***

## Response

The API returns a JSON response indicating the success or failure of the update operation.

**Success Response Fields:**

| Field     | Type    | Description                                        |
| --------- | ------- | -------------------------------------------------- |
| `success` | boolean | `true` if the update was successful                |
| `message` | string  | Confirmation message                               |
| `project` | object  | The updated project object with all current values |

**Error Response Fields:**

| Field     | Type    | Description                               |
| --------- | ------- | ----------------------------------------- |
| `success` | boolean | `false` if the update failed              |
| `message` | string  | Error description                         |
| `error`   | string  | Detailed error information (if available) |

## Response Examples

<ResponseExample>
  ```json 200 - Success theme={null}
  {
    "success": true,
    "message": "Project updated successfully",
    "project": {
      "projectName": "Updated Project Name",
      "audioSpeed": 100,
      "videoVolume": 50,
      "scenes": [
        {
          "text": "Updated scene text",
          "sentence": [
            {
              "text": "Updated scene text",
              "highlight": true
            }
          ],
          "settings": {
            "hideText": false
          },
          "background": {
            "type": "image",
            "elementData": {
              "url": "https://new-media-url.com/image.jpg"
            }
          }
        }
      ]
    }
  }
  ```

  ```json 400 - Bad Request theme={null}
  {
    "success": false,
    "message": "Invalid project data",
    "error": "Missing required fields"
  }
  ```

  ```json 401 - Unauthorized theme={null}
  {
    "message": "Unauthorized"
  }
  ```

  ```json 404 - Not Found theme={null}
  {
    "success": false,
    "message": "Project not found"
  }
  ```
</ResponseExample>

***

## Code Examples

<Tip>
  Replace `YOUR_API_KEY` with your actual API key that starts with `pictai_`
</Tip>

<CodeGroup>
  ```bash cURL theme={null}
  # First, get the current project
  PROJECT_DATA=$(curl --request GET \
    --url 'https://api.pictory.ai/pictoryapis/v2/projects/YOUR_PROJECT_ID' \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'accept: application/json')

  # Modify the project data (using jq)
  UPDATED_DATA=$(echo $PROJECT_DATA | jq '.projectName = "Updated Name"')

  # Send the update
  curl --request PUT \
    --url 'https://api.pictory.ai/pictoryapis/v2/projects/YOUR_PROJECT_ID' \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data "$UPDATED_DATA"
  ```

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

  project_id = "20251222191648030d7df02f5b4054d4ca8831f1369459e25"
  api_key = "YOUR_API_KEY"
  headers = {
      "Authorization": api_key,
      "Content-Type": "application/json"
  }

  # Step 1: Get the current project
  get_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
  response = requests.get(get_url, headers=headers)
  project = response.json()

  # Step 2: Modify the project
  project['projectName'] = "Updated Project Name"
  project['audioSpeed'] = 110

  # Step 3: Send the update
  put_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
  update_response = requests.put(put_url, json=project, headers=headers)
  result = update_response.json()

  if result.get('success'):
      print("Project updated successfully")
  else:
      print(f"Update failed: {result.get('message')}")
  ```

  ```javascript JavaScript theme={null}
  const projectId = '20251222191648030d7df02f5b4054d4ca8831f1369459e25';
  const apiKey = 'YOUR_API_KEY';
  const headers = {
    'Authorization': `${apiKey}`,
    'Content-Type': 'application/json'
  };

  // Step 1: Get the current project
  const getResponse = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}`,
    { headers: { ...headers, 'accept': 'application/json' } }
  );
  const project = await getResponse.json();

  // Step 2: Modify the project
  project.projectName = 'Updated Project Name';
  project.audioSpeed = 110;

  // Step 3: Send the update
  const updateResponse = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}`,
    {
      method: 'PUT',
      headers,
      body: JSON.stringify(project)
    }
  );

  const result = await updateResponse.json();

  if (result.success) {
    console.log('Project updated successfully');
  } else {
    console.log(`Update failed: ${result.message}`);
  }
  ```
</CodeGroup>

***

## Usage Notes

<Warning>
  **Always Retrieve Before Update**: Always fetch the current project state using the GET endpoint before making modifications. This ensures you have the complete object with all required fields.
</Warning>

<Note>
  **Complete Object Required**: Even if you only want to change one field, you must send the entire project object. Omitting fields will cause them to be removed from the project.
</Note>

<Note>
  **Render After Update**: Updating a project only saves the changes to the project data. To generate a new video with your updates, you must call the [Render Project](/api-reference/videos/render-project) endpoint after the update is complete.
</Note>

<Tip>
  **Test Changes First**: When making significant updates, consider testing on a copy of the project first to ensure your modifications do not cause rendering issues.
</Tip>

***

## Common Use Cases

### 1. Update Scene Background Visual

Replace the background media for a specific scene:

```python theme={null}
import requests

def update_scene_background(project_id, scene_index, new_media_url, media_type, api_key):
    """
    Update the background visual for a specific scene

    media_type: "image" or "video"
    """
    headers = {"Authorization": api_key, "Content-Type": "application/json"}

    # Get current project
    get_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    response = requests.get(get_url, headers=headers)
    project = response.json()

    # Update the scene background
    if scene_index < len(project.get('scenes', [])):
        background = project['scenes'][scene_index].get('background', {})
        background['type'] = media_type
        background['elementData'] = background.get('elementData', {})
        background['elementData']['url'] = new_media_url
        background['elementData']['preview_jpg'] = new_media_url
        background['elementData']['library'] = 'uploads'

        if media_type == 'video':
            background['settings'] = {'loopVideo': True}

        project['scenes'][scene_index]['background'] = background
        print(f"Updated scene {scene_index} background to: {new_media_url}")

        # Send update
        put_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
        update_response = requests.put(put_url, json=project, headers=headers)
        result = update_response.json()

        if result.get('success'):
            print("Project updated successfully")
            return result
        else:
            print(f"Update failed: {result.get('message')}")
            return None
    else:
        print(f"Scene index {scene_index} out of range")
        return None

# Example usage
update_scene_background(
    "YOUR_PROJECT_ID",
    0,  # First scene
    "https://your-media-url.com/new-image.jpg",
    "image",
    "YOUR_API_KEY"
)
```

### 2. Update Scene Text

Modify the text and sentence for one or more scenes:

```javascript theme={null}
async function updateSceneText(projectId, sceneUpdates, apiKey) {
  const headers = {
    'Authorization': `${apiKey}`,
    'Content-Type': 'application/json'
  };

  // Get current project
  const getResponse = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}`,
    { headers }
  );
  const project = await getResponse.json();

  // Update scene text
  sceneUpdates.forEach(({ sceneIndex, newText, keywords }) => {
    if (sceneIndex < project.scenes.length) {
      // Update the text field
      project.scenes[sceneIndex].text = newText;

      // Update sentence array with formatting
      project.scenes[sceneIndex].sentence = [
        {
          text: newText,
          highlight: false,
          decoration: ['decor-bold']
        }
      ];

      // Update keywords if provided
      if (keywords) {
        project.scenes[sceneIndex].keywords = keywords;
      }

      console.log(`Updated scene ${sceneIndex} text`);
    }
  });

  // Send update
  const updateResponse = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}`,
    {
      method: 'PUT',
      headers,
      body: JSON.stringify(project)
    }
  );

  const result = await updateResponse.json();
  return result;
}

// Example usage
const updates = [
  { sceneIndex: 0, newText: 'A Stunning Masterpiece', keywords: ['Stunning Masterpiece'] },
  { sceneIndex: 1, newText: 'Sweeping views with year-round sunsets', keywords: ['Sweeping views', 'sunsets'] }
];

const result = await updateSceneText('YOUR_PROJECT_ID', updates, 'YOUR_API_KEY');
console.log('Update result:', result);
```

### 3. Update Scene Settings and Element Settings

Apply settings changes to scenes and their elements:

```python theme={null}
def update_scene_settings(project_id, settings_updates, api_key):
    """
    Apply settings to multiple scenes

    settings_updates: dict with scene indices as keys and settings as values
    Note:
      - scenes[].settings only supports: hideText
      - scenes[].elements[].settings supports: loopVideo, muteClipAudio
      - scenes[].background.settings supports: loopVideo (for video backgrounds)
      - scenes[].background.styleData supports: imageZoomPan

    Example: {
        0: {"hideText": True},
        2: {"hideText": False}
    }
    """
    headers = {"Authorization": api_key, "Content-Type": "application/json"}

    # Get current project
    get_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    response = requests.get(get_url, headers=headers)
    project = response.json()

    # Update scene settings (only hideText is supported at scene level)
    for scene_index, new_settings in settings_updates.items():
        if scene_index < len(project.get('scenes', [])):
            current_settings = project['scenes'][scene_index].get('settings', {})
            # Only update hideText at scene level
            if 'hideText' in new_settings:
                current_settings['hideText'] = new_settings['hideText']
            project['scenes'][scene_index]['settings'] = current_settings
            print(f"Updated settings for scene {scene_index}")

    # Send update
    put_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    update_response = requests.put(put_url, json=project, headers=headers)

    return update_response.json()


def update_element_settings(project_id, scene_index, element_index, element_settings, api_key):
    """
    Update settings for a specific video element overlay

    element_settings supports: loopVideo, muteClipAudio
    """
    headers = {"Authorization": api_key, "Content-Type": "application/json"}

    # Get current project
    get_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    response = requests.get(get_url, headers=headers)
    project = response.json()

    scenes = project.get('scenes', [])
    if scene_index < len(scenes):
        elements = scenes[scene_index].get('elements', [])
        if element_index < len(elements):
            current_settings = elements[element_index].get('settings', {})
            current_settings.update(element_settings)
            project['scenes'][scene_index]['elements'][element_index]['settings'] = current_settings
            print(f"Updated element {element_index} settings in scene {scene_index}")

    # Send update
    put_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    update_response = requests.put(put_url, json=project, headers=headers)

    return update_response.json()


# Example usage - Update scene settings (hideText only)
scene_settings = {
    0: {"hideText": True},
    2: {"hideText": False}
}
result = update_scene_settings("YOUR_PROJECT_ID", scene_settings, "YOUR_API_KEY")
print(f"Scene settings update successful: {result.get('success')}")

# Example usage - Update video element settings (loopVideo, muteClipAudio)
result = update_element_settings(
    "YOUR_PROJECT_ID",
    scene_index=0,
    element_index=0,
    element_settings={"loopVideo": True, "muteClipAudio": True},
    api_key="YOUR_API_KEY"
)
print(f"Element settings update successful: {result.get('success')}")
```

### 4. Update Project Metadata

Change project name and playback settings:

```javascript theme={null}
async function updateProjectMetadata(projectId, metadata, apiKey) {
  const headers = {
    'Authorization': `${apiKey}`,
    'Content-Type': 'application/json'
  };

  // Get current project
  const getResponse = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}`,
    { headers }
  );
  const project = await getResponse.json();

  // Update metadata
  if (metadata.name) project.projectName = metadata.name;
  if (metadata.audioSpeed) project.audioSpeed = metadata.audioSpeed;
  if (metadata.videoVolume !== undefined) project.videoVolume = metadata.videoVolume;

  console.log('Updated project metadata:', metadata);

  // Send update
  const updateResponse = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/projects/${projectId}`,
    {
      method: 'PUT',
      headers,
      body: JSON.stringify(project)
    }
  );

  return await updateResponse.json();
}

// Example usage
const metadata = {
  name: 'My Updated Project',
  audioSpeed: 105,
  videoVolume: 80
};

const result = await updateProjectMetadata('YOUR_PROJECT_ID', metadata, 'YOUR_API_KEY');
console.log('Result:', result);
```

### 5. Replace All Scene Backgrounds

Replace backgrounds for all scenes at once:

```python theme={null}
def replace_all_backgrounds(project_id, media_items, api_key):
    """
    Replace background media for all scenes

    media_items: list of dicts with 'url' and 'type' keys
    Example: [
        {"url": "https://example.com/image1.jpg", "type": "image"},
        {"url": "https://example.com/video1.mp4", "type": "video"},
    ]
    """
    headers = {"Authorization": api_key, "Content-Type": "application/json"}

    # Get current project
    get_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    response = requests.get(get_url, headers=headers)
    project = response.json()

    scenes = project.get('scenes', [])

    if len(media_items) != len(scenes):
        print(f"Warning: {len(media_items)} items provided for {len(scenes)} scenes")
        return None

    # Update all scene backgrounds
    for i, media in enumerate(media_items):
        background = scenes[i].get('background', {})
        background['type'] = media['type']
        background['elementData'] = background.get('elementData', {})
        background['elementData']['url'] = media['url']
        background['elementData']['preview_jpg'] = media['url']
        background['elementData']['library'] = 'uploads'

        if media['type'] == 'video':
            background['settings'] = {'loopVideo': True}
            background['elementData']['duration'] = media.get('duration', 10)

        scenes[i]['background'] = background
        print(f"Scene {i}: {media['url']} ({media['type']})")

    project['scenes'] = scenes

    # Send update
    put_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    update_response = requests.put(put_url, json=project, headers=headers)

    return update_response.json()

# Example usage
new_backgrounds = [
    {"url": "https://media-url.com/scene1.jpg", "type": "image"},
    {"url": "https://media-url.com/scene2.mp4", "type": "video", "duration": 15},
    {"url": "https://media-url.com/scene3.jpg", "type": "image"}
]

result = replace_all_backgrounds("YOUR_PROJECT_ID", new_backgrounds, "YOUR_API_KEY")
if result and result.get('success'):
    print("All backgrounds updated successfully")
```

### 6. Safe Update with Validation

Implement validation before updating:

```python theme={null}
import copy

def safe_update_project(project_id, update_function, api_key):
    """
    Safely update project with validation

    update_function: function that takes project dict and modifies it
    """
    headers = {"Authorization": api_key, "Content-Type": "application/json"}

    # Get current project
    get_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    response = requests.get(get_url, headers=headers)

    if response.status_code != 200:
        print("Failed to fetch project")
        return None

    original_project = response.json()
    updated_project = copy.deepcopy(original_project)

    # Apply updates
    try:
        update_function(updated_project)
    except Exception as e:
        print(f"Error applying updates: {e}")
        return None

    # Validate critical fields are present
    required_fields = ['projectName', 'scenes', 'schemaVersion']
    for field in required_fields:
        if field not in updated_project:
            print(f"Critical field '{field}' missing after update")
            return None

    # Send update
    put_url = f"https://api.pictory.ai/pictoryapis/v2/projects/{project_id}"
    update_response = requests.put(put_url, json=updated_project, headers=headers)

    result = update_response.json()

    if result.get('success'):
        print("Project updated successfully")
    else:
        print(f"Update failed: {result.get('message')}")

    return result

# Example usage
def my_updates(project):
    """Define your updates here"""
    project['projectName'] = 'New Name'
    project['audioSpeed'] = 95

    # Hide text for first scene (scenes[].settings.hideText)
    if project['scenes']:
        if 'settings' not in project['scenes'][0]:
            project['scenes'][0]['settings'] = {}
        project['scenes'][0]['settings']['hideText'] = True

    # Mute audio for first video element (elements[].settings.muteClipAudio)
    if project['scenes'] and project['scenes'][0].get('elements'):
        element = project['scenes'][0]['elements'][0]
        if element.get('type') == 'video':
            if 'settings' not in element:
                element['settings'] = {}
            element['settings']['muteClipAudio'] = True

result = safe_update_project("YOUR_PROJECT_ID", my_updates, "YOUR_API_KEY")
```

***

## Best Practices

### Safety Guidelines

1. **Always Fetch First**: Get the current project state before making any modifications
2. **Preserve All Fields**: Include all fields in the update request, even if unchanged
3. **Test on Copies**: Test significant changes on duplicate projects first
4. **Validate Before Sending**: Ensure all required fields are present before updating
5. **Keep Backups**: Save the original project state before applying updates

### Common Pitfalls to Avoid

* **❌ Partial Updates**: Sending only modified fields will delete other fields
* **❌ Missing Required Fields**: Omitting critical fields causes project corruption
* **❌ Invalid Values**: Using incorrect data types or formats for fields
* **❌ Modifying System Fields**: Changing internal IDs or metadata
* **❌ No Validation**: Updating without checking the project state first

### Performance Tips

* Batch multiple changes in a single update request
* Minimize the frequency of updates during active editing
* Cache the project object locally during editing sessions
* Only fetch/update when necessary to reduce API calls
