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

> ## Agent Instructions
> PostQueen's hosted API is https://api.postqueen.ai. The public API base is https://api.postqueen.ai/public/v1 and takes the raw API key in the Authorization header, with no Bearer prefix.
> The MCP server for posting is https://api.postqueen.ai/mcp/YOUR_API_KEY (or /mcp with Authorization: Bearer YOUR_API_KEY). docs.postqueen.ai/mcp only searches these docs and cannot post.
> The API key is under Connections > API Keys in the app, and only workspace admins can see it.

# Generate a video

> Make a video with AI, saved to the media library and ready to put on a post.

Pick a generator in `type`, a shape in `output`, and send the generator's inputs in `customParams`. The request stays open until the video is ready, which takes minutes, and answers with the stored file.

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X POST "https://api.postqueen.ai/public/v1/generate-video" \
    -H "Authorization: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    --max-time 2400 \
    -d '{
      "type": "seedance",
      "output": "vertical",
      "customParams": {
        "prompt": "Slow push-in on a bowl of spring salad on a wooden table by a sunny window, warm morning light.",
        "images": []
      }
    }'
  ```

  ```js Node.js theme={"system"}
  const res = await fetch("https://api.postqueen.ai/public/v1/generate-video", {
    method: "POST",
    headers: {
      Authorization: process.env.POSTQUEEN_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      type: "seedance",
      output: "vertical",
      customParams: {
        prompt: "Slow push-in on a bowl of spring salad on a wooden table by a sunny window, warm morning light.",
        images: [],
      },
    }),
  });
  const video = await res.json();
  ```
</CodeGroup>

The answer has the same shape as [Upload a file](/public-api/uploads/upload-file), with `status` already `ready`. Put its `id` and `path` in a post's `image` array as they are: there is nothing to poll.

## The generators

| `type`              | What it makes                                                                                                                | `customParams`                                                                                                                                                                                                                                                                                                                       |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `seedance`          | One 8-second clip at 720p, with sound.                                                                                       | `prompt` (string, required): one scene, as a visual brief of the camera, the subject, the motion, the light and the atmosphere, with no on-screen text or logos. `images` (list, required): up to three images from your media library, each as the `id` and `path` an upload returned. Send `[]` for a video from the prompt alone. |
| `image-text-slides` | A narrated slideshow of three to five slides, each with a generated picture, the narration read aloud and shown as captions. | `voice` (string, required): a voice `id` from [Run a video helper](/public-api/videos/function). `prompt` (string, required): what the video should say and show, in plain text.                                                                                                                                                     |

`output` is `vertical` (9:16) for Reels, Stories, TikTok and Shorts, or `horizontal` (16:9) for a feed post. An Image Text Slides video is 1080 × 1920 or 1920 × 1080.

## How long it takes

A video takes a few minutes. Seedance gives up after about 30 minutes and answers `500`. Set your HTTP client's timeout above that. If the connection closes before the answer, the video is still made and saved: find it in the app's media library.

## Credits and the free trial

* Each video uses one of your plan's AI videos for the month. The count starts again on the day of the month your subscription started. How many each plan has is on [Plans and limits](/cloud/plans).
* A video that fails gives its credit back. A request refused with `400`, `402` or `406` uses none.
* During the free trial, `seedance` answers `406`. `image-text-slides` works. See [Free trial](/cloud/trial).

This endpoint has no hourly limit. Your monthly credits are the limit.

## If it fails

| Answer                                                                                                                                           | What to do                                                                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `400` with a `message` list                                                                                                                      | A field is missing or wrong. `type must be any of: ...` lists the generators you can use                                                                                                                           |
| `402` `You have reached the maximum number of generated videos for your subscription. Please upgrade your subscription to generate more videos.` | This month's videos are used up. Wait for the monthly reset, or move to a bigger plan at the `url` in the body. `No credits left this month` means the last credit went to another request made at the same moment |
| `406` `This video is not available in trial mode`                                                                                                | Seedance during the free trial. Use `image-text-slides`, or end the trial on **Billing**                                                                                                                           |
| `422` `Your request was rejected by the AI safety system.`                                                                                       | The provider refused the prompt. The message names the flagged categories when the provider gives them. Change the prompt                                                                                          |
| `500` `AI generation failed, please try again later.`                                                                                            | The generation failed or timed out. The credit was given back. Try again                                                                                                                                           |

<Tip>
  Agents connected over MCP start the same video as a job and poll it, so nothing waits on an open connection: see [`generateVideoTool`](/mcp/tools#generatevideotool).
</Tip>


## OpenAPI

````yaml POST /generate-video
openapi: 3.1.0
info:
  title: PostQueen Public API
  version: 1.0.0
  license:
    name: MIT
    identifier: MIT
  description: >-
    Schedule posts, upload media, read analytics and manage the channels of your
    PostQueen workspace.


    ## Authentication


    Send your API key in the `Authorization` header exactly as it is, with no
    prefix:


    ```

    Authorization: YOUR_API_KEY

    ```


    The key is under **Connections > API Keys** in the app. Only a workspace
    Admin or Super Admin can reveal or rotate it. An app your users sign in to
    with OAuth sends its `pos_` access token in the same header, also with no
    prefix. The workspace needs a subscription, and the free trial counts.


    ## Rate limits


    A workspace can send 30 `POST /posts` requests an hour, and a request
    refused with `400` counts too. `POST /upload` and `POST /upload-from-url`
    allow 300 requests an hour each, per workspace. Nothing else is limited. A
    limited endpoint answers with `X-RateLimit-Limit`, `X-RateLimit-Remaining`
    and `X-RateLimit-Reset` headers, and over the limit with `429` and
    `Retry-After` (seconds).


    ## Terminology


    The app says channel, the API says integration. They are the same thing: a
    connected account on a network.
servers:
  - url: https://api.postqueen.ai/public/v1
    description: PostQueen
security:
  - ApiKeyAuth: []
tags:
  - name: Channels
    description: Connected channels. The API calls them integrations.
  - name: Posts
    description: Create, list, change and delete posts
  - name: Media
    description: Upload files to the media library
  - name: Clipping
    description: >-
      Turn a YouTube video into short vertical clips. Not available on PostQueen
      yet
  - name: AI video
    description: Generate a video with AI into the media library
  - name: Analytics
    description: Channel and post metrics
  - name: Notifications
    description: The workspace's notifications
  - name: OAuth
    description: Sign-in for apps that act for other PostQueen workspaces
paths:
  /generate-video:
    post:
      tags:
        - AI video
      summary: Generate a video
      description: >-
        Makes a video with AI and saves it to the media library. The request
        stays open until the video is ready, which takes minutes, and then
        answers with the stored file, ready to put on a post. Each video uses
        one of the plan's monthly AI video credits; a generation that fails
        gives the credit back. `seedance` is not available during the free
        trial.
      operationId: generateVideo
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GenerateVideoRequest'
            examples:
              seedance:
                summary: A clip from a prompt (seedance)
                value:
                  type: seedance
                  output: vertical
                  customParams:
                    prompt: >-
                      Slow push-in on a bowl of spring salad on a wooden table
                      by a sunny window, steam rising from fresh bread beside
                      it, warm morning light.
                    images: []
              seedance-images:
                summary: A clip that follows your images (seedance)
                value:
                  type: seedance
                  output: horizontal
                  customParams:
                    prompt: >-
                      The camera circles the dish slowly while a hand sprinkles
                      herbs on top.
                    images:
                      - id: YOUR_MEDIA_ID
                        path: https://cdn.example.com/a1b2c3d4e5.png
              slides:
                summary: A narrated slideshow (image-text-slides)
                value:
                  type: image-text-slides
                  output: vertical
                  customParams:
                    voice: YOUR_VOICE_ID
                    prompt: >-
                      Three reasons to try our spring menu: every dish comes in
                      a smaller size, the salads are picked the same morning,
                      and there is a new lemon tart.
      responses:
        '201':
          description: The video, saved in the media library
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaFile'
              example:
                id: c2f1a7d0-5b3e-4f7a-9d61-8e2b4c0a9f13
                name: k3l4m5n6o7.mp4
                originalName: null
                path: https://cdn.example.com/k3l4m5n6o7.mp4
                thumbnail: https://cdn.example.com/p8q9r0s1t2.jpg
                alt: null
                status: ready
        '400':
          description: >-
            The body or `customParams` failed validation. `message` lists every
            problem. An unknown `type` lists the generators you can use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NestError'
              examples:
                type:
                  summary: Unknown generator
                  value:
                    message:
                      - 'type must be any of: seedance, image-text-slides'
                    error: Bad Request
                    statusCode: 400
                params:
                  summary: A missing input
                  value:
                    message:
                      - prompt must be a string
                    error: Bad Request
                    statusCode: 400
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: >-
            The plan's AI videos for this month are used up. Credits come back
            on the day of the month the subscription started. When the last
            credit went to a request made at the same moment, the body is
            `{"statusCode": 402, "message": "No credits left this month"}`
            instead
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/PaymentRequired'
                  - $ref: '#/components/schemas/NestError'
              example:
                statusCode: 402
                message: >-
                  You have reached the maximum number of generated videos for
                  your subscription. Please upgrade your subscription to
                  generate more videos.
                url: https://app.postqueen.ai/billing
        '406':
          description: >-
            `seedance` during the free trial. End the trial, or use
            `image-text-slides`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NestError'
              example:
                statusCode: 406
                message: This video is not available in trial mode
        '422':
          description: >-
            The AI provider's safety system refused the prompt. The message
            names the flagged categories when the provider gives them
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NestError'
              example:
                statusCode: 422
                message: >-
                  Your request was rejected by the AI safety system. Please
                  adjust your prompt and try again.
        '500':
          description: The generation failed or timed out. The credit was given back
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NestError'
              example:
                statusCode: 500
                message: AI generation failed, please try again later.
components:
  schemas:
    GenerateVideoRequest:
      type: object
      required:
        - type
        - output
        - customParams
      properties:
        type:
          type: string
          enum:
            - seedance
            - image-text-slides
          description: >-
            The generator. `seedance` makes an 8-second clip with sound,
            `image-text-slides` a narrated slideshow
        output:
          type: string
          enum:
            - vertical
            - horizontal
          description: >-
            `vertical` (9:16) for Reels, Stories, TikTok and Shorts;
            `horizontal` (16:9) for a feed post
        customParams:
          description: The generator's inputs
          oneOf:
            - $ref: '#/components/schemas/SeedanceParams'
            - $ref: '#/components/schemas/ImageTextSlidesParams'
    MediaFile:
      type: object
      properties:
        id:
          type: string
          description: Unique file ID
        name:
          type: string
          description: Stored file name
        originalName:
          type:
            - string
            - 'null'
          description: The name the file was uploaded under
        path:
          type: string
          description: URL of the stored file. Send it with `id` as a post's media
        thumbnail:
          type:
            - string
            - 'null'
          description: Thumbnail URL, set for video
        alt:
          type:
            - string
            - 'null'
          description: Alt text, when one was set
        status:
          type: string
          enum:
            - ready
            - processing
            - failed
          description: >-
            A video (MP4 or MOV) is normalized in the background and starts as
            `processing`; poll `GET /media/{id}/status` until it is `ready`
            before you put it on a post. Images are `ready` at once.
      example:
        id: e639003b-f727-4a1e-87bd-74a2c48ae41e
        name: a1b2c3d4e5.png
        originalName: launch-banner.png
        path: https://cdn.example.com/a1b2c3d4e5.png
        thumbnail: null
        alt: null
        status: ready
    NestError:
      type: object
      description: >-
        The standard error shape: validation errors, not-found errors and rate
        limits
      properties:
        statusCode:
          type: integer
        message:
          oneOf:
            - type: string
            - type: array
              items:
                type: string
        error:
          type: string
    PaymentRequired:
      type: object
      properties:
        statusCode:
          type: integer
        message:
          type: string
        url:
          type: string
          description: The billing page
    SeedanceParams:
      type: object
      title: seedance
      description: '`customParams` for `seedance`: one 8-second clip at 720p, with sound.'
      required:
        - prompt
        - images
      properties:
        prompt:
          type: string
          description: >-
            One scene of about 8 seconds, as a visual brief: the camera, the
            subject, the motion, the light and the atmosphere. Plain text, with
            no on-screen text or logos
        images:
          type: array
          maxItems: 3
          description: >-
            Up to three reference images from your media library, each as the
            `id` and `path` an upload returned. Send an empty list to make the
            video from the prompt alone
          items:
            type: object
            required:
              - id
              - path
            properties:
              id:
                type: string
                description: The media id
              path:
                type: string
                description: The media path
    ImageTextSlidesParams:
      type: object
      title: image-text-slides
      description: >-
        `customParams` for `image-text-slides`: a narrated slideshow with
        captions.
      required:
        - voice
        - prompt
      properties:
        voice:
          type: string
          description: A voice `id` from the `loadVoices` helper (Run a video helper)
        prompt:
          type: string
          description: >-
            What the video should say and show, as plain text. PostQueen writes
            the narration from it, and each slide gets a generated picture
    MessageError:
      type: object
      description: The error shape of most checks written into the public API itself
      properties:
        msg:
          type: string
  responses:
    Unauthorized:
      description: >-
        `No API Key found`, `Invalid API key`, `Invalid OAuth token`, or `No
        subscription found` when the workspace has no subscription
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MessageError'
          example:
            msg: Invalid API key
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        Your API key from Connections > API Keys, or an OAuth access token
        (`pos_...`). Send it as it is, with no `Bearer` prefix.

````