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

# Render Project

> Render one of your projects again from its editor timeline

## Overview

Every create endpoint renders its project on its own once generation finishes, so you do not need this call to get a first video. Use it to render a project again:

* after editing it in the Hooked editor, or through [Edit Project](/api-reference/project/edit) (captions, music, logo);
* after a render failed (`video.status` is `FAILED`).

The render uses the project's current editor timeline and the aspect ratio set in the editor. It does not generate anything new: no script, voice or clips. The project must have finished generating.

Each call creates a new video and answers its `videoId`. Follow it with [Get Video Details](/api-reference/video/details), or pass a `webhook`: the project's original webhook is not reused, so pass it again if you want a call for this render. [Get Project](/api-reference/project/details) shows the latest video under `video`.

<Info>
  Rendering costs no credits, for every team.
</Info>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.hooked.so/v1/render" \
    -H "x-api-key: your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "projectId": "cm4x9k2p10001ab12cd34ef56",
      "webhook": "https://example.com/hooked/webhook?token=YOUR_SECRET"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.hooked.so/v1/render", {
    method: "POST",
    headers: {
      "x-api-key": process.env.HOOKED_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      projectId: "cm4x9k2p10001ab12cd34ef56",
      webhook: "https://example.com/hooked/webhook?token=YOUR_SECRET",
    }),
  });
  const { data } = await response.json();
  console.log("Render started, video ID:", data.videoId);
  ```

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

  response = requests.post(
      "https://api.hooked.so/v1/render",
      headers={"x-api-key": os.environ["HOOKED_API_KEY"]},
      json={
          "projectId": "cm4x9k2p10001ab12cd34ef56",
          "webhook": "https://example.com/hooked/webhook?token=YOUR_SECRET",
      },
  )
  data = response.json()["data"]
  print("Render started, video ID:", data["videoId"])
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "data": {
      "videoId": "cm4xa1b2c0007ab12mn34op56",
      "projectId": "cm4x9k2p10001ab12cd34ef56",
      "status": "STARTED"
    },
    "message": "Render started successfully"
  }
  ```

  ```json 400 (empty timeline) theme={null}
  {
    "success": false,
    "message": "There is nothing on the timeline to render yet"
  }
  ```

  ```json 400 (missing projectId) theme={null}
  {
    "success": false,
    "message": "Missing projectId"
  }
  ```

  ```json 401 theme={null}
  {
    "code": "not_authenticated",
    "message": "Not authenticated",
    "errorCode": "NOT_AUTHENTICATED",
    "details": { "x-api-key": "Header not provided or API Key invalid" }
  }
  ```

  ```json 403 (foreign files) theme={null}
  {
    "success": false,
    "message": "The project uses files that do not belong to this team"
  }
  ```

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

## Errors

| Status | Message | Cause |
| - | - | - |
| 400 | `Invalid JSON body` | The body is not valid JSON. |
| 400 | `Missing projectId` | `projectId` not sent. |
| 400 | `webhook: Must be a valid HTTPS URL` | The webhook is not a valid URL or points to a private host. |
| 400 | `There is nothing on the timeline to render yet` | The project has not finished generating, or its timeline is empty. Wait for the project to finish. |
| 400 | `Subscription not found` | Your team has no billing record. Contact support. |
| 401 | `code: "not_authenticated"` | Missing or invalid `x-api-key`. |
| 403 | `code: "entitlement_required"` | Your team does not have the Hooked app product. |
| 403 | `The project uses files that do not belong to this team` | The timeline references files of another team. |
| 404 | `Project not found` | The ID does not exist or belongs to another team. |
| 500 | `API Render Error in renderMedia` | The render could not start. Retry later. |

<Warning>
  Each successful call starts a new render. Before retrying after a timeout or a 5xx, check [Get Project](/api-reference/project/details) to see whether a render already started.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Get Project" icon="folder-open" href="/api-reference/project/details">
    Project status and its latest video
  </Card>

  <Card title="Get Video Details" icon="info" href="/api-reference/video/details">
    Follow the render by video ID
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Payloads Hooked sends when a render ends
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /v1/render
openapi: 3.0.0
info:
  title: Hooked API
  version: 1.0.0
  description: AI Video Generation API
