> ## 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 Storyboard Elements

> Update existing render elements in a storyboard preview before rendering the final video

## Overview

The Update Storyboard Elements API allows you to modify existing render elements generated from the [Create Storyboard Preview API](/api-reference/videos/create-storyboard-preview). Use this endpoint to update background visuals, scene text, or background music URLs before rendering the final video using the [Render from Preview API](/api-reference/videos/render-from-preview).

<Note>
  This endpoint only updates **existing** elements in the storyboard. You cannot add new elements, only modify elements that were generated during preview creation. Each element is identified by its unique `id`.
</Note>

<Warning>
  The storyboard preview job must be in a `completed` status before you can update its elements. Use the [Get Storyboard Preview Job by ID](/api-reference/jobs/get-storyboard-preview-job-by-id) API to verify the job status.
</Warning>

***

## When to Use This API

| Scenario                       | Description                                    |
| ------------------------------ | ---------------------------------------------- |
| **Replace background visuals** | Change a scene's background image or video URL |
| **Update scene text**          | Modify the text content displayed on a scene   |
| **Change background music**    | Replace the background music track URL         |
| **Adjust element timing**      | Modify `startTime` or `duration` of elements   |

***

## Workflow

```mermaid theme={null}
sequenceDiagram
    participant App as Your App
    participant API as Pictory API

    App->>API: Create Storyboard Preview
    API-->>App: jobId
    App->>API: Poll Job Status (GET /jobs/{jobId})
    API-->>App: completed + renderParams with elements
    App->>App: Identify elements to update by id
    App->>API: Update Storyboard Elements (PUT /storyboard/{jobId}/elements)
    API-->>App: 204 No Content
    App->>API: Render from Preview
    API-->>App: Final rendered video
```

***

## API Endpoint

```http theme={null}
PUT https://api.pictory.ai/pictoryapis/v2/video/storyboard/{jobid}/elements
```

***

## Path Parameters

<ParamField path="jobid" type="string" required>
  The job ID returned from the [Create Storyboard Preview API](/api-reference/videos/create-storyboard-preview). The job must be in `completed` status.
</ParamField>

***

## Request Headers

<ParamField header="Authorization" type="string" required>
  API key for authentication

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

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

***

## Request Body

The request body is a JSON **array** of element objects. Each object must include the required fields (`id`, `elementType`, `type`, `startTime`, `duration`) along with any properties you want to update. Only elements with matching `id` values in the existing storyboard will be updated.

<ParamField body="id" type="string" required>
  The unique identifier of the element to update. Must match an existing element `id` from the storyboard preview `renderParams.elements` array.

  Examples: `backgroundElement_20260306230202544742ecd40c041449...`, `bgMusic`, `voiceOver`, `SceneText_202603062302035443dd4953bf12...`
</ParamField>

<ParamField body="elementType" type="string" required>
  The type category of the element. Must match the existing element's `elementType`.

  **Values:**

  * `backgroundElement`: Scene background image or video
  * `audioElement`: Background music or voice-over audio
  * `SceneText`: Scene text overlay (displayed on scenes)
  * `layerItem`: Layer items such as title text, avatars, or overlays
</ParamField>

<ParamField body="type" type="string" required>
  The media type of the element. Must match or be compatible with the element.

  **Values:**

  * `video`: Video media
  * `image`: Image media
  * `audio`: Audio media
  * `text`: Text content
</ParamField>

<ParamField body="startTime" type="number" required>
  The start time of the element in seconds. Must be >= 0.
</ParamField>

<ParamField body="duration" type="number" required>
  The duration of the element in seconds. Must be > 0.
</ParamField>

***

## Element Attributes Reference

Any additional properties included in the element update object will be merged with the existing element. Below is a comprehensive reference of all attributes for each element type.

<Note>
  Element IDs are auto-generated unique strings (e.g., `backgroundElement_20260306230202544742ecd40c041449dac21bb737c799201`). Always retrieve the actual IDs from the preview job response rather than constructing them manually.
</Note>

### Element Types Summary

| Element          | `elementType`       | `type`           | `id` Pattern                   | Description                            |
| ---------------- | ------------------- | ---------------- | ------------------------------ | -------------------------------------- |
| Scene Background | `backgroundElement` | `video`, `image` | `backgroundElement_{uniqueId}` | Background visual for each scene       |
| Scene Text       | `SceneText`         | `text`           | `SceneText_{uniqueId}`         | Sentence-level text overlay on a scene |
| Title/Layer Text | `layerItem`         | `text`           | `{uniqueId}`                   | Title text, headings, or overlay text  |
| Layer Visual     | `layerItem`         | `video`, `image` | `{uniqueId}`                   | Overlay images, videos, or avatars     |
| Background Music | `audioElement`      | `audio`          | `bgMusic`                      | Background music track                 |
| Voice-Over       | `audioElement`      | `audio`          | `voiceOver`                    | AI-generated narration audio           |

***

### Background Element (`backgroundElement`)

The background element is the full-screen visual behind each scene. One background element exists per scene.

#### Media Properties

