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

# Video API

> Create AI-generated educational videos from text prompts and conversations.

# Video Generation API

The Opennote Video API enables you to create high-quality educational videos from text prompts, scripts, or conversational message formats. Our AI-powered video generation system transforms your content into engaging visual presentations with narration, graphics, and educational elements.

You can create videos from text prompts, direct scripts, or conversational messages.

<Steps>
  <Step title="Submit Video Job Request">
    Send your content with model preferences and configuration options.
  </Step>

  <Step title="Check Status While Processing">
    Our system processes your content, generates visuals, and creates narration. You can view the status of your video job by polling the status endpoint, or wait for the webhook to be triggered.
  </Step>

  <Step title="Deliver Your Video">
    Get your completed video via S3 URL or direct `base64` encoded video.
  </Step>
</Steps>

## Quick Start Example

<CodeGroup>
  ```python Python theme={null}
  from opennote import OpennoteClient
  import time

  client = OpennoteClient(api_key="your_editable_api_key")

  # Create a video from a simple prompt
  response = client.video.create(
      model="picasso",
      messages=[
          {"role": "system", "content": "Create educational content for high school students"},
          {"role": "user", "content": "Explain photosynthesis in plants with visual examples"}
      ],
      title="Photosynthesis in Plants",
      upload_to_s3=True,
      include_sources=True,
      search_for="plant photosynthesis biology",
      source_count=3
  )

  if response.success:
      print(f"Video creation started! ID: {response.video_id}")
      
      # Poll for completion
      while True:
          status = client.video.status(response.video_id)
          print(f"Status: {status.status} ({status.progress}%)")
          
          if status.status == "completed":
              print(f"Video ready: {status.response.s3_url}!")
              break
          elif status.status == "failed":
              print(f"ERROR, Video creation failed: {status.message}")
              break
          
          time.sleep(15)
  ```

  ```typescript TypeScript theme={null}
  import { OpennoteClient } from '@opennote-ed/sdk';

  const client = new OpennoteClient('your_editable_api_key');

  // Create a video from a simple prompt
  const response = await client.video.create({
      model: "picasso",
      messages: [
          { role: "system", content: "Create educational content for high school students" },
          { role: "user", content: "Explain photosynthesis in plants with visual examples" }
      ],
      title: "Photosynthesis in Plants",
      upload_to_s3: true,
      include_sources: true,
      search_for: "plant photosynthesis biology",
      source_count: 3
  });

  if (response.success && response.video_id) {
      console.log(`Video creation started! ID: ${response.video_id}`);
      
      // Poll for completion
      while (true) {
          const status = await client.video.status(response.video_id);
          console.log(`Status: ${status.status} (${status.progress}%)`);
          
          if (status.status === "completed" && status.response?.s3_url) {
              console.log(`Video ready: ${status.response.s3_url}!`);
              break;
          } else if (status.status === "failed") {
              console.error(`ERROR, Video creation failed: ${status.message}`);
              break;
          }
          
          await new Promise(resolve => setTimeout(resolve, 15000));
      }
  }
  ```

  ```bash cURL theme={null}
  # Create video
  curl -X POST "https://api.opennote.com/v1/video/create" \
    -H "Authorization: Bearer your_editable_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "picasso",
      "messages": [
        {"role": "system", "content": "Create educational content for high school students"},
        {"role": "user", "content": "Explain photosynthesis in plants with visual examples"}
      ],
      "title": "Photosynthesis in Plants",
      "upload_to_s3": true,
      "include_sources": true,
      "search_for": "plant photosynthesis biology",
      "source_count": 3
    }'

  # Check status
  curl -X GET "https://api.opennote.com/v1/video/status/{video_id}" \
    -H "Authorization: Bearer your_editable_api_key"
  ```
</CodeGroup>

## Configuration Options

The only `model` available as of right now is `picasso`. We are working on more models in the future, as well as dated compatability for models.

### Message Formats

#### System Messages (Optional)

Provide context about the audience, style, or educational goals:

```json theme={null}
{
  "role": "system", 
  "content": "You are creating content for college-level chemistry students. Use technical terms but explain them clearly."
}
```

#### User and Assistant Messages

With the `messages` parameter, you can provide a list of messages, with the first message being the system message, and the last message being a user message.
This can simulate a conversation between an assistant and a user, which can be useful for creating more natural videos that work towards the conversation. The user message will be the last message in the video, and ideally is where the user "requests" the video.

The last message will also be the primary message that the video will be about, and the rest of the conversation is used as context for the video. Here is an example of a good user message, though it does not always have to be this verbose:

```json theme={null}
{
  "role": "user",
  "content": "Create a video explaining chemical bonding, covering ionic, covalent, and metallic bonds with real-world examples."
}
```

