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 tags: - name: Getting started description: "> **Early Access:** These APIs are still under active development, and may change and evolve over time. We're thankful for your input, and excited to build with you.\n\nThe Descript API lets you programmatically create projects, import media, and edit your projects — all without opening the app.\n\nTo learn more, visit [descript.com/api](https://descript.com/api).\n\n## Create an API token\n\n1. In Descript, open **Settings** and select **API tokens** from the sidebar. Then click **Create token**.\n\n![Navigate to Settings > API tokens and click Create token](assets/token1.png)\n\n2. Give your token a name and select the Drive it should be associated with. Click **Create token**.\n\n\"Name\n\n3. 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.\n\n\"Copy\n\n> **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.\n\nInclude the token as a Bearer token in the `Authorization` header of your API requests.\n\n## Import media into a new project\n\nYou 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.\n\nTo import files, pass in public or pre-signed URIs. Currently, the API does not support uploading a file directly. To test the API with an example file, use the demo video included in the sample request below.\n\nImporting 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.\n\n\n\n**Request**\n\n```bash\ncurl -X POST https://descriptapi.com/v1/jobs/import/project_media \\\n -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"project_name\": \"My First Video\",\n \"add_media\": {\n \"demo.mp4\": {\n \"url\": \"https://test-files.descriptapi.com/demo-video.mp4\"\n }\n },\n \"add_compositions\": [\n {\n \"name\": \"Demo Video\",\n \"clips\": [\n { \"media\": \"demo.mp4\" }\n ]\n }\n ]\n }'\n```\n\n**Response**\n\n```json\n{\n \"job_id\": \"project-media-import-9d635d5b\",\n \"drive_id\": \"c9c5c47e\",\n \"project_id\": \"e2f89ce6\",\n \"project_url\": \"https://web.descript.com/e2f89ce6\"\n}\n```\n\n## Check for import completion\n\nPoll 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.\n\nYou 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.\n\n**Request**\n\n```bash\ncurl https://descriptapi.com/v1/jobs/project-media-import-9d635d5b \\\n -H \"Authorization: Bearer YOUR_API_TOKEN\"\n```\n\n**Response**\n\n```json\n{\n \"job_id\": \"project-media-import-9d635d5b\",\n \"job_type\": \"import/project_media\",\n \"job_state\": \"stopped\",\n \"project_id\": \"e2f89ce6\",\n \"project_url\": \"https://web.descript.com/e2f89ce6\",\n \"result\": {\n \"status\": \"success\",\n \"media_status\": {\n \"main.mp4\": {\n \"status\": \"success\",\n \"duration_seconds\": 69.477006\n }\n },\n \"created_compositions\": [\n { \"id\": \"f8e5088a-4d53-4aab-9d4f-c6624b7d7622\", \"name\": \"Demo Video\" }\n ]\n }\n}\n```\n\n## Prompt for edits with Agent Underlord\n\nOnce your media is imported, you can use the [agent edit endpoint](#operation/agentEditJob) to prompt Underlord for edits, just as you would in the app. Because it's an API, conversation and follow up questions aren't practical. So we recommend framing your edits as a one-shot prompt with all the information the agent needs.\n\nEditing 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.\n\n**Request**\n\n```bash\ncurl -X POST https://descriptapi.com/v1/jobs/agent \\\n -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"project_id\": \"e2f89ce6\",\n \"prompt\": \"Add studio sound and captions\"\n }'\n```\n\n**Response**\n\n```json\n{\n \"job_id\": \"project-agent-edit-e2f89ce6\",\n \"drive_id\": \"c9c5c47e\",\n \"project_id\": \"e2f89ce6\",\n \"project_url\": \"https://web.descript.com/e2f89ce6\"\n}\n```\n\n## Wait for the agent to complete its job\n\nPoll 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.\n\n**Request**\n\n```bash\ncurl https://descriptapi.com/v1/jobs/project-agent-edit-e2f89ce6 \\\n -H \"Authorization: Bearer YOUR_API_TOKEN\"\n```\n\nOnce 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.\n\n**Response**\n\n```json\n{\n\t\"job_id\":\"project-agent-edit-e2f89ce6\",\n\t\"job_type\":\"agent\",\n \"project_id\":\"YOUR_PROJECT_ID\",\n\t\"project_url\":\"https://web.descript.com/e2f89ce6\",\n\t\"job_state\":\"stopped\",\n\t\"created_at\":\"2026-02-09T05:42:27.554Z\",\n\t\"stopped_at\":\"2026-02-09T05:43:15.296Z\",\n\t\"drive_id\":\"1df135a5-dc4a-4dc3-8f7d-681cfbe961e4\",\n\t\"result\":{\n\t\t\"status\":\"success\",\n \"agent_response\":\"Done! I've applied Studio Sound to enhance your audio quality and added classic karaoke-style captions to your video.\",\n\t\t\"project_changed\":true,\n\t\t\"media_seconds_used\":0,\n\t\t\"ai_credits_used\":32\n }\n}\n```\n" - 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/hc/en-us/articles/360043869551-Becoming-a-Supported-Content-Hosting-Partner) 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 `` 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. ``` ``` paths: /jobs/import/project_media: post: tags: - API Endpoints summary: Import media and sequences security: - bearerAuth: [] description: "Import media files into a new or existing project and create compositions.\n\nThis endpoint can:\n- Create a new project if `project_id` is not provided\n- Import media files from URLs\n- Create multitrack sequences\n- Create compositions (timelines) from existing or new media in the project\n- Trigger transcription and other background processing tasks\n\n### Media URL requirements\n- URLs must be accessible by Descript servers\n- URLs must support HTTP Range requests\n- Recommended to sign URLs for 12-48 hours to reduce chance of failure\n- [Supported file types](https://help.descript.com/hc/en-us/articles/10164098416909-Supported-file-types)\n\n### Direct file upload\n\nInstead 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.\n\n### Async Operations\n\nImports\_run in the background and return a `job_id`. Monitor progress via the [GET /jobs/{job_id}](#operation/getJob) endpoint.\n\n### Dynamic webhook\n\nIf `callback_url` is provided, Descript will POST the job status to that URL when the job finishes (successfully or not).\n\nThe payload will match the format returned by [GET /jobs/{job_id}](#operation/getJob).\n" operationId: importProjectMedia requestBody: content: application/json: schema: type: object description: | Request to import media into a project and optionally create compositions. This operation will: - Create a new project if project_id is not provided (using the drive associated with the personal token) - Import media files from URLs or create multitrack sequences - Optionally create one or more compositions - Trigger transcription and other background processing properties: project_id: type: string format: uuid description: | Existing project ID to import media into. If not provided, a new project will be created. When importing into an existing project, media filenames must not conflict with existing files. example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_name: type: string description: Name for the new project. Only used when project_id is not provided. example: Marketing Video team_access: type: string description: | Access level for drive members. Only applicable when creating a new project (when project_id is not provided). Defaults to `none` if not specified. - edit: Users can edit the project - comment: Users can view and comment but not edit - view: Users can view but not comment or edit - none: No shared access (private to owner) enum: - edit - comment - view - none default: none example: edit folder_name: type: string description: | Folder path to place the new project in (e.g. "Clients/Acme/Videos"). Supports nested paths using "/" as separator. Only applicable when creating a new project (when project_id is not provided). Existing folders along the path are reused; missing segments are created automatically. example: Clients/Acme add_media: type: object description: | Map of media reference IDs (display names with optional folder paths) to media import items. Keys are the display names that will appear in the project (e.g., "Misc/intro.mp4" or "demo.mp4"). Values define how to import each media item (URL import or multitrack sequence). additionalProperties: type: object description: | Defines how to import a single media item. Can be either a URL import or a multitrack sequence. oneOf: - type: object title: URL Import description: Import media from a URL required: - url properties: url: type: string format: uri description: | URL to import media from. Must be accessible by Descript servers and support Range requests. Recommended to sign URLs for 12-48 hours to reduce chance of failure. example: https://example.com/intro.mp4 language: type: string description: | ISO 639-1 language code for transcription (e.g., "en", "es", "fr"). If not specified, language is auto-detected from the audio. example: en additionalProperties: false - type: object title: Direct Upload description: | Upload a file directly to Descript. The API returns a signed upload URL in the response. PUT your file to that URL, then the import job will process it automatically. required: - content_type - file_size properties: content_type: type: string description: MIME type of the file (e.g., "video/mp4", "audio/wav") example: video/mp4 file_size: type: integer description: File size in bytes example: 52428800 language: type: string description: | ISO 639-1 language code for transcription (e.g., "en", "es", "fr"). If not specified, language is auto-detected from the audio. example: en additionalProperties: false - type: object title: Multitrack Sequence description: Create a multitrack sequence from multiple media files required: - tracks properties: tracks: type: array description: Array of tracks to combine into a multitrack sequence minItems: 1 items: type: object required: - media properties: media: type: string description: Media reference ID (display name) of the media to include in this track example: Recordings/camera1.mp4 offset: type: number format: float description: Optional time offset in seconds for syncing this track example: 50 default: 0 additionalProperties: false additionalProperties: false example: Misc/intro.mp4: url: https://example.com/intro.mp4 demo.mp4: url: https://example.com/demo.mp4 Multicam_Track: tracks: - media: Recordings/camera1.mp4 offset: 0 - media: Recordings/camera2.mp4 offset: 50 add_compositions: type: array description: Optional list of compositions to create in the project items: type: object description: Defines a composition to create in the project properties: name: type: string description: Name of the composition. If not provided, uses default naming. example: Rough Cut width: type: integer description: Width of the composition in pixels example: 1920 default: 1920 height: type: integer description: Height of the composition in pixels example: 1080 default: 1080 fps: type: number description: | **[Work in progress]** This property is not yet supported and will be ignored if provided. Frame rate for the composition in frames per second. Common values: 24, 25, 29.97, 30, 60. default: 30 example: 30 clips: type: array description: Ordered list of clips to include in the composition items: type: object required: - media properties: media: type: string description: Media reference ID (display name) of the media to add as a clip example: Misc/intro.mp4 additionalProperties: false required: - clips additionalProperties: false callback_url: type: string format: uri description: | Optional webhook URL to call when the job completes or fails. Descript will POST the job status (same format as [GET /jobs/{job_id}](#operation/getJob)) to this URL. example: https://example.com/webhooks/descript/job_callback additionalProperties: false examples: create_with_composition: summary: Create project with initial composition description: Most common use case for seeding a project with a simple composition value: project_name: Marketing Video add_media: Misc/intro.mp4: url: https://example.com/intro.mp4 demo.mp4: url: https://example.com/demo.mp4 Misc/outro.mp4: url: https://example.com/outro.mp4 add_compositions: - name: Rough Cut clips: - media: Misc/intro.mp4 - media: demo.mp4 - media: Misc/outro.mp4 import_only: summary: Import media without composition description: Import files for later composition creation value: project_name: Video Project add_media: Assets/intro.mp4: url: https://example.com/intro.mp4 Assets/logo.png: url: https://example.com/logo.png direct_upload: summary: Direct file upload description: Upload a local file directly instead of providing a URL value: project_name: Upload Project add_media: recording.mp4: content_type: video/mp4 file_size: 52428800 add_compositions: - name: Main clips: - media: recording.mp4 multitrack_sequence: summary: Create multitrack sequence description: Combine multiple tracks with time offsets value: project_name: Interview Edit add_media: Misc/intro.mp4: url: https://example.com/intro.mp4 Recordings/camera1.mp4: url: https://example.com/camera1.mp4 Recordings/camera2.mp4: url: https://example.com/camera2.mp4 Multicam_Track: tracks: - media: Recordings/camera1.mp4 offset: 0 - media: Recordings/camera2.mp4 offset: 50 add_compositions: - name: Rough Cut width: 1920 height: 1080 clips: - media: Misc/intro.mp4 - media: Multicam_Track description: Media import and project creation request required: true responses: '201': description: Import job created successfully content: application/json: schema: type: object description: Response returned when creating an import job properties: job_id: type: string format: uuid description: Unique identifier for the job example: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 drive_id: type: string format: uuid description: Drive ID where the project is located example: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: type: string format: uuid description: Project ID (newly created or existing) example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: type: string format: uri description: URL to access the project in Descript web app example: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb upload_urls: type: object description: | Signed upload URLs for each direct upload media item. Only present when the request includes direct upload references. PUT the file contents to the `upload_url` with `Content-Type: application/octet-stream`. The import job will automatically detect the upload and process the file. additionalProperties: type: object properties: upload_url: type: string format: uri description: Signed URL to PUT the file to. Expires after 3 hours. asset_id: type: string format: uuid description: GAT asset ID for the uploaded file artifact_id: type: string format: uuid description: GAT artifact ID for the uploaded file required: - upload_url - asset_id - artifact_id required: - job_id - drive_id - project_id - project_url '400': description: | Invalid input: - Malformed request body - Invalid media URLs - URLs not accessible or don't support Range requests - Media filename conflicts with existing files (when importing to existing project) content: application/json: schema: $ref: '#/components/schemas/Error400' examples: invalid_url: summary: Invalid media URL value: error: invalid_input message: Invalid media URL format url_not_accessible: summary: URL not accessible value: error: invalid_input message: Media URL is not accessible or does not support Range requests filename_conflict: summary: Filename conflict value: error: invalid_input message: Media filename conflicts with existing files in the project '401': description: Unauthorized - missing or invalid authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' examples: unauthorized: summary: Missing authentication token value: error: unauthorized message: Missing or invalid authentication token '402': description: Payment Required - insufficient media minutes to start the job content: application/json: schema: $ref: '#/components/schemas/Error402' examples: insufficient_minutes: summary: Insufficient media minutes value: error: payment_required message: Insufficient media minutes to start the job (300 minutes required, 50 available) '403': description: Forbidden - user does not have access to this resource content: application/json: schema: $ref: '#/components/schemas/Error403' examples: forbidden: summary: No access to drive or project value: error: forbidden message: User does not have access to this drive or project '404': description: | Not found: - Drive doesn't exist - Project doesn't exist (when project_id is provided) content: application/json: schema: $ref: '#/components/schemas/Error404' examples: project_not_found: summary: Project not found value: error: not_found message: Project not found drive_not_found: summary: Drive not found value: error: not_found message: Drive not found '429': $ref: '#/components/responses/Error429Response' /jobs/agent: post: tags: - API Endpoints summary: Agent edit security: - bearerAuth: [] description: | Use a background agent to create and edit projects using a natural language prompt. - **Edit existing project**: Provide a `project_id` to edit an existing project - **Target a specific composition**: Provide both `project_id` and `composition_id` to direct the agent to a specific composition within the project - **Create new project**: Provide a `project_name` instead of `project_id` to create a new project ### Common use cases - Create new content: "create a 30-second video about cooking tips" - Apply audio effects: "add studio sound to every clip" - Remove filler words: "remove all filler words from the transcript" - Create highlights: "create a 30-second highlight reel" - Content editing: "remove the section from 1:30 to 2:15" ### Async Operations Agent edits 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 completes or fails. The payload will match the format returned by [GET /jobs/{job_id}](#operation/getJob). operationId: agentEditJob requestBody: content: application/json: schema: type: object description: | Request to run Agent edit. The agent will interpret the prompt and either edit an existing project or create a new one. You must provide exactly one of `project_id` or `project_name`. properties: project_id: type: string format: uuid description: | The ID of an existing project to edit. Mutually exclusive with `project_name`. example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_name: type: string description: | Name for creating a new project. Mutually exclusive with `project_id`. example: My New Project composition_id: type: string description: | Composition to target within the project. When provided, the agent will focus its edits on this specific composition rather than choosing one automatically. Only valid when `project_id` is also provided. Requires `project_id`. Accepts any of the following formats: - A full composition UUID (e.g. `39677a40-1c43-4c36-8449-46cfbc4de2b5`) - A 5-character short ID from a Descript URL (e.g. `39677`) - A full Descript project URL (e.g. `https://web.descript.com/{project_id}/39677`) example: 39677a40-1c43-4c36-8449-46cfbc4de2b5 model: type: string description: | AI model to use for editing. Defaults to the default model. prompt: type: string description: | Natural language instruction for the agent to execute. Examples: "add studio sound to every clip", "remove all filler words", "create a 30-second highlight reel" example: add studio sound to every clip team_access: type: string enum: - edit - comment - view - none description: | Access level for team members when creating a new project. Only applicable when `project_name` is provided (not when using `project_id`). Defaults to `none` if not specified. callback_url: type: string format: uri description: | Optional webhook URL to call when the job completes or fails. Descript will POST the job status (same format as [GET /jobs/{job_id}](#operation/getJob)) to this URL. example: https://example.com/webhooks/descript/job_callback required: - prompt additionalProperties: false examples: create_new_project: summary: Create a new project value: project_name: Cooking Tips Video prompt: create a 30-second video about cooking tips with background music add_studio_sound: summary: Add Studio Sound effect value: project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb model: haiku-4.5 prompt: add studio sound to every clip remove_filler_words: summary: Remove filler words value: project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb prompt: remove all filler words create_highlight: summary: Create highlight reel value: project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb composition_id: 39677a40-1c43-4c36-8449-46cfbc4de2b5 prompt: create a 30-second highlight reel with the best moments with_callback: summary: Agent job with webhook callback value: project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb prompt: add studio sound to every clip callback_url: https://example.com/webhooks/descript/job_callback description: AI agent request required: true responses: '201': description: Agent edit job created successfully content: application/json: schema: type: object properties: job_id: type: string format: uuid description: Unique identifier for the Agent edit job example: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 drive_id: type: string format: uuid description: Drive ID where the project is located example: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: type: string format: uuid description: The project ID (existing or newly created) example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: type: string format: uri description: URL to access the project in Descript web app example: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb required: - job_id - drive_id - project_id - project_url '400': description: | Invalid input: - Malformed request body - Invalid project_id or composition_id - Empty or invalid prompt content: application/json: schema: $ref: '#/components/schemas/Error400' examples: empty_prompt: summary: Empty prompt value: error: invalid_input message: Prompt cannot be empty invalid_project: summary: Invalid project ID value: error: invalid_input message: Invalid project_id format '401': description: Unauthorized - missing or invalid authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' examples: unauthorized: summary: Missing authentication token value: error: unauthorized message: Missing or invalid authentication token '402': description: Payment Required - insufficient AI credits to start the job content: application/json: schema: $ref: '#/components/schemas/Error402' examples: insufficient_credits: summary: Insufficient AI credits value: error: payment_required message: Insufficient AI credits to start the job (100 credits required, 25 available) '404': description: | Not found: - Project doesn't exist - Composition doesn't exist (when composition_id is provided) content: application/json: schema: $ref: '#/components/schemas/Error404' examples: project_not_found: summary: Project not found value: error: not_found message: Project not found composition_not_found: summary: Composition not found value: error: not_found message: Composition not found '429': $ref: '#/components/responses/Error429Response' /jobs/publish: post: tags: - API Endpoints summary: Publish project media security: - bearerAuth: [] description: | Publish a project composition to create a shareable link and download the exported file. Publishes a specific composition from a project, rendering the output as video or audio at the specified resolution. When the job completes successfully the result contains both: - `share_url`: a public URL that can be used to view the published content on Descript's share site. - `download_url`: a time-limited signed URL to download the exported media file directly, along with `download_url_expires_at` indicating when the link expires. ### Republishing Publishing the same composition a second time automatically reuses the previous share URL, overwriting its content — so bookmarks and links handed out for the first publish keep working. Republish matching is keyed on `(project_id, composition_id, media_type)`, so a Video publish and an Audio publish of the same composition produce two separate share URLs. ### Async Operations Publish jobs run in the background and return a `job_id`. Monitor progress via the [GET /jobs/{job_id}](#operation/getJob) endpoint, which returns the `share_url`, `download_url`, and `download_url_expires_at` fields once the job finishes. ### Dynamic webhook If `callback_url` is provided, Descript will POST the job status to that URL when the job completes or fails. The payload will match the format returned by [GET /jobs/{job_id}](#operation/getJob). operationId: publishJob requestBody: content: application/json: schema: type: object description: | Request to publish a project composition. properties: project_id: type: string format: uuid description: The ID of the project to publish. example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb composition_id: type: string format: uuid description: The ID of the composition within the project to publish. example: 39677a40-1c43-4c36-8449-46cfbc4de2b5 media_type: type: string enum: - Video - Audio default: Video description: Media type of the published output. resolution: type: string enum: - 480p - 720p - 1080p - 1440p - 4K description: Resolution for the published output. Only applicable when media_type is Video. callback_url: type: string format: uri description: | Optional webhook URL to call when the job completes or fails. Descript will POST the job status (same format as [GET /jobs/{job_id}](#operation/getJob)) to this URL. example: https://example.com/webhooks/descript/job_callback access_level: type: string enum: - public - unlisted - drive - private description: | Desired access level for the published share page. If omitted, the drive's configured default is used. Returns 403 if the requested level is not permitted by the drive's publish settings (e.g. requesting `public` when search engine indexing is disabled). required: - project_id additionalProperties: false examples: publish_video: summary: Publish as 1080p video value: project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb composition_id: 39677a40-1c43-4c36-8449-46cfbc4de2b5 media_type: Video resolution: 1080p publish_audio: summary: Publish as audio only value: project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb composition_id: 39677a40-1c43-4c36-8449-46cfbc4de2b5 media_type: Audio with_callback: summary: Publish with webhook callback value: project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb composition_id: 39677a40-1c43-4c36-8449-46cfbc4de2b5 media_type: Video resolution: 720p callback_url: https://example.com/webhooks/descript/job_callback description: Publish request required: true responses: '201': description: Publish job created successfully content: application/json: schema: type: object properties: job_id: type: string format: uuid description: Unique identifier for the publish job example: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 drive_id: type: string format: uuid description: Drive ID where the project is located example: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: type: string format: uuid description: The project ID being published example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: type: string format: uri description: URL to access the project in Descript web app example: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb required: - job_id - drive_id - project_id - project_url '400': description: | Invalid input: - Malformed request body - Invalid project_id or composition_id format - Invalid media_type or resolution value content: application/json: schema: $ref: '#/components/schemas/Error400' examples: invalid_project: summary: Invalid project ID value: error: invalid_input message: Invalid project_id format invalid_resolution: summary: Invalid resolution value: error: invalid_input message: '"resolution" must be one of [480p, 720p, 1080p, 1440p, 4K]' '401': description: Unauthorized - missing or invalid authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' examples: unauthorized: summary: Missing authentication token value: error: unauthorized message: Missing or invalid authentication token '403': description: | Forbidden: - User doesn't have write access to the project - Project doesn't belong to the token's drive - Requested `access_level` is not permitted by the drive's publish settings content: application/json: schema: $ref: '#/components/schemas/Error403' examples: forbidden: summary: No access to project value: error: forbidden message: Forbidden '422': description: | Unprocessable Entity '429': $ref: '#/components/responses/Error429Response' /jobs: get: tags: - API Endpoints summary: List jobs description: | List recent jobs with optional filtering by project or job type. By default, jobs created within the last 7 days are returned. Use `created_after` and `created_before` to customize the time range. The maximum lookback is 30 days. Results are paginated. Use the `cursor` from the response `pagination.next_cursor` to fetch subsequent pages. Query parameters allow you to filter the results: * Filter by `project_id` to see all jobs for a project * Filter by `type` to see specific job types (import/project_media, agent) operationId: listJobs security: - bearerAuth: [] parameters: - in: query name: project_id description: Filter by project ID required: false schema: type: string format: uuid - in: query name: type description: Filter by job type required: false schema: type: string enum: - import/project_media - agent - in: query name: cursor description: Cursor for the next page of results, obtained from `pagination.next_cursor` in a previous response required: false schema: type: string - in: query name: limit description: Number of items per page (1-100). Defaults to 20. required: false schema: type: integer minimum: 1 maximum: 100 default: 20 - in: query name: created_after description: 'Filter jobs created after this timestamp (ISO 8601). Default: 7 days ago. Oldest allowed: 30 days ago.' required: false schema: type: string format: date-time - in: query name: created_before description: 'Filter jobs created before this timestamp (ISO 8601). Default: now.' required: false schema: type: string format: date-time responses: '200': description: Jobs list retrieved successfully content: application/json: schema: type: object required: - data - pagination properties: data: type: array items: $ref: '#/components/schemas/JobStatus' pagination: type: object properties: next_cursor: type: string description: Cursor for the next page of results. Absent if no more results. examples: mixed_jobs: summary: List of jobs with different types and states value: data: - job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: import/project_media job_state: stopped created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:35:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb result: status: success media_status: Misc/intro.mp4: status: success duration_seconds: 10.5 media_seconds_used: 11 - job_id: a1b2c3d4-5678-90ab-cdef-1234567890ab job_type: agent job_state: running created_at: '2025-11-18T11:00:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb progress: label: Applying Studio Sound to clip 2... last_update_at: '2025-11-18T11:01:00Z' pagination: next_cursor: YTFiMmMzZDQtNTY3OC05MGFiLWNkZWYtMTIzNDU2Nzg5MGFi '401': description: Unauthorized - invalid or missing authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' examples: unauthorized: summary: Missing authentication token value: error: unauthorized message: Missing or invalid authentication token '429': $ref: '#/components/responses/Error429Response' /jobs/{job_id}: get: tags: - API Endpoints summary: Get job status security: - bearerAuth: [] description: | Retrieve the status of any job. The response format varies based on job type and includes type-specific fields. operationId: getJob parameters: - in: path name: job_id description: The job ID required: true schema: type: string format: uuid responses: '200': description: Job status retrieved successfully content: application/json: schema: $ref: '#/components/schemas/JobStatus' examples: import_success: summary: Import job completed successfully value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: import/project_media job_state: stopped created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:35:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb result: status: success media_status: Misc/intro.mp4: status: success duration_seconds: 10.5 demo.mp4: status: success duration_seconds: 125 media_seconds_used: 136 created_compositions: - id: 0171c name: Rough Cut import_partial: summary: Import job partially completed value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: import/project_media job_state: stopped created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:35:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb result: status: partial media_status: Misc/intro.mp4: status: success duration_seconds: 10.5 demo.mp4: status: success duration_seconds: 125 outro.mp4: status: failed error_message: URL is not accessible or does not support Range requests media_seconds_used: 136 created_compositions: - id: 0171c name: Rough Cut agent_success: summary: Agent job completed successfully value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: agent job_state: stopped created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:32:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb result: status: success agent_response: I added Studio Sound to all 3 clips in your composition project_changed: true media_seconds_used: 0 ai_credits_used: 150 job_running: summary: Job still running value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: import/project_media job_state: running created_at: '2025-11-18T10:30:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb agent_running: summary: Agent job running with progress value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: agent job_state: running created_at: '2025-11-18T10:30:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb progress: label: Applying Studio Sound to clip 2 last_update_at: '2025-11-18T10:32:00Z' publish_success: summary: Publish job completed successfully value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: publish job_state: stopped created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:35:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb result: status: success composition_id: 39677a40-1c43-4c36-8449-46cfbc4de2b5 share_url: https://share.descript.com/view/abc123 download_url: https://storage.googleapis.com/bucket/object?X-Goog-Signature=... download_url_expires_at: '2025-11-18T11:35:00Z' publish_running: summary: Publish job running with share URL in progress value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: publish job_state: running created_at: '2025-11-18T10:30:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb progress: label: Rendering video export last_update_at: '2025-11-18T10:32:00Z' composition_id: 39677a40-1c43-4c36-8449-46cfbc4de2b5 share_url: https://share.descript.com/view/abc123 publish_failed: summary: Publish job failed value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: publish job_state: stopped created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:31:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb result: status: error error_message: Export failed during render job_failed: summary: Job failed value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: import/project_media job_state: stopped created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:31:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb result: status: error error_message: Failed to download media file from URL job_cancelled: summary: Job cancelled value: job_id: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: agent job_state: cancelled created_at: '2025-11-18T10:30:00Z' stopped_at: '2025-11-18T10:31:00Z' drive_id: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb '401': description: Unauthorized - invalid or missing authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' examples: unauthorized: summary: Missing authentication token value: error: unauthorized message: Missing or invalid authentication token '403': description: Forbidden - user does not have access to this job content: application/json: schema: $ref: '#/components/schemas/Error403' examples: forbidden: summary: No access to job value: error: forbidden message: User does not have access to this job '404': description: Job not found content: application/json: schema: $ref: '#/components/schemas/Error404' examples: not_found: summary: Job does not exist value: error: not_found message: Job not found '429': $ref: '#/components/responses/Error429Response' delete: tags: - API Endpoints summary: Cancel job description: | Cancel a running job. operationId: cancelJob security: - bearerAuth: [] parameters: - in: path name: job_id description: The job ID required: true schema: type: string format: uuid responses: '204': description: Job cancelled successfully '401': description: Unauthorized - invalid or missing authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' examples: unauthorized: summary: Missing authentication token value: error: unauthorized message: Missing or invalid authentication token '403': description: Forbidden - user does not have access to this job content: application/json: schema: $ref: '#/components/schemas/Error403' examples: forbidden: summary: No access to job value: error: forbidden message: User does not have access to this job '404': description: Job not found content: application/json: schema: $ref: '#/components/schemas/Error404' examples: not_found: summary: Job does not exist value: error: not_found message: Job not found '429': $ref: '#/components/responses/Error429Response' /projects: get: tags: - API Endpoints summary: List projects description: | List projects accessible to the authenticated user within a drive. The drive is determined from the access token. Results are paginated. Use the `cursor` from the response `pagination.next_cursor` to fetch subsequent pages. operationId: listProjects security: - bearerAuth: [] parameters: - in: query name: name description: Filter projects whose name contains this string (case-insensitive). required: false schema: type: string - in: query name: folder_path description: Filter projects by folder path (e.g. "Clients/Acme/Videos"). Use "/" to separate nested folders. Returns only projects directly inside the deepest folder. required: false schema: type: string - in: query name: created_by description: Filter projects created by this user UUID. Pass `me` to filter by the authenticated user. required: false schema: type: string - in: query name: created_after description: Filter projects created after this ISO 8601 timestamp. required: false schema: type: string format: date-time - in: query name: created_before description: Filter projects created before this ISO 8601 timestamp. required: false schema: type: string format: date-time - in: query name: updated_after description: Filter projects updated after this ISO 8601 timestamp. required: false schema: type: string format: date-time - in: query name: updated_before description: Filter projects updated before this ISO 8601 timestamp. required: false schema: type: string format: date-time - in: query name: sort description: Sort field. Defaults to created_at. required: false schema: type: string enum: - name - created_at - updated_at - last_viewed_at default: created_at - in: query name: direction description: Sort direction. Defaults to desc. required: false schema: type: string enum: - asc - desc default: desc - in: query name: cursor description: Pagination cursor from a previous response's `pagination.next_cursor`. required: false schema: type: string - in: query name: limit description: Number of projects per page (1-100). Defaults to 20. required: false schema: type: integer minimum: 1 maximum: 100 default: 20 responses: '200': description: Projects listed successfully content: application/json: schema: type: object required: - data - pagination properties: data: type: array items: type: object required: - id - name - created_at - updated_at properties: id: type: string format: uuid description: Project ID example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb name: type: string description: Project name example: Marketing Video created_at: type: string format: date-time description: When the project was created example: '2025-11-18T10:30:00Z' updated_at: type: string format: date-time description: When the project was last updated example: '2025-11-19T14:00:00Z' pagination: type: object properties: next_cursor: type: string description: Cursor for the next page. Absent if no more results. '401': description: Unauthorized - invalid or missing authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' '429': $ref: '#/components/responses/Error429Response' /projects/{project_id}: get: tags: - API Endpoints summary: Get project details description: | Get a detailed project summary including all media files and compositions. Returns the project's id, name, drive_id, a map of media files (keyed by display path) with type and duration, and a list of compositions with id, name, duration, and media type. Use this to inspect a project's contents before editing or importing media. operationId: getProject security: - bearerAuth: [] parameters: - in: path name: project_id description: The project UUID required: true schema: type: string format: uuid responses: '200': description: Project details retrieved successfully content: application/json: schema: type: object required: - id - name - drive_id - created_at - updated_at - media_files - compositions properties: id: type: string format: uuid description: Project ID name: type: string description: Project name drive_id: type: string format: uuid description: Drive ID the project belongs to created_at: type: string format: date-time description: When the project was created (ISO 8601) updated_at: type: string format: date-time description: When the project was last updated (ISO 8601) media_files: type: object description: Map of display path to media file info additionalProperties: type: object required: - type properties: type: type: string enum: - audio - video - image - sequence - other description: Media type duration: type: number format: float description: Duration in seconds (absent for images) compositions: type: array description: Compositions in the project items: type: object required: - id - name properties: id: type: string format: uuid description: Composition ID name: type: string description: Composition name duration: type: number format: float description: Duration in seconds media_type: type: string description: Media type of the composition '401': description: Unauthorized - invalid or missing authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' '403': description: Forbidden - user does not have access to this project content: application/json: schema: $ref: '#/components/schemas/Error403' '404': description: Project not found content: application/json: schema: $ref: '#/components/schemas/Error404' '429': $ref: '#/components/responses/Error429Response' /status: get: tags: - API Endpoints summary: Check API status security: - bearerAuth: [] description: | > **Work in progress** — This endpoint is not yet available and will return an error if called. Check API availability and validate authentication token. This endpoint can be used to: - Verify that your authentication token is valid - Check API connectivity without performing any heavy operations Returns a success response if the token is valid, or a 401 error if the token is invalid or missing. operationId: getStatus responses: '200': description: Token is valid and API is available content: application/json: schema: type: object properties: status: type: string enum: - ok description: API status indicator example: ok required: - status examples: success: summary: Successful status check value: status: ok '401': description: Unauthorized - missing or invalid authentication token content: application/json: schema: $ref: '#/components/schemas/Error401' examples: unauthorized: summary: Missing authentication token value: error: unauthorized message: Missing or invalid authentication token /published_projects/{publishedProjectSlug}: get: tags: - Export from Descript summary: Get Published Project Metadata description: | Retrieve metadata for a published Descript project by its URL slug. This endpoint provides information about the published project including title, duration, publisher details, privacy settings, and subtitles. This endpoint requires authentication using a personal token and is subject to rate limiting of 1000 requests per hour per user. operationId: getPublishedProjectMetadata security: - bearerAuth: [] parameters: - in: path name: publishedProjectSlug description: The unique URL slug identifying the published project required: true schema: type: string responses: '200': description: Successfully retrieved published project metadata. content: application/json: schema: $ref: '#/components/schemas/PublishedProjectMetadata' '401': description: Unauthorized - invalid or missing authentication token. content: application/json: schema: $ref: '#/components/schemas/PublishedProjectPrivateError' '403': description: Forbidden - user does not have access to this published project. content: application/json: schema: $ref: '#/components/schemas/PublishedProjectForbiddenError' '404': description: Not found - published project does not exist. content: application/json: schema: $ref: '#/components/schemas/PublishedProjectError' '409': description: Conflict - published project is in an invalid state (processing or failed). content: application/json: schema: $ref: '#/components/schemas/PublishedProjectConflictError' '429': $ref: '#/components/responses/Error429Response' /edit_in_descript/schema: post: tags: - Edit in Descript summary: Create Import URL description: | Create an Import URL by sending a Project schema to Descript API from your service's backend. ### Import Schema Our import schemas are specified as a minimal JSON list of files which is detailed in full at the bottom of this section. At it's smallest, the request body looks like: ``` { "partner_drive_id": "162c61d1-6ced-4b25-a622-7dba922983ee", "project_schema": { "schema_version": "1.0.0", "files": [{"uri": "https://descriptusercontent.com/jane.wav?signature=d182bca64bf94a1483d2fd16b579f955"}] } } ``` ### File Access The file paths provided in the schema need to either be public or pre-signed URIs with enough time before expiration for failures and retries, we suggest URIs that won't expire for 48 hours. We ask that the files have already been saved when the import link is generated to minimize cases where we're waiting for eventually consistent storage of files that will never be written. We will, however, wait for eventual consistency of the storage layer and retry fetching files before eventually timing out. Files must be hosted on preapproved hosts as our import process has an allow list which it checks URIs against. Files will be requested with `User-Agent: Descriptbot/1.0` (version may change) for tracking purposes. ### Import link expiration Import links are no longer valid after a user imports their data once. Viewing an already used import link will not allow for importing again and will not provide access to a previously created Descript Project. Partners are able to generate a new import link at any time, regardless of if a previous import link has been used. The API does not currently provide partners with a link to the Descript Project, though users will be redirected to it from Descript's web interface the first time they import files, and can always find the Project in Descript. Import links expire after 3 hours and attempting to use an import link after the pre-signed links in the schema file have expired will result in an error, so we recommend generating the import link after the user has clicked the Edit in Descript button. ### Supported media specification We recommend sending the highest quality, uncompressed versions of files available to you. If you have multiple tracks, we recommend prioritizing sending us the full multi-track sequence over a combined file. * Audio: WAV, FLAC, AAC, MP3 * Video: h264, HEVC (container: MOV, MP4) operationId: postEditInDescriptSchema security: - bearerAuth: [] requestBody: description: Edit in Descript schema POST body. required: true content: application/json: schema: $ref: '#/components/schemas/EditInDescriptSchemaPostBody' responses: '201': description: Successful operation. content: application/json: schema: $ref: '#/components/schemas/EditInDescriptSchemaPostResponse' '400': description: Invalid input. '403': description: Associated drive does not have Edit in Descript permissions. '429': $ref: '#/components/responses/Error429Response' components: securitySchemes: bearerAuth: type: http scheme: bearer description: Personal API token created in Descript Settings → API Tokens. See the Authentication section for details. schemas: Error400: type: object properties: error: type: string example: invalid_input message: type: string example: Invalid request body required: - error - message Error401: type: object properties: error: type: string example: unauthorized message: type: string example: Missing or invalid authentication token required: - error - message Error402: type: object properties: error: type: string example: payment_required message: type: string example: Insufficient media minutes to start the job required: - error - message Error403: type: object properties: error: type: string example: forbidden message: type: string example: User does not have access to this resource required: - error - message Error404: type: object properties: error: type: string example: not_found message: type: string example: Resource not found required: - error - message Error429: description: | Rate limit exceeded response. When this error is returned, the response includes headers to help you implement proper retry logic: - `Retry-After`: Number of seconds to wait before retrying - `X-RateLimit-Remaining`: Requests remaining in current window - `X-RateLimit-Consumed`: Requests consumed in current window type: object properties: error: type: string description: Error code indicating rate limit was exceeded example: rate_limit_exceeded message: type: string description: Human-readable error message example: Too many requests required: - error - message EditInDescriptSchemaPostBody: type: object properties: partner_drive_id: type: string format: uuid description: The drive id associated with the auth token. example: aa3b2d8d-e5ca-41c2-8801-683328293fb9 project_schema: type: object description: The Project schema properties: schema_version: type: string format: string description: Schema version id. example: 1.0.0 source_id: type: string format: string description: External source id to be included in Descript export pages, see Export from Descript section for details. This ID is not currently used to deduplicate data coming into Descript, a new Project is created for each import. example: 06b3f5bb-68a2-468a-a5d9-df155c1e3588 files: type: array description: List of remote files to import into Descript. items: type: object properties: name: type: string format: string description: A name for the file. uri: type: string format: url description: A public/pre-signed uri to the audio or video asset, see supported media specifications section. start_offset: type: object description: The amount of time into the Project's timeline this audio or video file should start at. properties: seconds: type: number description: Float number of seconds into the timeline. example: name: Jane's studio recording uri: https://descriptusercontent.com/jane.wav?signature=d182bca64bf94a1483d2fd16b579f955 start_offset: seconds: 10 required: - uri required: - schema_version - files required: - partner_drive_id - project_schema EditInDescriptSchemaPostResponse: type: object properties: url: type: string format: url example: https://web.descript.com/import?nonce=6e3625ca-f885-460d-b2e3-e3faac79c788 description: A url for partners to redirect their user to in order to start the import process—expires after 3 hours. PublishedProjectMetadata: type: object description: Metadata for a successfully published Descript project properties: download_url: type: string format: uri description: A time-limited signed URL for downloading the original published media file. See download_url_expires_at for expiration date. example: https://storage.googleapis.com/bucket/file.mp4?X-Goog-Signature=... download_url_expires_at: type: string format: date-time description: ISO 8601 timestamp indicating when the download_url expires. Present when download_url is present. example: '2025-01-16T10:30:00.000Z' project_id: type: string format: uuid description: The unique identifier of the source Descript project example: 12345678-1234-5678-1234-567812345678 publish_type: type: string enum: - audio - video - audiogram description: The type of published project example: video privacy: type: string enum: - public - unlisted - private - drive - password description: The access permission level for this published project example: unlisted metadata: type: object description: Detailed metadata about the published project properties: title: type: string description: The title of the published project example: My Video Project duration_seconds: type: number description: Duration of the published content in seconds (rounded to nearest millisecond) example: 125.456 duration_formatted: type: string description: Human-readable duration in HH:MM:SS format example: '00:02:05' published_at: type: string format: date-time description: ISO 8601 timestamp of when the project was published example: '2025-01-15T10:30:00.000Z' published_by: type: object description: Information about the user who published the project properties: first_name: type: string description: First name of the publisher example: Jane last_name: type: string description: Last name of the publisher example: Doe subtitles: type: string description: Full VTT-formatted subtitle/caption content for the published project example: WEBVTT\n\n00:00:00.000 --> 00:00:02.000\nWelcome to my video required: - project_id - publish_type - privacy - metadata - subtitles PublishedProjectError: type: object description: Error response for published project requests properties: error: type: string enum: - not_found - unauthorized - forbidden description: Error type identifier example: not_found message: type: string description: Human-readable error message example: Published project not found required: - error - message PublishedProjectPrivateError: type: object description: Error response when published project is private to drive and user is unauthenticated properties: error: type: string enum: - unauthorized description: Error type identifier example: unauthorized message: type: string description: Human-readable error message example: Private to drive, user must log in to account with access. PublishedProjectForbiddenError: type: object description: Error response when the user does not have access to the published project properties: error: type: string enum: - forbidden description: Error type identifier example: forbidden message: type: string description: Human-readable error message example: User id 00000000-0000-0000-0000-000000000000 does not have access to this project PublishedProjectConflictError: type: object description: Error response when published project is in an invalid state properties: error: type: string enum: - conflict description: Error type identifier example: conflict message: type: string description: Human-readable error message example: Published in invalid state state: type: string enum: - processing - failed description: Current state of the published project example: processing required: - error - message - state ImportSuccessResult: type: object title: ImportSuccessResult description: Result when media was imported (fully or partially) properties: status: type: string enum: - success - partial description: | - success: All media imported successfully - partial: Some media imported successfully, some failed example: success media_status: type: object description: | Status of each media item in the import. Keys are the media reference IDs from the request. additionalProperties: type: object description: Status information for a single media import properties: status: type: string enum: - success - failed description: Status of this individual media import duration_seconds: type: number format: float description: Duration of the imported media in seconds (only present for successful imports) example: 125.5 error_message: type: string description: Error message if the import failed (only present for failed imports) example: URL is not accessible or does not support Range requests required: - status example: Misc/intro.mp4: status: success duration_seconds: 10.5 demo.mp4: status: success duration_seconds: 125 media_seconds_used: type: integer description: Total media seconds consumed by this import example: 75 created_compositions: type: array description: Compositions created during import items: type: object properties: id: type: string example: 0daabc7a-437a-43eb-b04b-b398d502f393 name: type: string example: Rough Cut required: - status - media_status - media_seconds_used ImportErrorResult: type: object title: ImportErrorResult description: Result when job failed completely properties: status: type: string enum: - error description: Job failed completely example: error error_message: type: string description: Human-readable error message example: Failed to import media files error_code: type: string description: Machine-readable error code example: import_failed required: - status - error_message ImportJobStatus: type: object description: Status of an import job properties: job_id: type: string format: uuid description: Unique identifier for the job example: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: type: string enum: - import/project_media description: Type of job job_state: type: string enum: - queued - running - stopped - cancelled description: | Current state of the job: - queued: Job is waiting to start - running: Job is actively processing - stopped: Job has finished (check result.status for outcome) - cancelled: Job was cancelled by user example: stopped created_at: type: string format: date-time description: When the job was created example: '2025-11-18T10:30:00Z' stopped_at: type: string format: date-time description: When the job stopped (only present when job_state is stopped or cancelled) example: '2025-11-18T10:35:00Z' drive_id: type: string format: uuid description: The drive ID example: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: type: string format: uuid description: The project ID example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: type: string format: uri description: URL to access the project in Descript web app example: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb progress: type: object description: Progress information for the workflow (only present when job_state is running) properties: label: type: string description: Human-readable label describing the current action example: Importing media... percent: type: integer description: Percentage of the workflow that has been completed example: 50 last_update_at: type: string format: date-time description: When the progress was last updated example: '2025-11-18T10:32:00Z' required: - label result: description: Job result (only present when job_state is stopped) oneOf: - $ref: '#/components/schemas/ImportSuccessResult' - $ref: '#/components/schemas/ImportErrorResult' discriminator: propertyName: status mapping: success: '#/components/schemas/ImportSuccessResult' partial: '#/components/schemas/ImportSuccessResult' error: '#/components/schemas/ImportErrorResult' required: - job_id - job_type - job_state - created_at - drive_id - project_id - project_url AgentSuccessResult: type: object title: AgentSuccessResult description: Result when Agent edit completed successfully properties: status: type: string enum: - success description: Indicates successful completion example: success agent_response: type: string description: AI response describing what was done example: I added Studio Sound to all 3 clips in your composition project_changed: type: boolean description: Whether the agent made modifications to the project example: true media_seconds_used: type: integer description: Media minutes (in seconds) consumed by this operation example: 34 ai_credits_used: type: integer description: AI credits consumed by this operation example: 5 required: - status - agent_response - project_changed AgentErrorResult: type: object title: AgentErrorResult description: Result when Agent edit failed properties: status: type: string enum: - error description: Indicates the job failed example: error error_message: type: string description: Human-readable error message example: AI agent failed to process the request error_code: type: string description: Machine-readable error code example: agent_execution_failed required: - status - error_message AgentJobStatus: type: object description: Status of an Agent edit job properties: job_id: type: string format: uuid description: Unique identifier for the job example: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: type: string enum: - agent description: Type of job example: agent job_state: type: string enum: - queued - running - stopped - cancelled description: | Current state of the job: - queued: Job is waiting to start - running: Job is actively processing - stopped: Job has finished (check result.status for outcome) - cancelled: Job was cancelled by user example: stopped created_at: type: string format: date-time description: When the job was created example: '2025-11-18T10:30:00Z' stopped_at: type: string format: date-time description: When the job stopped (only present when job_state is stopped or cancelled) example: '2025-11-18T10:35:00Z' drive_id: type: string format: uuid description: The drive ID example: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: type: string format: uuid description: The project ID example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: type: string format: uri description: URL to access the project in Descript web app example: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb progress: type: object description: Progress information for the workflow (only present when job_state is running) properties: label: type: string description: Human-readable label describing the current action example: Applying Studio Sound to clip 2... percent: type: integer description: Percentage of the workflow that has been completed example: 50 last_update_at: type: string format: date-time description: When the progress was last updated example: '2025-11-18T10:32:00Z' required: - label result: description: Job result (only present when job_state is stopped) oneOf: - $ref: '#/components/schemas/AgentSuccessResult' - $ref: '#/components/schemas/AgentErrorResult' discriminator: propertyName: status mapping: success: '#/components/schemas/AgentSuccessResult' error: '#/components/schemas/AgentErrorResult' required: - job_id - job_type - job_state - created_at - drive_id - project_id - project_url PublishSuccessResult: type: object description: Result when publish completed successfully properties: status: type: string enum: - success description: Indicates successful completion composition_id: type: string description: Identifier of the published composition example: 39677a40-1c43-4c36-8449-46cfbc4de2b5 share_url: type: string format: uri description: Public URL to view the published content example: https://share.descript.com/view/abc123 download_url: type: string format: uri description: | Time-limited signed URL to download the original published media file. Present when the job completed successfully and signing succeeded. example: https://storage.googleapis.com/bucket/object?X-Goog-Signature=... download_url_expires_at: type: string format: date-time description: ISO 8601 time when download_url expires (if download_url is set) required: - status - composition_id - share_url PublishErrorResult: type: object description: Result when publish failed properties: status: type: string enum: - error description: Indicates the publish job failed error_message: type: string description: Human-readable error message example: Export failed during render required: - status - error_message PublishJobStatus: type: object description: Status of a publish job properties: job_id: type: string format: uuid description: Unique identifier for the job example: 6dc3f30a-58c2-4174-96a6-dc18cf3c7776 job_type: type: string enum: - publish description: Type of job job_state: type: string enum: - queued - running - stopped - cancelled description: | Current state of the job: - queued: Job is waiting to start - running: Job is actively processing - stopped: Job has finished (check result.status for outcome) - cancelled: Job was cancelled by user example: stopped created_at: type: string format: date-time description: When the job was created example: '2025-11-18T10:30:00Z' stopped_at: type: string format: date-time description: When the job stopped (only present when job_state is stopped or cancelled) example: '2025-11-18T10:35:00Z' drive_id: type: string format: uuid description: The drive ID example: c9c5c47e-158a-49f7-846b-4f6ee2a229a2 project_id: type: string format: uuid description: The project ID example: 9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb project_url: type: string format: uri description: URL to access the project in Descript web app example: https://web.descript.com/9f36ee32-5a2c-47e7-b1a3-94991d3e3ddb progress: type: object description: Progress information for the workflow (only present when job_state is running) properties: label: type: string description: Human-readable label describing the current action example: Exporting composition... percent: type: integer description: Percentage of the workflow that has been completed example: 50 last_update_at: type: string format: date-time description: When the progress was last updated example: '2025-11-18T10:32:00Z' composition_id: type: string description: Composition being published (present when known) example: 39677a40-1c43-4c36-8449-46cfbc4de2b5 share_url: type: string format: uri description: Share URL when available before the job completes example: https://share.descript.com/view/abc123 required: - label result: description: Job result (only present when job_state is stopped) oneOf: - $ref: '#/components/schemas/PublishSuccessResult' - $ref: '#/components/schemas/PublishErrorResult' discriminator: propertyName: status mapping: success: '#/components/schemas/PublishSuccessResult' error: '#/components/schemas/PublishErrorResult' required: - job_id - job_type - job_state - created_at - drive_id - project_id - project_url JobStatus: oneOf: - $ref: '#/components/schemas/ImportJobStatus' - $ref: '#/components/schemas/AgentJobStatus' - $ref: '#/components/schemas/PublishJobStatus' discriminator: propertyName: job_type mapping: import/project_media: '#/components/schemas/ImportJobStatus' agent: '#/components/schemas/AgentJobStatus' publish: '#/components/schemas/PublishJobStatus' description: | Status of an async job. The response structure varies based on the job type. Use the `job_type` field to determine which fields will be present. responses: Error429Response: description: | Too many requests - rate limit exceeded. Use the `Retry-After` header to determine when to retry. headers: Retry-After: description: Number of seconds to wait before retrying the request schema: type: integer example: 30 X-RateLimit-Remaining: description: Number of requests remaining in the current rate limit window schema: type: integer example: 0 X-RateLimit-Consumed: description: Number of requests consumed in the current rate limit window schema: type: integer example: 100 content: application/json: schema: $ref: '#/components/schemas/Error429' examples: rate_limit: summary: Rate limit exceeded value: error: rate_limit_exceeded message: Too many requests. Please try again later. x-tagGroups: - name: Early Access Public API tags: - Getting started - Using the CLI - API Endpoints - Direct file upload - Authentication - Rate Limiting - name: Partner APIs tags: - Edit in Descript - Export from Descript