| Property          | Type   | Description                                                                                                                                                                      | Example                                                                        |
| ----------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `url`             | string | Full-resolution media URL used for final video rendering                                                                                                                         | `"https://cdn.com/video-hd.mp4"`                                               |
| `visualUrl`       | string | Media URL used for preview playback. Can be a lower-resolution video or even a thumbnail image for faster preview loading. When rendering the final video, `url` is used instead | `"https://cdn.com/video-preview.mp4"` or `"https://cdn.com/preview-thumb.jpg"` |
| `type`            | string | Media type of `url`: `video` or `image`                                                                                                                                          | `"video"`                                                                      |
| `visualType`      | string | Media type of `visualUrl`. Can differ from `type`; e.g., `visualType` may be `image` (thumbnail) while `type` is `video` (full render)                                           | `"image"`                                                                      |
| `backgroundColor` | string | Solid background color in RGBA format. Used as fallback or when no media URL is set                                                                                              | `"rgba(0, 0, 0, 1)"`                                                           |

#### Display Properties

| Property     | Type   | Default     | Description                                                                                          |
| ------------ | ------ | ----------- | ---------------------------------------------------------------------------------------------------- |
| `width`      | string | `"100%"`    | Element width as a percentage of the video frame                                                     |
| `xPercent`   | string | `"0%"`      | Horizontal position offset as percentage                                                             |
| `yPercent`   | string | `"0%"`      | Vertical position offset as percentage                                                               |
| `objectMode` | string | `"cover"`   | How the media fits the frame. `cover` fills and crops to fit, `fit` letterboxes to show entire media |
| `cursor`     | string | `"default"` | Mouse cursor style in the editor                                                                     |

#### Video Playback Properties

| Property   | Type    | Default | Description                                                                 |
| ---------- | ------- | ------- | --------------------------------------------------------------------------- |
| `loop`     | boolean | `true`  | Whether to loop video playback when it reaches the end                      |
| `mute`     | boolean | `true`  | Whether to mute the video's built-in audio track                            |
| `segments` | array   | `[]`    | Time segments for video clipping. Each segment: `{ "start": 0, "end": 10 }` |

#### Visual Effects

| Property               | Type    | Description                                             | Example            |
| ---------------------- | ------- | ------------------------------------------------------- | ------------------ |
| `colorOverlay`         | object  | Semi-transparent color overlay on top of the background | See below          |
| `colorOverlay.hide`    | boolean | Whether the overlay is hidden                           | `false`            |
| `colorOverlay.bgColor` | string  | Overlay color in RGB format                             | `"rgb(22,91,110)"` |
| `colorOverlay.opacity` | number  | Overlay opacity from 0 (transparent) to 1 (opaque)      | `0.65`             |

#### Metadata

| Property  | Type   | Description                                      | Example   |
| --------- | ------ | ------------------------------------------------ | --------- |
| `library` | string | Source library where the media was selected from | `"getty"` |

<Accordion title="Full Background Element Example">
  ```json theme={null}
  {
    "id": "backgroundElement_20260306230202544742ecd40c041449dac21bb737c799201",
    "elementType": "backgroundElement",
    "type": "video",
    "url": "https://media.gettyimages.com/id/2262113992/video/professor-lectures.mp4",
    "backgroundColor": "rgba(0, 0, 0, 1)",
    "xPercent": "0%",
    "yPercent": "0%",
    "width": "100%",
    "objectMode": "cover",
    "loop": true,
    "mute": true,
    "segments": [],
    "startTime": 0,
    "duration": 7.6,
    "colorOverlay": {
      "hide": false,
      "bgColor": "rgb(22,91,110)",
      "opacity": 0.65
    },
    "cursor": "default",
    "visualUrl": "https://media.gettyimages.com/id/2262113992/video/professor-lectures.mp4",
    "visualType": "video",
    "library": "getty"
  }
  ```
</Accordion>

***

### Scene Text (`SceneText`) and Layer Text (`layerItem` with `type: "text"`)

Text elements display text content on top of scenes. **SceneText** elements represent the per-sentence text overlay for a scene, while **layerItem** text elements represent titles, headings, or additional text overlays.

#### Content Properties

| Property | Type   | Description                                                                                  | Example                                |
| -------- | ------ | -------------------------------------------------------------------------------------------- | -------------------------------------- |
| `text`   | string | Text content to display. Supports `<strong>` tags to highlight keywords using `keywordColor` | `"AI will <strong>save time</strong>"` |

#### Font & Styling Properties

| Property       | Type   | Default   | Description                                                                                                     | Example                    |
| -------------- | ------ | --------- | --------------------------------------------------------------------------------------------------------------- | -------------------------- |
| `fontFamily`   | string | `"Arial"` | Font family name                                                                                                | `"Space Grotesk"`          |
| `fontWeight`   | number | `400`     | Font weight (400 = normal, 700 = bold)                                                                          | `400`                      |
| `fontSize`     | string | `"16"`    | Font size in pixels                                                                                             | `"32"`                     |
| `fontColor`    | string | -         | Text color in RGB or RGBA format                                                                                | `"rgb(255,255,255)"`       |
| `keywordColor` | string | -         | Color applied to text wrapped in `<strong>` tags                                                                | `"rgba(248, 173, 151, 1)"` |
| `textAlign`    | string | `"left"`  | Text alignment: `left`, `center`, or `right`                                                                    | `"center"`                 |
| `decoration`   | array  | `[]`      | Text decorations. Values: `"decor-bold"`, `"decor-italics"`, `"decor-underline"`, `"decor-linethrough"`         | `["decor-bold"]`           |
| `case`         | string | `"none"`  | Text case transformation: `none`, `case-uppercase`, `case-lowercase`, `case-capitalize`, `case-smallcapitalize` | `"none"`                   |
| `lineHeight`   | number | `1.3`     | Line height multiplier for spacing between lines                                                                | `1.48`                     |

