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

# Import media to drive library

> Import media files into the drive media library (shared workspace-level media collection).

Unlike project media import, this endpoint adds files to the drive's shared media library
rather than to a specific project. Files can optionally be placed in a folder.

### Media URL requirements
- URLs must be accessible by Descript servers
- URLs must support HTTP Range requests
- Recommended to sign URLs for 12-48 hours to reduce chance of failure
- [Supported file types](https://help.descript.com/add-and-manage-media/supported-file-types#importing-with-the-api): the API accepts more containers and codecs than the app, including MXF, MPEG-TS, and AVI

### Direct file upload

Instead of providing a URL, you can upload files directly by specifying `content_type` and `file_size` for a media item. The response will include a signed `upload_url` for each direct upload item. PUT the file bytes to that URL, and the import job will process it automatically. See the [Direct file upload](#tag/Direct-file-upload) guide for a full walkthrough.

### Async Operations

Imports run in the background and return a `job_id`. Monitor progress via the [GET /jobs/{job_id}](#operation/getJob) endpoint.

### Dynamic webhook

If `callback_url` is provided, Descript will POST the job status to that URL when the job finishes (successfully or not).




## OpenAPI

````yaml POST /jobs/import/drive_media
openapi: 3.0.0
info:
  description: Descript API documentation.
  version: '1.2'
  title: Descript API
  x-logo:
    url: assets/descript-logo.svg
    altText: Descript
  license:
    name: Proprietary
    url: https://www.descript.com/terms
servers:
  - url: https://descriptapi.com/v1
security: []
tags:
  - name: Getting started
    description: >
      The Descript API lets you programmatically create projects, import media,
      and edit your projects — all without opening the app.


      To learn more, visit [descript.com/api](https://descript.com/api).


      ## Create an API token


      1. In Descript, open **Settings** and select **API tokens** from the
      sidebar. Then click **Create token**.


      ![Navigate to Settings > API tokens and click Create
      token](assets/token1.png)


      2. Give your token a name and select the Drive it should be associated
      with. Click **Create token**.


      <img src="assets/token2.png" alt="Name your token and select a Drive"
      width="500" />


      3. Copy your token and store it in a safe place. You won't be able to view
      it again. If you lose it, you'll need to generate a new one.


      <img src="assets/token3.png" alt="Copy and save your API token"
      width="500" />


      > **Warning:** Treat your API token like a password. Anyone with your
      token can make API requests on your behalf using your account permissions.
      Never share your token publicly or commit it to source control.


      Include the token as a Bearer token in the `Authorization` header of your
      API requests.


      ## Import media into a new project


      You can create a new project, import media, and place the media into a
      composition all in one API request using the [import
      endpoint](#operation/importProjectMedia). This step also transcribes and
      processes the media so that it's ready for you or the agent to edit.


      To import a file that is already on the internet, pass a public or
      pre-signed URL, as in the sample below. To upload a file from your
      machine, omit `url` and send `content_type` and `file_size` instead. The
      response includes `upload_urls`. PUT the file bytes to the signed URL,
      then poll the job. The [Direct file upload](#tag/Direct-file-upload) guide
      walks through that flow.


      Importing and processing is an asynchronous job, so the response payload
      will contain a `job_id` for you to [query the status of the
      job](#operation/getJob) and information about the newly created project.
      Note that `project_id` and `project_url` are returned immediately
      alongside `job_id`, but opening the project in Descript will not always
      show the API's processing state in real time. To prevent unintended
      changes, we recommend that you do not make any changes to the project
      until the job has stopped.




      **Request**


      ```bash

      curl -X POST https://descriptapi.com/v1/jobs/import/project_media \
        -H "Authorization: Bearer YOUR_API_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "project_name": "My First Video",
          "add_media": {
            "demo.mp4": {
              "url": "https://test-files.descriptapi.com/demo-video.mp4"
            }
          },
          "add_compositions": [
            {
              "name": "Demo Video",
              "clips": [
                { "media": "demo.mp4" }
              ]
            }
          ]
        }'
      ```


      **Response**


      ```json

      {
        "job_id": "project-media-import-9d635d5b",
        "drive_id": "c9c5c47e",
        "project_id": "e2f89ce6",
        "project_url": "https://web.descript.com/e2f89ce6"
      }

      ```


      ## Check for import completion


      Poll the [job status endpoint](#operation/getJob) using the `job_id` from
      the last step to check whether the import job is finished processing. When
      it is, you'll see `job_state: "stopped"`. You can then check the `results`
      object to see its full results.


      You can also pass in a `callback_url` as a part of the first import
      request, and we'll ping you when the job has stopped with the same
      response payload.


      **Request**


      ```bash

      curl https://descriptapi.com/v1/jobs/project-media-import-9d635d5b \
        -H "Authorization: Bearer YOUR_API_TOKEN"
      ```


      **Response**


      ```json

      {
        "job_id": "project-media-import-9d635d5b",
        "job_type": "import/project_media",
        "job_state": "stopped",
        "project_id": "e2f89ce6",
        "project_url": "https://web.descript.com/e2f89ce6",
        "result": {
          "status": "success",
          "media_status": {
            "main.mp4": {
              "status": "success",
              "duration_seconds": 69.477006
            }
          },
          "created_compositions": [
            { "id": "f8e5088a-4d53-4aab-9d4f-c6624b7d7622", "name": "Demo Video" }
          ]
        }
      }

      ```


      ## Prompt for edits with Agent Underlord


      Once your media is imported, use the [agent edit
      endpoint](#operation/agentEditJob) to prompt Underlord. The response
      includes a `conversation_id`. To follow up, call the endpoint again with
      the same `project_id` and that `conversation_id`, plus your next prompt.
      The agent then sees the earlier turns. A single prompt that includes
      everything you need also works.


      Editing can take some time, so the response also returns a `job_id` that
      you can use to [check the status of the job](#operation/getJob). You can
      also pass in a `callback_url` as a part of an agent request, and we'll
      ping you when the job has stopped.


      **Request**


      ```bash

      curl -X POST https://descriptapi.com/v1/jobs/agent \
        -H "Authorization: Bearer YOUR_API_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "project_id": "e2f89ce6",
          "prompt": "Add studio sound and captions"
        }'
      ```


      **Response**


      ```json

      {
        "job_id": "project-agent-edit-e2f89ce6",
        "drive_id": "c9c5c47e",
        "project_id": "e2f89ce6",
        "project_url": "https://web.descript.com/e2f89ce6",
        "conversation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "resolved_model": "auto"
      }

      ```


      **Follow-up request**


      ```bash

      curl -X POST https://descriptapi.com/v1/jobs/agent \
        -H "Authorization: Bearer YOUR_API_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "project_id": "e2f89ce6",
          "conversation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
          "prompt": "Now add captions"
        }'
      ```


      ## Wait for the agent to complete its job


      Poll the [job status endpoint](#operation/getJob) using the `job_id`. When
      the agent job completes successfully, the response includes a summary of
      what it accomplished (see the `result.agent_response` field). Use the
      project URL to open the project in Descript and review Underlord's
      changes.


      **Request**


      ```bash

      curl https://descriptapi.com/v1/jobs/project-agent-edit-e2f89ce6 \
        -H "Authorization: Bearer YOUR_API_TOKEN"
      ```


      Once the agent job is successfully complete, you’ll see a response from
      the agent with a brief summary of what it accomplished. You can then use
      the project url to review its changes directly in Descript.


      **Response**


      ```json

      {
        "job_id": "project-agent-edit-e2f89ce6",
        "job_type": "agent",
        "project_id": "e2f89ce6",
        "project_url": "https://web.descript.com/e2f89ce6",
        "job_state": "stopped",
        "created_at": "2026-02-09T05:42:27.554Z",
        "stopped_at": "2026-02-09T05:43:15.296Z",
        "drive_id": "1df135a5-dc4a-4dc3-8f7d-681cfbe961e4",
        "result": {
          "status": "success",
          "agent_response": "Done! I've applied Studio Sound to enhance your audio quality and added classic karaoke-style captions to your video.",
          "project_changed": true,
          "media_seconds_used": 0,
          "ai_credits_used": 32,
          "conversation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        }
      }

      ```


      If the edit does not complete, the job still reaches `job_state:
      "stopped"` and `result.status` is `"error"`. `result.error_message`
      explains why. A single file inside an import job uses a different value:
      that file's entry in `media_status` has `status` set to `"failed"` when
      the file does not import.
  - name: Using the CLI
    description: >
      The CLI wraps the API into a simple to use command line tool with
      interactive flows for setting up authentication, importing, and prompting
      the agent. It also has built-in polling for job completion.


      ## Requirements


      Before installing the CLI, you'll need **Node.js 24 or higher**. Visit
      [nodejs.org](https://nodejs.org/) to download and install the latest LTS
      version for your operating system.


      ## Install and set up


      First, install the latest version of the CLI using npm.


      ```bash

      npm install -g @descript/platform-cli@latest

      ```


      Next, configure the CLI with your API key.


      ```bash

      descript-api config set api-key

      ```


      ![Setting up the CLI](assets/cli-set-up.gif)



      ## Import media


      Use the `import` command to create a project by passing a project name and
      the link to any media you want to upload, or run `descript-api import` for
      interactive mode. The CLI shows live progress and outputs the project ID
      when done.


      ```bash

      descript-api import \
        --name "My First Project" \
        --media "https://test-files.descriptapi.com/demo-video.mp4"
      ```


      ![Interactive CLI import](assets/cli-import.gif)


      ## Use the agent


      Use the `agent` command to edit a project by passing in the project id and
      an Underlord prompt.


      ```bash

      descript-api agent \
        --project-id YOUR_PROJECT_ID \
        --prompt "Remove filler words and add Studio Sound to all clips"
      ```


      You can also ask Underlord create a new project from a prompt alone by
      writing the script for you!


      ```bash

      descript-api edit --new \
        --prompt "Write a script about how to make great coffee"
      ```


      ## All commands


      Run `descript-api help` to see the full list of available commands and
      options.
  - name: API Endpoints
    description: Import media, edit projects with AI, and query jobs and projects.
  - name: Direct file upload
    description: >
      Instead of providing a public URL, you can upload files directly from your
      local machine using the [import endpoint](#operation/importProjectMedia).
      The flow has three steps: request signed upload URLs, PUT your file bytes,
      then poll for completion.


      ## Step 1 — Request upload URLs


      Call the import endpoint with `content_type` and `file_size` instead of
      `url` for each media item you want to upload directly.


      **Request**


      ```bash

      curl -X POST https://descriptapi.com/v1/jobs/import/project_media \
        -H "Authorization: Bearer YOUR_API_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "project_name": "My Upload Project",
          "add_media": {
            "recording.mp4": {
              "content_type": "video/mp4",
              "file_size": 52428800
            }
          },
          "add_compositions": [
            {
              "name": "Main",
              "clips": [
                { "media": "recording.mp4" }
              ]
            }
          ]
        }'
      ```


      The response includes an `upload_urls` object keyed by media reference ID.
      Each entry contains a signed `upload_url` (valid for 3 hours), plus
      `asset_id` and `artifact_id` for the created asset.


      **Response**


      ```json

      {
        "job_id": "project-media-import-a1b2c3d4",
        "drive_id": "c9c5c47e",
        "project_id": "e2f89ce6",
        "project_url": "https://web.descript.com/e2f89ce6",
        "upload_urls": {
          "recording.mp4": {
            "upload_url": "https://storage.googleapis.com/bucket/...",
            "asset_id": "d4e5f6a7-1234-5678-9abc-def012345678",
            "artifact_id": "a1b2c3d4-5678-9abc-def0-123456789abc"
          }
        }
      }

      ```


      ## Step 2 — Upload the file


      PUT the raw file bytes to the signed URL. Use `Content-Type:
      application/octet-stream`.


      ```bash

      curl -X PUT \
        -H "Content-Type: application/octet-stream" \
        --data-binary @recording.mp4 \
        "https://storage.googleapis.com/bucket/..."
      ```


      The import job detects the upload automatically and begins processing.


      ## Step 3 — Poll for completion


      Check the job status the same way as a URL-based import — poll the [job
      status endpoint](#operation/getJob) with the `job_id`, or provide a
      `callback_url` in the original request.


      ```bash

      curl https://descriptapi.com/v1/jobs/project-media-import-a1b2c3d4 \
        -H "Authorization: Bearer YOUR_API_TOKEN"
      ```


      When the job reaches `job_state: "stopped"`, check `result.status` for
      success or failure.


      ## Mixing URL imports and direct uploads


      You can combine URL-based and direct upload media items in a single
      request. Items with `url` are fetched server-side; items with
      `content_type` and `file_size` return signed upload URLs.


      ```bash

      curl -X POST https://descriptapi.com/v1/jobs/import/project_media \
        -H "Authorization: Bearer YOUR_API_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "project_name": "Mixed Import",
          "add_media": {
            "intro.mp4": {
              "url": "https://example.com/intro.mp4"
            },
            "recording.mp4": {
              "content_type": "video/mp4",
              "file_size": 52428800
            }
          }
        }'
      ```


      The response will include `upload_urls` only for the direct upload items.


      ## Required fields


      | Field | Type | Description |

      |-------|------|-------------|

      | `content_type` | string | MIME type of the file (e.g., `video/mp4`,
      `audio/wav`) |

      | `file_size` | integer | File size in bytes |

      | `language` | string | *(optional)* ISO 639-1 language code for
      transcription. Auto-detected if omitted. |
  - name: Authentication
    description: >
      The Descript API uses personal API tokens to authenticate requests. Tokens
      are scoped to a specific Drive and inherit your permissions on that Drive.


      To create a token, see the [Getting
      Started](#section/Getting-started/Create-an-API-token) guide.


      > **Warning:** Treat your API token like a password. Anyone with your
      token can make API requests on your behalf using your account permissions.
      Never share your token publicly or commit it to source control.


      ## Using your token


      Include the token as a Bearer token in the `Authorization` header of your
      API requests.


      **Example**


      ```bash

      curl -H "Authorization: Bearer YOUR_API_TOKEN"
      https://descriptapi.com/v1/status

      ```
  - name: Rate Limiting
    description: >
      The Descript API implements rate limiting to ensure fair usage and protect
      service availability.

      When you exceed the rate limit, the API returns a `429 Too Many Requests`
      response.


      ## Rate Limit Headers


      When a rate limit is exceeded, the response includes the following
      headers:


      | Header | Description |

      |--------|-------------|

      | `Retry-After` | Number of seconds to wait before retrying the request |

      | `X-RateLimit-Remaining` | Number of requests remaining in the current
      window |

      | `X-RateLimit-Consumed` | Number of requests consumed in the current
      window |


      ## Handling Rate Limits


      When you receive a `429` response, use the `Retry-After` header to
      determine how long to wait before retrying.

      This approach is more efficient than using fixed delays or exponential
      backoff alone.
  - name: Edit in Descript
    description: >
      > **Note:** The Edit in Descript integration requires contacting Descript
      for access. [Reach out to us](https://descript.com/api) to get started.


      Edit in Descript API enables partners to give their users the ability to
      transfer audio or video content to Descript for editing.


      Edit in Descript buttons work by generating one-time use, public Import
      URLs to the Descript import UI that users

      can be automatically sent to. On that page, they can make a few simple
      selections before kicking off a Partner cloud

      storage to Descript cloud storage transfer. This will redirect them to a
      Descript Project ready for editing.


      Partners can initiate the request by securely sending an information
      schema backend-to-backend to the Descript API

      using a token, in exchange for the Import URL to redirect the user.
      Partners do not need to store this schema, as

      Descript will do so and use it to start fetching the files when the user
      confirms the action


      1. When a user clicks `Edit in Descript`, partner's backend service makes
      POST request to:
          `https://descriptapi.com/v1/edit_in_descript/schema` with an authorization bearer token header and JSON schema body
      2. Descript responds with either an Import URL or an error

      3. Partner redirects the user's browser to the URL returned in step 2 or
      display an error message and link to help documentation


      ### Partner User Experience

      Some guidelines for partners as you consider this integration:

      * We recommend placing the `Edit in Descript` option next to your download
      options
        * If you offer multiple download options, such as combined vs. split audio/video files, we recommend placing
      this integration clearly in context with each option, or only the
      supported option, to help users understand

      what will be exported.
        * Each time you request an import link, a new one is generated. Import links expire after 3 hours. After using an
      import link, the only way to find an imported Project again is in
      Descript.
        * If an import link has expired or the contents of the schema has changed, please request a new import link with
      the updated schema. This will create a new Descript Project when used.

      * We will provide Descript-branded assets to fit your proposed placement
      of the `Edit in Descript` CTA and ask

      that you don't edit the assets beyond what we provide. We are happy to
      work with you on getting you the right

      assets for your placement.

      * Partners should provide error-handling for the POST request, at minimum
      displaying a generic error message and

      linking to a help article (we can provide a link for this if you prefer).

      * Progress will be conveyed to the user in the Descript side of the user
      experience.


      ### Descript User Experience

      When users are directed to a Descript Import URL, they'll be asked to
      either create an account or login in order

      to proceed.


      Next, they will be presented with a few options about how they'd like to
      import the data, such as where the new

      Descript Project should be created.


      They'll then be redirected to the Project, where they can monitor the
      progress of the import and start editing.
  - name: Export from Descript
    description: >
      Users of Descript currently have three options to export their edited
      content. They can export files in various

      formats, share a Descript link, or use our [one-click cloud
      export](https://help.descript.com/connect-with-us/hosting-partners)

      to publish directly to a partner.


      ### Roundtrip Metadata

      If Project data previously came from a partner via an Edit in Descript
      schema then any Descript Export pages

      will include `<meta/>` tags which contains the `partner_drive_id` and
      `source_id` provided when originally

      importing into Descript. This allows partners to deduplicate data
      returning back to partner systems after

      editing in Descript. Both partner and source properties are included on
      all public Descript Export pages.


      ```

      <meta property="descript:partner"
      content="9121bf3a-60fe-4a31-ad59-ed32df610cc3" />

      <meta property="descript:source"
      content="409148bd-81aa-4af6-a9c3-29b761506f3a" />

      ```
paths:
  /jobs/import/drive_media:
    post:
      tags:
        - API Endpoints
      summary: Import media to drive library
      description: >
        Import media files into the drive media library (shared workspace-level
        media collection).


        Unlike project media import, this endpoint adds files to the drive's
        shared media library

        rather than to a specific project. Files can optionally be placed in a
        folder.


        ### Media URL requirements

        - URLs must be accessible by Descript servers

        - URLs must support HTTP Range requests

        - Recommended to sign URLs for 12-48 hours to reduce chance of failure

        - [Supported file
        types](https://help.descript.com/add-and-manage-media/supported-file-types#importing-with-the-api):
        the API accepts more containers and codecs than the app, including MXF,
        MPEG-TS, and AVI


        ### Direct file upload


        Instead of providing a URL, you can upload files directly by specifying
        `content_type` and `file_size` for a media item. The response will
        include a signed `upload_url` for each direct upload item. PUT the file
        bytes to that URL, and the import job will process it automatically. See
        the [Direct file upload](#tag/Direct-file-upload) guide for a full
        walkthrough.


        ### Async Operations


        Imports run in the background and return a `job_id`. Monitor progress
        via the [GET /jobs/{job_id}](#operation/getJob) endpoint.


        ### Dynamic webhook


        If `callback_url` is provided, Descript will POST the job status to that
        URL when the job finishes (successfully or not).
      operationId: importDriveMedia
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DriveMediaImportRequest'
        required: true
      responses:
        '201':
          description: Job created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DriveMediaImportResponse'
        '400':
          description: Invalid request
        '401':
          description: Unauthorized
        '402':
          description: Insufficient media minutes
        '403':
          description: Forbidden
        '404':
          description: Folder not found
        '429':
          description: Too many requests
      security:
        - bearerAuth: []
components:
  schemas:
    DriveMediaImportRequest:
      type: object
      required:
        - add_media
      properties:
        folder_id:
          type: string
          description: >-
            Target drive media folder ID (UUID) or "root" for the root folder.
            If not provided, defaults to root.
        add_media:
          type: object
          description: >
            Map of media reference IDs to their import definitions. Key is the
            display name

            or path (e.g. "video.mp4" or "Folder/SubFolder/video.mp4"). Paths
            with forward

            slashes create nested folders automatically.
          minProperties: 1
          maxProperties: 100
          additionalProperties:
            oneOf:
              - $ref: '#/components/schemas/MediaUrlReference'
              - $ref: '#/components/schemas/MediaUploadReference'
        callback_url:
          type: string
          format: uri
          description: >-
            Optional callback URL to receive notifications about job status
            changes.
    DriveMediaImportResponse:
      type: object
      required:
        - job_id
        - drive_id
        - folder_id
      properties:
        job_id:
          type: string
          description: The ID of the created import job. Use this to query job status.
        drive_id:
          type: string
          description: The drive ID where the media is being imported.
        drive_name:
          type: string
          nullable: true
          description: Human-readable name of the connected drive (workspace)
          example: My Team Workspace
        folder_id:
          type: string
          description: The target folder ID in the drive media library.
        upload_urls:
          type: object
          description: >-
            Signed upload URLs for direct upload media items. Only present when
            the request includes direct upload references.
          additionalProperties:
            type: object
            properties:
              upload_url:
                type: string
                format: uri
                description: Signed URL to PUT the file bytes to.
              asset_id:
                type: string
                description: The asset ID for the uploaded file.
              artifact_id:
                type: string
                description: The artifact ID for the uploaded file.
    MediaUrlReference:
      type: object
      required:
        - url
      properties:
        url:
          type: string
          format: uri
          description: >-
            URL to download the media file from. Recommended signed for 12+
            hours.
        language:
          type: string
          description: ISO 639-1 language code for transcription (e.g., "en", "es").
    MediaUploadReference:
      type: object
      required:
        - content_type
        - file_size
      properties:
        content_type:
          type: string
          description: MIME type of the file to upload (e.g. "video/mp4").
        file_size:
          type: integer
          minimum: 1
          description: File size in bytes.
        language:
          type: string
          description: ISO 639-1 language code for transcription (e.g., "en", "es").
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Personal API token created in Descript Settings → API Tokens. See the
        Authentication section for details.

````

## Related topics

- [Import files from Google Drive](https://help.descript.com/add-and-manage-media/google-drive.md)
- [Import media library files to your project](https://help.descript.com/add-and-manage-media/import-media-library.md)
- [Import Zoom recordings into Descript](https://help.descript.com/add-and-manage-media/zoom.md)
- [Track and understand your media minutes and AI credits](https://help.descript.com/billing-payments-plans/track-and-understand-your-media-minutes-and-ai-credits.md)
- [Descript MCP overview](https://help.descript.com/api-and-mcp/mcp.md)