servers:
  - url: https://api.hooked.so
security:
  - ApiKeyAuth: []
tags:
  - name: Videos
    description: Create projects, follow them and render videos
  - name: Account Analysis
    description: Analyze TikTok and YouTube accounts
  - name: Channels
    description: Connected social accounts you publish to
  - name: Publish
    description: Schedule videos to your connected accounts
  - name: Images
    description: 'Image editing: background removal and expansion'
  - name: Media
    description: 'Your media library: list, import from a URL, upload a file'
  - name: Avatars
    description: Your team's own avatars (Actors) and their Hook + Demo reactions
paths:
  /v1/render:
    post:
      tags:
        - Videos
      summary: Render Project
      description: >-
        Renders one of your projects again from its editor timeline, at the
        aspect ratio set in the editor. Projects render on their own when
        generation finishes; use this after editing a project or after a failed
        render. Free: renders never cost credits. Each call creates a new video:
        poll GET /v1/video/{videoId} or pass a webhook.
      operationId: renderProject
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - projectId
              properties:
                projectId:
                  type: string
                  description: >-
                    A project of your team (the `projectId` a create endpoint
                    answered). It must have finished generating.
                  example: cm4x9k2p10001ab12cd34ef56
                webhook:
                  type: string
                  format: uri
                  description: >-
                    HTTPS URL on a public host, called when this render
                    completes or fails. The project's original webhook is not
                    reused: pass it again to be called for this render. Requests
                    are signed: verify the Hooked-Signature header (HMAC-SHA256
                    of "<t>.<raw body>") with the signing secret from Settings →
                    Webhooks; Hooked-Event and Hooked-Delivery headers name the
                    event and the delivery. No redirects are followed; 10 s
                    timeout; 3 immediate attempts. See the Webhooks guide for
                    the payload.
            example:
              projectId: cm4x9k2p10001ab12cd34ef56
              webhook: https://example.com/hooked/webhook?token=YOUR_SECRET
      responses:
        '200':
          description: Render started
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      videoId:
                        type: string
                        description: >-
                          The new video. Follow it with GET /v1/video/{videoId};
                          the webhook carries the same ID.
                      projectId:
                        type: string
                        description: The project you rendered.
                      status:
                        type: string
                        enum:
                          - STARTED
                        description: 'Always `STARTED`: the render runs in the background.'
              example:
                success: true
                data:
                  videoId: cm4xa1b2c0007ab12mn34op56
                  projectId: cm4x9k2p10001ab12cd34ef56
                  status: STARTED
                message: Render started successfully
        '400':
          description: >-
            `Missing projectId`, `Invalid JSON body`, `webhook: Must be a valid
            HTTPS URL`, `There is nothing on the timeline to render yet` (the
            project has not finished generating), or `Subscription not found`
            (the team has no billing record)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError400'
              example:
                success: false
                message: There is nothing on the timeline to render yet
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: >-
            The team does not have the product, or the timeline uses files of
            another team
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/EntitlementRequired403'
                  - $ref: '#/components/schemas/ValidationError400'
              example:
                success: false
                message: The project uses files that do not belong to this team
        '404':
          description: >-
            Not found. Another team's resource answers the same way as a missing
            one.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFound404'
              example:
                success: false
                message: Project not found
        '409':
          $ref: '#/components/responses/IdempotencyInProgress'
        '422':
          $ref: '#/components/responses/IdempotencyMismatch'
        '429':
          $ref: '#/components/responses/RateLimited429'
        '500':
          description: The render could not start
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
              example:
                success: false
                message: API Render Error in renderMedia
      security:
        - ApiKeyAuth: []
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >-
        Makes the request safe to retry. 1-255 printable ASCII characters, one
        per operation (your job id, or a UUID you store), reused on every retry.
        Within 24 hours the same key with the same body answers with the first
        response and the header `Idempotent-Replayed: true`, without running or
        charging again. Keys are scoped to your team and the endpoint. 5xx
        answers and refusals before anything ran (401, 402, 403, 409, 429) are
        not kept. See [Idempotency](/guides/idempotency).
      schema:
        type: string
        minLength: 1
        maxLength: 255
        pattern: ^[\x20-\x7E]+$
      example: order-1042-video
  schemas:
    ValidationError400:
      type: object
      description: >-
        The request is invalid. `message` is `<field>: <reason>` when a field is
        to blame.
      properties:
        success:
          type: boolean
          enum:
            - false
        message:
          type: string
      required:
        - success
        - message
      example:
        success: false
        message: 'webhook: Must be a valid HTTPS URL'
    EntitlementRequired403:
      type: object
      description: >-
        Valid key, but the team does not have the product this endpoint belongs
        to.
      properties:
        code:
          type: string
          enum:
            - entitlement_required
        error:
          type: string
          enum:
            - entitlement_required
        message:
          type: string
        product:
          type: string
          example: app
      required:
        - code
        - error
        - message
        - product
      example:
        code: entitlement_required
        error: entitlement_required
        message: This endpoint requires the "app" product.
        product: app
    NotFound404:
      type: object
      description: >-
        Not found. Another team's resource answers the same way as a missing
        one.
      properties:
        success:
          type: boolean
          enum:
            - false
        message:
          type: string
      required:
        - success
        - message
      example:
        success: false
        message: Project not found
    Error500:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
        message:
          type: string
        error:
          type: string
          description: Present on some endpoints
      example:
        success: false
        message: Internal server error
    Error401:
      type: object
      description: Missing or invalid `x-api-key`.
      properties:
        code:
          type: string
          enum:
            - not_authenticated
        message:
          type: string
          example: Not authenticated
        errorCode:
          type: string
          enum:
            - NOT_AUTHENTICATED
        details:
          type: object
          additionalProperties:
            type: string
      example:
        code: not_authenticated
        message: Not authenticated
        errorCode: NOT_AUTHENTICATED
        details:
          x-api-key: Header not provided or API Key invalid
  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error401'
    IdempotencyInProgress:
      description: >-
        A request with the same `Idempotency-Key` is still running. Retry in a
        few seconds.
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                enum:
                  - false
              code:
                type: string
              message:
                type: string
              details:
                type: object
                additionalProperties: true
            required:
              - success
              - code
              - message
          example:
            success: false
            code: idempotency_conflict
            message: A request with this Idempotency-Key is still in progress
            details: {}
    IdempotencyMismatch:
      description: >-
        The `Idempotency-Key` was already used with a different body. Use a new
        key for a new request.
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                enum:
                  - false
              code:
                type: string
              message:
                type: string
              details:
                type: object
                additionalProperties: true
            required:
              - success
              - code
              - message
          example:
            success: false
            code: idempotency_mismatch
            message: Idempotency-Key was used with a different request
            details: {}
    RateLimited429:
      description: >-
        Your team is over a rate limit: 30 POST requests per minute, 120 other
        requests per minute, or 3 downloads of your URLs running at once.
        Nothing ran and nothing was charged: wait `Retry-After` seconds and send
        the request again. See [Rate
        limits](/guides/error-handling#rate-limits-429).
      headers:
        Retry-After:
          description: Seconds to wait before sending again.
          schema:
            type: integer
        X-RateLimit-Limit:
          description: The limit of the bucket you hit (not sent for the download cap).
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: 'Requests left in this window: 0 (not sent for the download cap).'
          schema:
            type: integer
        X-RateLimit-Reset:
          description: >-
            When the window ends, in Unix seconds (not sent for the download
            cap).
          schema:
            type: integer
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                enum:
                  - false
              errorCode:
                type: string
                enum:
                  - RATE_LIMITED
              message:
                type: string
              retryAfterSeconds:
                type: integer
            required:
              - success
              - errorCode
              - message
              - retryAfterSeconds
          example:
            success: false
            errorCode: RATE_LIMITED
            message: >-
              Rate limit exceeded: 30 POST requests per minute per team. Retry
              after 12 s.
            retryAfterSeconds: 12
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.