#### Text Background Properties

| Property               | Type    | Default           | Description                                                                               | Example                  |
| ---------------------- | ------- | ----------------- | ----------------------------------------------------------------------------------------- | ------------------------ |
| `textBackgroundColor`  | string  | `"rgba(0,0,0,0)"` | Background color behind the text or as a fill for the full-width box                      | `"rgba(22, 91, 110, 1)"` |
| `textBackgroundRadius` | array   | `[0,0,0,0]`       | Border radius for the text background box: `[topLeft, topRight, bottomRight, bottomLeft]` | `[8, 8, 8, 8]`           |
| `boxBackground`        | boolean | `false`           | Whether text has a visible background box                                                 | `false`                  |

#### Text Shadow Properties

| Property          | Type   | Default           | Description                      | Example           |
| ----------------- | ------ | ----------------- | -------------------------------- | ----------------- |
| `dropShadowColor` | string | `"rgba(0,0,0,0)"` | Drop shadow color in RGBA format | `"rgba(0,0,0,1)"` |
| `dropShadowBlur`  | number | `0`               | Shadow blur radius               | `3`               |

#### Layout & Position Properties

| Property             | Type    | Default | Description                                                       | Example         |
| -------------------- | ------- | ------- | ----------------------------------------------------------------- | --------------- |
| `width`              | string  | `"90%"` | Text box width as percentage of the video frame                   | `"70.00%"`      |
| `fullWidth`          | boolean | `false` | Whether text spans the full viewport width with padding           | `false`         |
| `preset`             | string  | -       | Position preset anchor. See [Position Presets](#position-presets) | `"bottom-left"` |
| `opacity`            | number  | `1`     | Element transparency from 0 (invisible) to 1 (opaque)             | `1`             |
| `draggable`          | boolean | `true`  | Whether the element can be repositioned in the editor             | `true`          |
| `resizable`          | boolean | `false` | Whether the element can be resized in the editor                  | `false`         |
| `canChangeWidthOnly` | boolean | `true`  | Restrict resizing to width only (height adjusts automatically)    | `true`          |
| `snapTo`             | boolean | `true`  | Snap to alignment guidelines when dragging                        | `true`          |
| `midHandle`          | boolean | `false` | Use center handle for resizing                                    | `false`         |
| `cropable`           | boolean | `false` | Whether the element can be cropped                                | `false`         |

#### Bullet Properties

| Property      | Type    | Default     | Description                                  | Example     |
| ------------- | ------- | ----------- | -------------------------------------------- | ----------- |
| `bullet`      | boolean | `false`     | Display a bullet point beside each text line | `false`     |
| `bulletColor` | string  | `"#e30022"` | Bullet point color                           | `"#e30022"` |
| `bulletWidth` | number  | `15`        | Bullet point width in pixels                 | `15`        |

#### Animation Properties

| Property                   | Type   | Description                                                                                                        | Example          |
| -------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------ | ---------------- |
| `animation`                | array  | Array of animation objects for entry and exit effects                                                              | See below        |
| `animation[].name`         | string | Animation name: `Blur`, `Drift`, `Fade`, `Wipe`, `Show`, `Elastic`, `Typewriter`, `TextReveal`, `Bulletin`, `None` | `"Blur"`         |
| `animation[].type`         | array  | Animation phase: `["entry"]` or `["exit"]`                                                                         | `["entry"]`      |
| `animation[].writingStyle` | string | How text animates in: `character` (per letter), `word` (per word), `line` (per line), `paragraph` (all at once)    | `"paragraph"`    |
| `animation[].speed`        | object | Animation speed configuration                                                                                      | `{ "value": 2 }` |
| `animation[].speed.value`  | number | Speed value (higher = faster)                                                                                      | `2`              |
| `animation[].direction`    | string | Direction of animation: `up`, `down`, `left`, `right`                                                              | `"up"`           |

#### Computed Properties (read-only)

| Property    | Type  | Description                                                                                                                                                                                                                                                                                                                                                                              |
| ----------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `textLines` | array | Pre-calculated line break and character position data used by the rendering engine. Each entry contains character positions (`charX`), highlight flags (`h`), and line dimensions (`ht`). This is auto-generated. **When updating text content, you must set `textLines` to an empty array `[]` so that line breaks and character positions are recalculated during preview and render** |

<Accordion title="Full Scene Text Element Example">
  ```json theme={null}
  {
    "id": "SceneText_202603062302035443dd4953bf1294b4283d4b8ba0259fd1a",
    "elementType": "SceneText",
    "type": "text",
    "text": "By <strong>automating</strong> <strong>tasks</strong> like <strong>content generation</strong>, <strong>visual design</strong>, and <strong>video editing</strong>, AI will <strong>save time</strong> and <strong>enhance consistency</strong>.",
    "fontFamily": "Space Grotesk",
    "fontWeight": 400,
    "fontSize": "32",
    "fontColor": "rgb(255,255,255)",
    "keywordColor": "rgba(248, 173, 151, 1)",
    "textBackgroundColor": "rgba(22, 91, 110, 1)",
    "dropShadowColor": "rgba(0,0,0,0)",
    "dropShadowBlur": 3,
    "textAlign": "left",
    "decoration": [],
    "case": "none",
    "lineHeight": 1.465625,
    "textBackgroundRadius": [0, 0, 0, 0],
    "boxBackground": false,
    "width": "70.00%",
    "preset": "bottom-left",
    "opacity": 1,
    "draggable": true,
    "resizable": false,
    "snapTo": true,
    "midHandle": false,
    "canChangeWidthOnly": true,
    "cropable": false,
    "startTime": 7.6,
    "duration": 11.1,
    "animation": [
      {
        "writingStyle": "line",
        "name": "Drift",
        "type": ["entry"],
        "speed": { "value": 1 },
        "direction": "left"
      },
      {
        "name": "None",
        "type": ["exit"]
      }
    ]
  }
  ```
</Accordion>

<Accordion title="Full Title/Layer Text Element Example">
  ```json theme={null}
  {
    "id": "2026030623020497002befca37e444fb880a281abadc0c756",
    "elementType": "layerItem",
    "type": "text",
    "text": "AI's Impact on Educators and Creators",
    "fontFamily": "Space Grotesk",
    "fontWeight": 400,
    "fontSize": "48",
    "fontColor": "rgb(255,255,255)",
    "keywordColor": "rgba(248, 173, 151, 1)",
    "textBackgroundColor": "rgba(0,0,0,0)",
    "dropShadowColor": "rgba(0,0,0,0)",
    "dropShadowBlur": 3,
    "textAlign": "center",
    "decoration": [],
    "case": "none",
    "lineHeight": 1.48125,
    "textBackgroundRadius": [0, 0, 0, 0],
    "boxBackground": false,
    "width": "90.00%",
    "fullWidth": false,
    "preset": "center-center",
    "opacity": 1,
    "draggable": true,
    "resizable": false,
    "snapTo": true,
    "midHandle": false,
    "canChangeWidthOnly": true,
    "cropable": false,
    "startTime": 0,
    "duration": 7.6,
    "animation": [
      {
        "writingStyle": "paragraph",
        "name": "Blur",
        "type": ["entry"],
        "speed": { "value": 2 },
        "direction": "up"
      },
      {
        "name": "None",
        "type": ["exit"]
      }
    ]
  }
  ```
</Accordion>

***

### Audio Element (`audioElement`)

Audio elements represent background music (`bgMusic`) or voice-over narration (`voiceOver`).

#### Audio Properties

| Property | Type    | Description                                                                             | Example                                   |
| -------- | ------- | --------------------------------------------------------------------------------------- | ----------------------------------------- |
| `url`    | string  | Audio file URL (MP3 or compatible format)                                               | `"https://tracks.melod.ie/.../track.mp3"` |
| `fade`   | boolean | Whether to apply fade in/out effect to the audio. Typically `true` for background music | `true`                                    |

#### Segments

| Property               | Type   | Description                                                                                                                                            | Example   |
| ---------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- |
| `segments`             | array  | Array of audio segment objects. Each segment defines a playback range with its own volume level. Segments correspond to individual scenes in the video | See below |
| `segments[].startTime` | number | Start time of the segment in the video timeline (seconds)                                                                                              | `0`       |
| `segments[].duration`  | number | Duration of the segment (seconds)                                                                                                                      | `7.6`     |
| `segments[].volume`    | number | Volume level for this segment (0 to 1). Background music typically uses `0.1`, voice-over uses `1`                                                     | `0.1`     |
| `segments[].audioTime` | number | Offset position in the audio file to start playback from (seconds)                                                                                     | `0`       |

<Accordion title="Full Background Music Element Example">
  ```json theme={null}
  {
    "type": "audio",
    "id": "bgMusic",
    "elementType": "audioElement",
    "fade": true,
    "url": "https://tracks.melod.ie/track_versions/23517/MEL565_15_1_Utopia.mp3",
    "segments": [
      { "startTime": 0, "duration": 7.6, "volume": 0.1, "audioTime": 0 },
      { "startTime": 7.6, "duration": 11.1, "volume": 0.1, "audioTime": 7.6 },
      { "startTime": 18.7, "duration": 7.6, "volume": 0.1, "audioTime": 18.7 },
      { "startTime": 26.3, "duration": 11, "volume": 0.1, "audioTime": 26.3 },
      { "startTime": 37.3, "duration": 8.5, "volume": 0.1, "audioTime": 37.3 },
      { "startTime": 45.8, "duration": 9.1, "volume": 0.1, "audioTime": 45.8 },
      { "startTime": 54.9, "duration": 10.9, "volume": 0.1, "audioTime": 54.9 }
    ],
    "startTime": 0,
    "duration": 100000
  }
  ```
</Accordion>

<Accordion title="Full Voice-Over Element Example">
  ```json theme={null}
  {
    "type": "audio",
    "id": "voiceOver",
    "elementType": "audioElement",
    "url": "https://audios-prod.pictorycontent.com/polly/production/projects/.../voiceover.mp3",
    "segments": [
      { "startTime": 0, "duration": 7.6, "volume": 1, "audioTime": 0 },
      { "startTime": 7.6, "duration": 11.1, "volume": 1, "audioTime": 7.6 },
      { "startTime": 18.7, "duration": 7.6, "volume": 1, "audioTime": 18.7 },
      { "startTime": 26.3, "duration": 11, "volume": 1, "audioTime": 26.3 },
      { "startTime": 37.3, "duration": 8.5, "volume": 1, "audioTime": 37.3 },
      { "startTime": 45.8, "duration": 9.1, "volume": 1, "audioTime": 45.8 },
      { "startTime": 54.9, "duration": 10.9, "volume": 1, "audioTime": 54.9 }
    ],
    "startTime": 0,
    "duration": 100000
  }
  ```
</Accordion>

***

### Layer Visual Element (`layerItem` with `type: "video"` or `"image"`)

Layer visual elements are overlay images or videos displayed on top of the background, such as avatars, logos, or decorative graphics.

| Property         | Type    | Default       | Description                                                                                                          | Example                                                                        |
| ---------------- | ------- | ------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `url`            | string  | -             | Full-resolution media URL used for final video rendering                                                             | `"https://cdn.com/avatar-hd.mp4"`                                              |
| `visualUrl`      | string  | -             | Media URL used for preview playback. Can be a lower-resolution video or a thumbnail image for faster preview loading | `"https://cdn.com/avatar-preview.mp4"` or `"https://cdn.com/avatar-thumb.jpg"` |
| `type`           | string  | -             | Media type of `url`: `video` or `image`                                                                              | `"video"`                                                                      |
| `visualType`     | string  | -             | Media type of `visualUrl`. Can differ from `type` (e.g., `image` thumbnail for a `video` render)                     | `"image"`                                                                      |
| `objectMode`     | string  | `"cover"`     | How media fits: `cover` (fill and crop) or `fit` (letterbox)                                                         | `"cover"`                                                                      |
| `width`          | string  | -             | Element width as percentage                                                                                          | `"25%"`                                                                        |
| `height`         | string  | -             | Element height as percentage (optional)                                                                              | `"30%"`                                                                        |
| `preset`         | string  | -             | Position preset anchor                                                                                               | `"bottom-right"`                                                               |
| `xPercent`       | string  | -             | Horizontal position when no preset                                                                                   | `"70%"`                                                                        |
| `yPercent`       | string  | -             | Vertical position when no preset                                                                                     | `"60%"`                                                                        |
| `opacity`        | number  | `1`           | Transparency (0 to 1)                                                                                                | `1`                                                                            |
| `loop`           | boolean | `true`        | Loop video playback                                                                                                  | `true`                                                                         |
| `mute`           | boolean | `false`       | Mute video audio                                                                                                     | `true`                                                                         |
| `aspectRatio`    | number  | -             | Width/height ratio                                                                                                   | `0.5625`                                                                       |
| `flipHorizontal` | boolean | `false`       | Mirror the element horizontally                                                                                      | `false`                                                                        |
| `flipVertical`   | boolean | `false`       | Mirror the element vertically                                                                                        | `false`                                                                        |
| `maskShape`      | string  | `"rectangle"` | Mask shape: `rectangle` or `circle`                                                                                  | `"circle"`                                                                     |
| `library`        | string  | -             | Source library (e.g., `avatar`, `giphy`)                                                                             | `"avatar"`                                                                     |

***

### Position Presets

The `preset` property defines a named anchor position for text and layer elements within the video frame:

| Preset              | Description                     |
| ------------------- | ------------------------------- |
| `top-left`          | Anchored to top-left corner     |
| `top-center`        | Anchored to top-center          |
| `top-right`         | Anchored to top-right corner    |
| `center-left`       | Anchored to middle-left         |
| `center-center`     | Centered in the frame           |
| `center-right`      | Anchored to middle-right        |
| `bottom-left`       | Anchored to bottom-left corner  |
| `bottom-center`     | Anchored to bottom-center       |
| `bottom-right`      | Anchored to bottom-right corner |
| `full-width-top`    | Full width, anchored to top     |
| `full-width-center` | Full width, centered vertically |
| `full-width-bottom` | Full width, anchored to bottom  |

***

### Text Animations

Text elements support entry and exit animations. The `animation` array contains animation configuration objects:

| Animation Name | Description                               |
| -------------- | ----------------------------------------- |
| `Blur`         | Text fades in with a blur effect          |
| `Drift`        | Text slides in from a specified direction |
| `Fade`         | Simple fade in/out                        |
| `Wipe`         | Text wipes in from a direction            |
| `Show`         | Instant appearance                        |
| `Elastic`      | Bouncy elastic entrance                   |
| `Typewriter`   | Characters appear one by one              |
| `TextReveal`   | Text reveals progressively                |
| `Bulletin`     | News bulletin style animation             |
| `None`         | No animation                              |

**Writing Styles** control the granularity of the animation:

| Writing Style | Description                           |
| ------------- | ------------------------------------- |
| `character`   | Animate one character at a time       |
| `word`        | Animate one word at a time            |
| `line`        | Animate one line at a time            |
| `paragraph`   | Animate the entire text block at once |

***

## Response

<ResponseExample>
  ```json 204 - Success theme={null}
  // No content returned. The elements have been updated successfully.
  ```

  ```json 400 - Bad Request theme={null}
  {
    "success": false,
    "error": {
      "code": "INVALID_REQUEST",
      "message": "Validation error details"
    }
  }
  ```

  ```json 401 - Unauthorized theme={null}
  {
    "message": "Unauthorized"
  }
  ```
</ResponseExample>

A `204 No Content` response indicates the elements were updated successfully. The updated elements will be reflected when you:

* Retrieve the job details using the [Get Storyboard Preview Job by ID](/api-reference/jobs/get-storyboard-preview-job-by-id) API
* Render the video using [Render from Preview](/api-reference/videos/render-from-preview)

***

## Code Examples

<Tip>
  Replace `YOUR_API_KEY` with your actual API key and `YOUR_JOB_ID` with the storyboard preview job ID.
</Tip>

### Update Background Visual

<CodeGroup>
  ```bash cURL theme={null}
  curl --request PUT \
    --url https://api.pictory.ai/pictoryapis/v2/video/storyboard/YOUR_JOB_ID/elements \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '[
      {
        "id": "backgroundElement_20260306230202544742ecd40c041449dac21bb737c799201",
        "elementType": "backgroundElement",
        "type": "video",
        "startTime": 0,
        "duration": 7.6,
        "url": "https://your-cdn.com/new-background.mp4",
        "visualUrl": "https://your-cdn.com/new-background.mp4",
        "visualType": "video"
      }
    ]'
  ```

  ```javascript JavaScript theme={null}
  // Get the element ID from the preview response renderParams.elements
  const elementId = renderParams.elements.find(
    el => el.elementType === 'backgroundElement' && el.startTime === 0
  ).id;

  const response = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/video/storyboard/${jobId}/elements`,
    {
      method: 'PUT',
      headers: {
        'Authorization': 'YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify([
        {
          id: elementId,
          elementType: 'backgroundElement',
          type: 'video',
          startTime: 0,
          duration: 7.6,
          url: 'https://your-cdn.com/new-background.mp4',
          visualUrl: 'https://your-cdn.com/new-background.mp4',
          visualType: 'video'
        }
      ])
    }
  );

  if (response.status === 204) {
    console.log('Background visual updated successfully');
  }
  ```

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

  # Get the element ID from the preview response renderParams.elements
  element_id = next(
      el['id'] for el in render_params['elements']
      if el['elementType'] == 'backgroundElement' and el['startTime'] == 0
  )

  response = requests.put(
      f'https://api.pictory.ai/pictoryapis/v2/video/storyboard/{job_id}/elements',
      headers={
          'Authorization': 'YOUR_API_KEY',
          'Content-Type': 'application/json'
      },
      json=[
          {
              'id': element_id,
              'elementType': 'backgroundElement',
              'type': 'video',
              'startTime': 0,
              'duration': 7.6,
              'url': 'https://your-cdn.com/new-background.mp4',
              'visualUrl': 'https://your-cdn.com/new-background.mp4',
              'visualType': 'video'
          }
      ]
  )

  if response.status_code == 204:
      print('Background visual updated successfully')
  ```

  ```php PHP theme={null}
  <?php

  $jobId = 'YOUR_JOB_ID';
  // Use the element ID from the preview response renderParams.elements
  $elementId = 'backgroundElement_20260306230202544742ecd40c041449dac21bb737c799201';
  $url = "https://api.pictory.ai/pictoryapis/v2/video/storyboard/{$jobId}/elements";

  $payload = [
      [
          'id' => $elementId,
          'elementType' => 'backgroundElement',
          'type' => 'video',
          'startTime' => 0,
          'duration' => 7.6,
          'url' => 'https://your-cdn.com/new-background.mp4',
          'visualUrl' => 'https://your-cdn.com/new-background.mp4',
          'visualType' => 'video'
      ]
  ];

  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: YOUR_API_KEY',
      'Content-Type: application/json'
  ]);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

  $response = curl_exec($ch);
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if ($httpCode === 204) {
      echo "Background visual updated successfully\n";
  }
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
  )

  func main() {
      jobID := "YOUR_JOB_ID"
      // Use the element ID from the preview response renderParams.elements
      elementID := "backgroundElement_20260306230202544742ecd40c041449dac21bb737c799201"
      url := fmt.Sprintf("https://api.pictory.ai/pictoryapis/v2/video/storyboard/%s/elements", jobID)

      payload := []map[string]interface{}{
          {
              "id":          elementID,
              "elementType": "backgroundElement",
              "type":        "video",
              "startTime":   0,
              "duration":    7.6,
              "url":         "https://your-cdn.com/new-background.mp4",
              "visualUrl":   "https://your-cdn.com/new-background.mp4",
              "visualType":  "video",
          },
      }

      requestBody, _ := json.Marshal(payload)

      req, err := http.NewRequest("PUT", url, bytes.NewBuffer(requestBody))
      if err != nil {
          panic(err)
      }

      req.Header.Set("Authorization", "YOUR_API_KEY")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      if resp.StatusCode == 204 {
          fmt.Println("Background visual updated successfully")
      }
  }
  ```