Keep in mind that user messages do NOT determine the length of the video. The length is determined by the [`length`](/api-reference/create-video-job#body-length) parameter.
Stating things like "Make a 10-minute video" will not change anything about the video.

#### Using Scripts instead of Messages

The `script` parameter is a more verbose way to create a video, and is more useful for creating detailed videos. It is also cheaper than using the `messages` parameter, as no script is generated.
Scripts are spoken exactly as is, so write them fully, such as `f of x` for `f(x)`, otherwise the text-to-speech model will not understand the special formatting of characters.

You can seperate a script into different sections by using the `-----` separator. An example of this is shown in the [Extra Examples](#extra-examples) section.

### Advanced Options

<AccordionGroup>
  <Accordion title="Web Search within Videos">
    ```python theme={null}
    response = client.video.create(
        # ... other params
        include_sources=True,
        search_for="<specific topic or keywords>",
        source_count=5,  # Number of sources to include
    )
    ```

    Enable automatic web search to enhance your video with reliable sources and citations. These will be returned to you in the final response as well.
  </Accordion>

  <Accordion title="Webhooks">
    ```python theme={null}
    response = client.video.create(
        # ... other params
        webhook_url="https://your-domain.com/video-webhook"
    )
    ```

    Get notified when your video is ready instead of polling the status endpoint. When the video completes (or fails), we will send a `POST` request to the provided webhook URL,
    with the same schema as the status endpoint.
  </Accordion>

  <Accordion title="Storage Options">
    ```python theme={null}
    response = client.video.create(
        # ... other params
        upload_to_s3=True,  # Store in S3 for reliable access
    )
    ```

    If the above is `False`, then we will return a `base64` encoded video in the response. Otherwise, we return the `s3_url` of the video, which is uploaded to our S3 bucket for you.
    These videos are stored permanently for you to access at your conveinece, and will always come back with the following base URL: `https://videofs-opennote-us-east-1.s3.us-east-1.amazonaws.com...`
  </Accordion>
</AccordionGroup>

## Webhooks

When using webhooks, you'll receive an update about your video once it has been completed, with the same schema as the status endpoint, which you can view in the [API Reference](/api-reference/get-video-status).
Keep in mind that even though the status endpoint is usually a `GET` request, the webhook will deliver a `POST` request to your webhook URL.

#### Security

To verify that your webhook is coming from the right location, you can expect the following headers to be returned alongside your video:

```json theme={null}
{
    "Content-Type": "application/json",
    "X-Request-Id": <video_id>,
    "X-Webhook-Platform": "opennote-api",
    "X-Origin": "https://video.api.opennote.com",
    "X-Organization-Id": <your_api_org_id>,
    "X-API-Key": <api_key_used_to_create_the_video>
}
```

A function such as this one below can be used to verify the webhook.

```python theme={null}
def verify_webhook(request):
    required_headers = {
        'X-Webhook-Platform': 'opennote-api',
        'X-Origin': 'https://video.api.opennote.com',
        'X-Organization-Id': 'your_api_org_id',
        'X-API-Key': 'your_api_key'
    }
    
    for header, expected in required_headers.items():
        if request.headers.get(header) != expected:
            return False
    return True
```

## Error Handling

### Common Error Responses

<AccordionGroup>
  <Accordion title="401 Unauthorized">
    ```json theme={null}
    {
      "error": "Invalid API key or unauthorized access",
      "message": "API key is missing or invalid"
    }
    ```

    **Solution**: Verify your API key is correct and has `editable` permissions.
  </Accordion>

  <Accordion title="402 Insufficient Credits">
    ```json theme={null}
    {
      "error": "Insufficient credits", 
      "message": "...",
    }
    ```

    This occurs when your credit balance has dropped below *\$0* and will block future requests.

    **Solution**: Add more credits to your account via the [dashboard](https://opennote.com/api/teams).
  </Accordion>

  <Accordion title="429 Rate Limited">
    You will get a `Retry-After` header in the response, which you can use to wait before making another request.
  </Accordion>

  <Accordion title="500 Processing Error">
    ```json theme={null}
    {
      "error": "Video generation failed",
      "message": "...",
      "video_id": "abc123"
    }
    ```

    **Solution**: Check the content format and try again. Contact support if the issue persists.
  </Accordion>
</AccordionGroup>

## Best Practices

### Creating Effective Videos

* **Be specific**: Include target audience, duration, and key concepts in the `system` prompt.
* **Provide context**: Use system messages to set educational goals
* **Structure content**: Break complex topics into clear sections
* **Include examples**: Mention specific examples or case studies to include
* **Use Web Search**: Enable web search to bring more context to the video model.
* **Literal Scripts**: If you are using the `script` parameter, note that what you write will **be spoken exactly as is**.
  * Math, formulas, etc. will be spoken exactly as you write them, so write them fully, such as `f of x` for `f(x)`.
  * The more literal you are in your script for what you want it to say, the better the video will be.

### Optimizing Performance

* **Use focused prompts**: Specific requests generate faster than broad topics
* **Enable webhooks**: Avoid polling overhead with real-time notifications
* **Cache results**: Store completed videos to avoid regenerating similar content

### Cost Management

* **Preview content**: Use shorter videos with the `length` parameter for testing before creating full-length content
* **Optimize usage**: Fewer sources and no web search means lower costs, as well as no title or script generation.
  * Using the `script` parameter will always be cheaper than using the `messages` parameter, as no script is generated.
  * Using the `title` parameter will be cheaper than not, as no title is generated.
  * Mark `upload_to_s3` as `False` to avoid storage costs.
* **Reuse content**: Leverage existing videos for similar topics
* **Monitor usage**: Track credit consumption in your dashboard

## Example Use Cases

<CardGroup cols={2}>
  <Card title="Course Materials" icon="graduation-cap">
    Create lecture videos from course outlines or textbook chapters for flipped classroom models.
  </Card>

  <Card title="Training Content" icon="briefcase">
    Transform training manuals into engaging video content for employee onboarding and skills development.
  </Card>

  <Card title="Educational Summaries" icon="book">
    Convert research papers or long-form content into digestible video summaries.
  </Card>

  <Card title="Interactive Tutorials" icon="play-circle">
    Build step-by-step tutorial videos from written instructions or documentation.
  </Card>
</CardGroup>

## Support

Need help with video generation?

* **Technical Issues**: [devtools@opennote.me](mailto:devtools@opennote.me)
* **Content Guidelines**: Check our best practices above
* **API Questions**: See our [API Reference](/api-reference/video) for detailed documentation
* **Account Issues**: Visit your [dashboard](https://opennote.com/api/teams) for billing and usage

Ready to create your first video? Try the [API Playground](https://opennote.com/api/teams) or dive into our [SDK documentation](../sdks).

# Extra Examples

These are some extra examples to show different usages of the API.

### Using the `Script` Parameter

This example shows how we used the `-----` seperator to break up a script into different sections for a video.

```python theme={null}
from opennote import OpennoteClient

client = OpennoteClient(api_key="sk_opennote_...")

video_create_response = client.video.create(
    script="""
        Today, we're going to go over scalars and vectors. A Scalar is a quantity that is fully described by a single value, called a Magnitude, which is 
        the size or numerical value of a vector quantity, and has no Direction, i.e. The line or path along which something points or moves. 
        These include such quantities such as mass, time, and speed.
        -----
        A Vector is a quantity that has both Magnitude and Direction. Vectors are used to represent physical quantities that require both of these components 
        for a complete description. Vectors can be seen as an arrow in space, in which its length represents its magnitude and the direction the arrow is pointing is its direction. 
        A vector is also commonly described using the tail to head method, where the direction of the vector originates from the tail and goes towards the head of that vector. 
        These include such quantities such as displacement, velocity, and force. Vectors can be denoted with an arrow on top of the variable that is a vector. 
        A noteworthy example is that speed, which is a scalar, shows the same magnitude as velocity, which is a vector, but that speed has no direction, while velocity does.
        -----
        Sometimes, in a problem with many vectors, the vectors will not be aligned with each other, making the problem more tricky to figure out. 
        In physics, Trigonometry is frequently used with vectors to analyze their Components and break them into different directions. This is especially 
        useful when vectors are not aligned with the Standard Axes, x-axis and y-axis, in a coordinate system. The relationship between vectors and trigonometry comes 
        from the need to break a vector into its components or to calculate the magnitude and direction from its components.
        -----
        Let's say we have a vector "a" that is some angle "theta" above the x-axis. The magnitude of this vector is denoted as |a|. Using trigonometry, the vector's horizontal component 
        can be calculated using |a|cos(theta) and the vertical component can be calculated using |y|cos(theta). Using the Pythagorean Theorem among side lengths in a right triangle, 
        it can be derived that the magnitude of a equals the square root of the x component squared plus the y component squared. To find the angle, trigonometric identities are used, 
        which gives theta equals the inverse tangent of the y component over the x component.
    """, 
    upload_to_s3=True, 
    title="1.1 - Scalars and Vectors"
)


response = client.video.status(video_create_response.video_id)
print(response)
```

Here is the video that was generated with this script above:

<video className="rounded-lg" autoPlay={false} src="https://videofs-opennote-us-east-1.s3.us-east-1.amazonaws.com/e51e7950-d47b-4dcb-bc91-ceb6e568f5ed-2025-08-19T20%3A34%3A26.370106.mp4" controls />

As you can see, verbose scripts are very useful for creating detailed videos.