</CodeGroup>

### Update Scene Text

<CodeGroup>
  ```javascript JavaScript theme={null}
  // Find the scene text element to update
  const sceneTextElement = renderParams.elements.find(
    el => el.elementType === 'SceneText' && el.startTime === 7.6
  );

  const response = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/video/storyboard/${jobId}/elements`,
    {
      method: 'PUT',
      headers: {
        'Authorization': 'YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify([
        {
          id: sceneTextElement.id,
          elementType: 'SceneText',
          type: 'text',
          startTime: sceneTextElement.startTime,
          duration: sceneTextElement.duration,
          text: 'By <strong>automating</strong> repetitive <strong>tasks</strong>, AI frees up time for <strong>creative work</strong> and <strong>strategic thinking</strong>.',
          textLines: [] // Clear textLines so line breaks are recalculated
        }
      ])
    }
  );

  if (response.status === 204) {
    console.log('Scene text updated successfully');
  }
  ```

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

  # Find the scene text element to update
  scene_text = next(
      el for el in render_params['elements']
      if el['elementType'] == 'SceneText' and el['startTime'] == 7.6
  )

  response = requests.put(
      f'https://api.pictory.ai/pictoryapis/v2/video/storyboard/{job_id}/elements',
      headers={
          'Authorization': 'YOUR_API_KEY',
          'Content-Type': 'application/json'
      },
      json=[
          {
              'id': scene_text['id'],
              'elementType': 'SceneText',
              'type': 'text',
              'startTime': scene_text['startTime'],
              'duration': scene_text['duration'],
              'text': 'By <strong>automating</strong> repetitive <strong>tasks</strong>, AI frees up time for <strong>creative work</strong> and <strong>strategic thinking</strong>.',
              'textLines': []  # Clear textLines so line breaks are recalculated
          }
      ]
  )

  if response.status_code == 204:
      print('Scene text updated successfully')
  ```
</CodeGroup>

### Update Background Music

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/video/storyboard/${jobId}/elements`,
    {
      method: 'PUT',
      headers: {
        'Authorization': 'YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify([
        {
          id: 'bgMusic',
          elementType: 'audioElement',
          type: 'audio',
          startTime: 0,
          duration: 100000,
          url: 'https://your-cdn.com/new-background-music.mp3'
        }
      ])
    }
  );

  if (response.status === 204) {
    console.log('Background music updated successfully');
  }
  ```

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

  response = requests.put(
      f'https://api.pictory.ai/pictoryapis/v2/video/storyboard/{job_id}/elements',
      headers={
          'Authorization': 'YOUR_API_KEY',
          'Content-Type': 'application/json'
      },
      json=[
          {
              'id': 'bgMusic',
              'elementType': 'audioElement',
              'type': 'audio',
              'startTime': 0,
              'duration': 100000,
              'url': 'https://your-cdn.com/new-background-music.mp3'
          }
      ]
  )

  if response.status_code == 204:
      print('Background music updated successfully')
  ```
</CodeGroup>

### Update Multiple Elements

<CodeGroup>
  ```javascript JavaScript theme={null}
  // Update background visuals, scene text, and music in a single request
  const elements = renderParams.elements;

  // Find elements by type
  const bg1 = elements.find(el => el.elementType === 'backgroundElement' && el.startTime === 0);
  const bg2 = elements.find(el => el.elementType === 'backgroundElement' && el.startTime === 7.6);
  const sceneText = elements.find(el => el.elementType === 'SceneText' && el.startTime === 7.6);

  const response = await fetch(
    `https://api.pictory.ai/pictoryapis/v2/video/storyboard/${jobId}/elements`,
    {
      method: 'PUT',
      headers: {
        'Authorization': 'YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify([
        // Replace scene 1 background with a custom image
        {
          id: bg1.id,
          elementType: 'backgroundElement',
          type: 'image',
          startTime: 0,
          duration: 7.6,
          url: 'https://your-cdn.com/intro-background.jpg',
          visualUrl: 'https://your-cdn.com/intro-background.jpg',
          visualType: 'image'
        },
        // Replace scene 2 background with a custom video
        {
          id: bg2.id,
          elementType: 'backgroundElement',
          type: 'video',
          startTime: 7.6,
          duration: 11.1,
          url: 'https://your-cdn.com/demo-footage.mp4',
          visualUrl: 'https://your-cdn.com/demo-footage.mp4',
          visualType: 'video'
        },
        // Update scene text: set textLines to null so line breaks are recalculated
        {
          id: sceneText.id,
          elementType: 'SceneText',
          type: 'text',
          startTime: 7.6,
          duration: 11.1,
          text: 'Updated scene text with <strong>highlighted keywords</strong> for emphasis.',
          textLines: []
        },
        // Replace background music
        {
          id: 'bgMusic',
          elementType: 'audioElement',
          type: 'audio',
          startTime: 0,
          duration: 100000,
          url: 'https://your-cdn.com/corporate-music.mp3'
        }
      ])
    }
  );

  if (response.status === 204) {
    console.log('All elements updated successfully');
  }
  ```

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

  elements = render_params['elements']

  # Find elements by type
  bg1 = next(el for el in elements if el['elementType'] == 'backgroundElement' and el['startTime'] == 0)
  bg2 = next(el for el in elements if el['elementType'] == 'backgroundElement' and el['startTime'] == 7.6)
  scene_text = next(el for el in elements if el['elementType'] == 'SceneText' and el['startTime'] == 7.6)

  response = requests.put(
      f'https://api.pictory.ai/pictoryapis/v2/video/storyboard/{job_id}/elements',
      headers={
          'Authorization': 'YOUR_API_KEY',
          'Content-Type': 'application/json'
      },
      json=[
          # Replace scene 1 background with a custom image
          {
              'id': bg1['id'],
              'elementType': 'backgroundElement',
              'type': 'image',
              'startTime': 0,
              'duration': 7.6,
              'url': 'https://your-cdn.com/intro-background.jpg',
              'visualUrl': 'https://your-cdn.com/intro-background.jpg',
              'visualType': 'image'
          },
          # Replace scene 2 background with a custom video
          {
              'id': bg2['id'],
              'elementType': 'backgroundElement',
              'type': 'video',
              'startTime': 7.6,
              'duration': 11.1,
              'url': 'https://your-cdn.com/demo-footage.mp4',
              'visualUrl': 'https://your-cdn.com/demo-footage.mp4',
              'visualType': 'video'
          },
          # Update scene text: set textLines to None so line breaks are recalculated
          {
              'id': scene_text['id'],
              'elementType': 'SceneText',
              'type': 'text',
              'startTime': 7.6,
              'duration': 11.1,
              'text': 'Updated scene text with <strong>highlighted keywords</strong> for emphasis.',
              'textLines': None
          },
          # Replace background music
          {
              'id': 'bgMusic',
              'elementType': 'audioElement',
              'type': 'audio',
              'startTime': 0,
              'duration': 100000,
              'url': 'https://your-cdn.com/corporate-music.mp3'
          }
      ]
  )

  if response.status_code == 204:
      print('All elements updated successfully')
  ```
</CodeGroup>

***

## Important Notes

<AccordionGroup>
  <Accordion title="Only existing elements can be updated">
    You cannot add new elements to the storyboard. Only elements that already exist in the `renderParams.elements` array (returned from the storyboard preview job) can be updated. Elements are matched by their unique `id`.
  </Accordion>

  <Accordion title="Element IDs come from the preview response">
    After creating a storyboard preview and polling the job to completion, the response includes `renderParams.elements`, an array of all elements with their `id` values. Element IDs are auto-generated unique strings. Always retrieve the actual IDs from the response rather than constructing them manually.
  </Accordion>

  <Accordion title="Updates are merged, not replaced">
    When you update an element, the properties you provide are merged with the existing element properties. Properties you do not include in the update remain unchanged. For example, updating only the `url` of a background element will keep its existing `loop`, `mute`, `objectMode`, and other properties.
  </Accordion>

  <Accordion title="Remove textLines when updating text content">
    When updating the `text` property of a `SceneText` or `layerItem` text element, you must set `textLines` to an empty array `[]`. The `textLines` array contains pre-calculated line break positions and character coordinates that are specific to the original text. If you change the text but keep the old `textLines`, the rendering engine will use stale layout data, causing incorrect line breaks and character positioning. Setting `textLines` to `[]` ensures the system recalculates the layout during preview and video rendering.
  </Accordion>

  <Accordion title="Scene text supports HTML highlighting">
    Scene text (`SceneText`) elements support `<strong>` tags for keyword highlighting. For example: `"This is <strong>important</strong> text"` will render "important" with the element's `keywordColor`.
  </Accordion>

  <Accordion title="Updates persist for rendering">
    Once elements are updated, the changes are saved to the storyboard job. When you subsequently render the video using [Render from Preview](/api-reference/videos/render-from-preview), the updated elements will be used in the final render.
  </Accordion>
</AccordionGroup>

***

## Related APIs

<CardGroup cols={2}>
  <Card title="Create Storyboard Preview" icon="eye" href="/api-reference/videos/create-storyboard-preview">
    Generate a storyboard preview with elements to update
  </Card>

  <Card title="Render from Preview" icon="film" href="/api-reference/videos/render-from-preview">
    Render the final video from the updated storyboard
  </Card>

  <Card title="Render Storyboard Video" icon="video" href="/api-reference/videos/render-storyboard-video">
    Render video directly from storyboard input
  </Card>

  <Card title="Get Storyboard Preview Job" icon="spinner" href="/api-reference/jobs/get-storyboard-preview-job-by-id">
    Check job status and retrieve render params
  </Card>
</CardGroup>
