# Pitch developer documentation > Pitch turns a product URL into a finished video. An AI agent opens a real browser, walks through > the product, writes the script, records the screen, and renders an MP4. The same studio builds > launch films and slide decks, and cleans up screen recordings you already have. > > There are two ways in: an MCP server for AI agents, and a REST API for your own code. Both take > the same API key and spend from the same credit balance. > > This file is the full developer documentation in one place. The same content, paginated, is at > https://trypitch.co/docs Base URL: https://api.trypitch.co MCP endpoint: https://api.trypitch.co/mcp MCP registry: co.trypitch/pitch API keys: https://trypitch.co/api-keys OpenAPI spec: https://trypitch.co/openapi.json Support: support@trypitch.co ================================================================================ OVERVIEW ================================================================================ Two surfaces, one key. MCP REST Endpoint POST /mcp /v1/* Use it when an AI agent decides what to make your code already knows Shape JSON-RPC tool calls plain HTTP and JSON Set up in Claude, Cursor, ChatGPT, Perplexity curl, any HTTP client What you can make Flow What it is Credits demo-video the agent drives your live product and narrates 3 launch-video a scripted, scored, cinematic film 6 to 13 deck a slide deck written and designed from a topic 1 deck + upload a redesign of a PDF or PPTX you upload 2 recording-edit a cut of a screen recording you already made 2 How a project runs Every output is a project: a workspace, an agent, and a conversation. You create it with a first prompt, the agent starts working right away, and creation returns with an id. The work takes minutes, not milliseconds, so you poll for the result. Once it is ready you can keep talking to the same agent to change things, for free. create -> working -> ready <-> working (each follow-up prompt) \ -> failed Credits come off the balance when the project is created, not when it finishes. If the first turn ends with nothing usable, we refund it. Every prompt after the first is free. ================================================================================ GETTING STARTED ================================================================================ 1. Create an API key Go to https://trypitch.co/api-keys and press Create key. Pitch keys start with pk_. The full key is shown once. We store only its SHA-256 hash, so we cannot show it to you again or recover it. Copy it somewhere safe. If you lose it, revoke it and make a new one. The key acts as the user who made it. Projects it creates belong to that user, show up in their app, and spend that user's credits. 2. Check your credits curl https://api.trypitch.co/v1/credits \ -H "Authorization: Bearer pk_your_key" { "balance": 42, "plan": "pro", "transactions": [ ... ] } A demo video costs 3 credits. If the balance is short, project creation fails with 402 insufficient_credits and nothing is charged. 3. Create a project Pick a flow and write the first prompt the way you would in the app: the URL and what you want. Instructions are free text. You do not need a script. curl -X POST https://api.trypitch.co/v1/projects \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d '{ "flow": "demo-video", "prompt": "https://trypitch.co — walk through sign-up and the first render. Keep it under 60 seconds." }' It answers 202 Accepted as soon as the agent has started. { "id": "cm4x8k2p90001abcd", "flow": "demo-video", "title": "trypitch.co", "status": "working", "busy": true, "prompt": "https://trypitch.co — walk through sign-up and the first render. Keep it under 60 seconds.", "options": {}, "outputs": [], "thumbnailUrl": null, "shareUrl": null, "error": null, "createdAt": "2026-09-02T10:14:03.221Z", "updatedAt": "2026-09-02T10:14:03.221Z" } 4. Poll for the result Ask for the project every few seconds until status stops being working. Ten seconds is a good interval. Most videos land in a few minutes. curl https://api.trypitch.co/v1/projects/cm4x8k2p90001abcd \ -H "Authorization: Bearer pk_your_key" { "id": "cm4x8k2p90001abcd", "flow": "demo-video", "title": "trypitch.co", "status": "ready", "busy": false, "outputs": [ { "kind": "video", "url": "https://s3.trypitch.co/pitch/.../final.mp4", "createdAt": "2026-09-02T10:19:41.010Z" } ], "thumbnailUrl": "https://s3.trypitch.co/pitch/.../thumb.jpg", "shareUrl": null, "error": null, "scenes": [ ... ] } Download the video entry in outputs and you are done. 5. Change something (optional) The project is a conversation. Send another message and the same agent edits what it made. This is free, as many times as you like. curl -X POST https://api.trypitch.co/v1/projects/cm4x8k2p90001abcd/prompt \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d '{ "text": "Cut the intro shorter and slow the narration down a little." }' status goes back to working. Poll again, and the newest entry in outputs is the new cut. The same thing in Node const KEY = process.env.PITCH_API_KEY const api = (path, init) => fetch("https://api.trypitch.co" + path, { ...init, headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json", ...init?.headers, }, }).then(async r => { const body = await r.json() if (!r.ok) throw new Error(body.error?.message ?? r.statusText) return body }) const project = await api("/v1/projects", { method: "POST", body: JSON.stringify({ flow: "demo-video", prompt: "https://trypitch.co — walk through sign-up. Under 60 seconds.", }), }) let state = project while (state.status === "working") { await new Promise(r => setTimeout(r, 10_000)) state = await api(`/v1/projects/${project.id}`) } if (state.status !== "ready") throw new Error(state.error ?? state.status) console.log(state.outputs.find(o => o.kind === "video")?.url) ================================================================================ AI AGENTS (MCP) ================================================================================ The server speaks the Model Context Protocol over Streamable HTTP. It is listed on the official MCP registry as co.trypitch/pitch. Endpoint POST https://api.trypitch.co/mcp Transport Streamable HTTP, stateless Auth Authorization: Bearer pk_... Registry name co.trypitch/pitch Stateless means every request stands alone. There is no session id and no server-sent stream to hold open. GET and DELETE on /mcp return 405 Method Not Allowed on purpose. Claude claude mcp add --transport http pitch \ https://api.trypitch.co/mcp \ --header "Authorization: Bearer pk_your_key" In claude.ai, go to Settings, then Connectors, then Add custom connector, and paste the endpoint with the same header. Cursor Open Settings, then MCP, then New server. Or edit .cursor/mcp.json directly. { "mcpServers": { "pitch": { "url": "https://api.trypitch.co/mcp", "headers": { "Authorization": "Bearer pk_your_key" } } } } ChatGPT Settings, then Connectors, then Advanced, then Developer mode, then Add. Paste the endpoint and add a custom header. URL https://api.trypitch.co/mcp Header Authorization: Bearer pk_your_key The same connector works from the Responses and Agents APIs. Perplexity Settings, then Connectors, then Add connector, then Custom (MCP). Paste the endpoint and set the auth header. The same setup works in Comet and the Perplexity API. Any other client If a client speaks Streamable HTTP and lets you set a header, it works. curl -X POST https://api.trypitch.co/mcp \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }' Most clients need the Accept header to include both application/json and text/event-stream. The MCP SDK sets this for you. What the agent can do Six tools. One spends credits and starts a project, one talks to a project for free, four are free reads. create_project 1 to 13, by flow prompt_project free get_project free list_projects free get_credits free get_pricing free Telling the agent how to behave Agents tend to call create_project and then stop, because the project is not finished yet. Put something like this in your system prompt: When you use Pitch: - Call get_credits before create_project so you know the project can pay for itself. - create_project returns a project with status "working". The video is not ready at that point. - Poll get_project every 10 seconds until status is "ready" or "failed". - On "ready", give the user the url of the newest entry in outputs (kind "video" or "pdf"). - On "failed", read the error field and say what went wrong. - To change something, call prompt_project on the same project. It is free. Never create a second project for an edit. - Never call create_project twice for the same request. That last line matters. create_project spends credits at once, so a retried call is a second charge, not a resumed project. Edits go through prompt_project, which is free. ================================================================================ AUTHENTICATION ================================================================================ Getting a key Sign in and open https://trypitch.co/api-keys. Press Create key, give it a name, and copy the value. Keys look like pk_TSAqoriIlkd0uywKpOkD9gjiE_eShH7v. We store a SHA-256 hash of the key and the first 12 characters, nothing else. That prefix is what you see in the list, so you can tell keys apart without us keeping the secret. Sending it Use a bearer token. This is the form to prefer everywhere. Authorization: Bearer pk_your_key Some MCP clients only let you set a plain header with a plain value. For those, this also works: X-API-Key: pk_your_key The two are equivalent. If both are present, Authorization wins. What a key can do A key acts as the user who created it. There are no scopes and no per-key limits yet. Anything that user can do in the app, the key can do through the API, and every credit it spends lands in that user's ledger next to their browser usage. Projects created with a key appear in that user's app, and vice versa. A brand-new account has to finish onboarding in the app once before a key can create projects. Until then creation returns 428 onboarding_required. Revoking Press Revoke on the keys page. It takes effect on the next request. Revoked keys stay in the list so you can see what was in use and when it was last used. Projects already working under a revoked key keep working. Revoking stops new requests, it does not cancel work in flight. Keeping keys safe - Put keys in environment variables, never in source control. - Never ship a key to a browser or a mobile app. Call Pitch from your server. - Use one key per system, so you can revoke one without breaking the rest. - Rotate by making the new key first, switching over, then revoking the old one. When auth fails A missing, unknown, or revoked key returns 401 on both surfaces. { "error": { "code": "unauthorized", "message": "Invalid or revoked API key" } } There is no way to tell those three cases apart from the response, on purpose. ================================================================================ CREDITS ================================================================================ What things cost Both surfaces create projects the same way: POST /v1/projects over REST, create_project over MCP. The flow sets the price. Flow What it makes Credits demo-video narrated demo of your live product 3 launch-video cinematic launch film 6 to 13 deck slide deck from a topic 1 deck with an upload and options.mode redesign of a PDF or PPTX 2 recording-edit cut of a screen recording 2 POST /v1/projects/:id/prompt any follow-up: edits, re-renders free prompt_project GET /v1/*, get_*, list_* any read free Launch video pricing Launch videos are the one variable price. The resolution sets the base, and narration adds one credit on top, because a narrated film needs a script and a voiceover clip per scene. Resolution Narrated (default) Music only 720p 6 5 1080p (default) 9 8 4K 13 12 If you send no options.resolution, you get 1080p and pay 9. An unrecognised value also falls back to 1080p rather than the cheapest tier, so a typo can never underpay. Exporting at a higher resolution later, from the app, charges only the difference. # a 6-credit launch video instead of the 9-credit default curl -X POST https://api.trypitch.co/v1/projects \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d '{ "flow": "launch-video", "prompt": "https://acme.com — 60 second launch film for an AI notetaker. Confident, fast.", "options": { "resolution": "720p" } }' Reading the balance curl https://api.trypitch.co/v1/credits -H "Authorization: Bearer pk_your_key" { "balance": 42, "plan": "pro", "transactions": [ { "amount": -3, "reason": "Demo video (trypitch.co)", "projectId": "cm4x...", "createdAt": "..." }, { "amount": 50, "reason": "Subscription renewal", "createdAt": "..." } ] } The balance is the sum of the ledger, not a stored counter. transactions returns the 20 most recent entries, newest first. Negative amounts are spend, positive are top-ups and refunds. Checking a price first curl https://api.trypitch.co/v1/pricing { "flows": [ { "id": "launch-video", "title": "Launch video", "credits": 9 }, { "id": "demo-video", "title": "Demo video", "credits": 3 }, { "id": "deck", "title": "Slide deck", "credits": 1 }, { "id": "recording-edit", "title": "Recording edit", "credits": 2 } ], "launchVideo": { "tiers": [ { "res": "720p", "credits": 5, "narrated": 6 }, { "res": "1080p", "credits": 8, "narrated": 9 }, { "res": "4k", "credits": 12, "narrated": 13 } ] }, "deck": { "generate": 1, "enhance": 2 }, "edits": "Every prompt after the first is free." } This endpoint needs no key. Over MCP the same payload comes back from get_pricing. When you run out We check the balance before creating anything. If it is short, the request fails with 402, no project is made, and nothing is charged. { "error": { "code": "insufficient_credits", "message": "Not enough credits. Balance is 2. Buy more at https://trypitch.co/pricing.", "balance": 2 } } Over MCP the same case comes back as a tool error whose text carries the balance. Refunds Credits come off as soon as the project row is written. If the agent's first turn ends with nothing usable, whether it failed, was stopped, or finished empty, we mark the project failed and put the exact amount back, so a bad first run never silently eats credits. Follow-up prompts are free, so there is nothing to refund on them. Email support@trypitch.co if a project failed for a reason on our side and was not refunded. Credits never expire. ================================================================================ FILES AND UPLOADS ================================================================================ Three flows take files you already have. Send them base64-encoded in the JSON body, in an uploads array of { fileBase64, fileName } objects. There is no separate upload step and no multipart form. This keeps MCP and REST identical, since an MCP tool call cannot carry a multipart body. Limits Flow What the file is Max size Accepted deck one deck to redesign; set options.mode for an enhance 50 MB .pdf .pptx recording-edit the recording to cut (required) 500 MB .mp4 .webm .mov .mkv .avi demo-video optional assets the agent turns into a slideshow 50 MB each .pdf .png .jpg .jpeg .webp Sizes are measured after decoding. Base64 is about a third larger than the raw file, so a 500 MB video is roughly a 667 MB request body. The type is read from the extension in fileName, not from the bytes. Sending a file curl -X POST https://api.trypitch.co/v1/projects \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg f "$(base64 -w0 deck.pdf)" \ '{ flow: "deck", prompt: "Modernise it, keep the story", options: { mode: "recreate" }, uploads: [{ fileBase64: $f, fileName: "deck.pdf" }] }')" In Node: import { readFile } from "node:fs/promises" const file = await readFile("recording.mp4") await fetch("https://api.trypitch.co/v1/projects", { method: "POST", headers: { Authorization: `Bearer ${process.env.PITCH_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ flow: "recording-edit", prompt: "Cut the dead air at the start. Add intro and outro cards.", options: { productName: "Acme", productUrl: "https://acme.com" }, uploads: [{ fileBase64: file.toString("base64"), fileName: "recording.mp4" }], }), }) When you send an upload, prompt may be empty. Otherwise it is required. What happens to your file We decode it to a temp file, check the extension and size, upload it to object storage, and delete the temp copy. The stored copy is what the agent reads into the project workspace. Output files live in the same bucket and are served over HTTPS. Assets for a demo video For demo-video, uploads are optional extras: PDFs or images the agent prepares into a slideshow and cuts between the live browser and the slides. Mention in the prompt what they are for. { "flow": "demo-video", "prompt": "https://trypitch.co — open with the pricing one-pager, then show sign-up.", "uploads": [{ "fileBase64": "...", "fileName": "pricing.pdf" }] } Errors 413 payload_too_large the decoded file is over the limit for that flow 415 unsupported_media_type the extension is not in the accepted list for that flow 400 invalid_request the base64 decoded to nothing ================================================================================ POLLING PROJECTS ================================================================================ Statuses status is derived from what the agent is doing and what is in the workspace, never stored, so it is always current. working the agent has a turn in flight: the first build, or a follow-up keep polling ready there is something to show; outputs holds the deliverables stop failed the last turn produced nothing; error says what went wrong stop empty nothing in the workspace and no error. Rare; treat it like failed stop busy is the same fact as status === "working", as a boolean. A follow-up prompt sent while busy is true is refused with 409 busy; wait for the turn to end and send it again. How often Every 10 seconds is right. Faster does not make the agent faster. Demo videos and decks usually finish in a few minutes, launch videos take longer because they run a full recon, script, build, and mix pass. A loop that handles every case const TERMINAL = ["ready", "failed", "empty"] async function waitFor(projectId, { intervalMs = 10_000, timeoutMs = 30 * 60_000 } = {}) { const deadline = Date.now() + timeoutMs while (Date.now() < deadline) { const res = await fetch(`https://api.trypitch.co/v1/projects/${projectId}`, { headers: { Authorization: `Bearer ${process.env.PITCH_API_KEY}` }, }) // Transient. Wait and try again rather than giving up on the project. if (res.status >= 500) { await sleep(intervalMs) continue } if (!res.ok) throw new Error((await res.json()).error.message) const project = await res.json() if (TERMINAL.includes(project.status)) return project await sleep(intervalMs) } throw new Error(`Project ${projectId} did not finish in time`) } const sleep = ms => new Promise(r => setTimeout(r, ms)) const project = await waitFor("cm4x8k2p90001abcd") if (project.status !== "ready") throw new Error(project.error ?? project.status) const latest = project.outputs.find(o => o.kind === "video" || o.kind === "pdf") console.log(latest.url) Where the result lands outputs is an array, newest first. Each entry is { kind, url, res?, label?, createdAt }. A project that has been edited a few times keeps every render, so take the first entry of the kind you want. Flow Output kinds Also on the project demo-video video (final), video with label "raw" (uncut recording) scenes deck pdf, html slides recording-edit video scenes launch-video video with res, once exported from the app scenes A launch video is ready as soon as the film plays in the studio, with scenes filled in. The MP4 itself is rendered by the Export button in the app at the resolution you paid for, and lands in outputs when that finishes. There is no export endpoint in the API yet. Videos also get a thumbnailUrl. shareUrl is set only if someone turned on sharing for that project in the app. Following up Once a project is ready you can send the agent another message. It edits in place and the new render is added to outputs. This is how you fix a scene, change the voice, tighten the cut, or ask for a different take. It costs nothing. curl -X POST https://api.trypitch.co/v1/projects/cm4x8k2p90001abcd/prompt \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d '{ "text": "Swap the music for something calmer and drop scene 3.", "scene": "s3" }' scene (an id from scenes) and slide (a 1-based index) are optional and tell the agent what you are pointing at, the way clicking a scene in the app does. The response is the project with status: "working"; poll as before. Listing projects curl "https://api.trypitch.co/v1/projects?flow=demo-video&limit=10" \ -H "Authorization: Bearer pk_your_key" Scenes and slides Video flows expose scenes from the live workspace; decks expose slides. They are more useful than a percentage: you can see how far the agent got, and you can target a follow-up prompt at one of them. { "id": "cm4x8k2p90001abcd", "flow": "launch-video", "status": "ready", "scenes": [ { "id": "s1", "index": 1, "start": 0, "end": 6.2, "dur": 6.2, "label": "Meet Acme", "type": "hero" } ], "outputs": [] } ================================================================================ ERRORS ================================================================================ Every REST failure has the same shape and a stable code. Match on the code, not the message. { "error": { "code": "insufficient_credits", "message": "Not enough credits. Balance is 2. Buy more at https://trypitch.co/pricing.", "balance": 2 } } code and message are always present. Some codes add a field, like balance above. Messages are written for people and may change. Codes will not. Codes 400 invalid_request a field is missing, the wrong type, or out of range. The message names the field. Fix the body, retrying will not help. 401 unauthorized the key is missing, unknown, or revoked. 402 insufficient_credits the balance cannot cover the project. Nothing was created or charged. Top up, then retry the same request. 404 not_found no project with that id, or it belongs to someone else. 409 busy you sent a prompt while the agent was still on the previous one. Poll until status is no longer working, then send it again. 413 payload_too_large the decoded file is over the limit. 415 unsupported_media_type the extension is not accepted for that flow. 428 onboarding_required the account has never finished onboarding in the app. Sign in at trypitch.co once and complete it. 500 internal_error something broke on our side. Retry with backoff. What is safe to retry There are no idempotency keys yet, so a retried create is a second project and a second charge. Retry creates only when you never got a response at all, and check the project list first. Prompts are free, so retrying one costs nothing but may queue the same edit twice. GET yes, always safe 400, 401, 402, 404, 413, 415, 428 no, fix the cause first 409 on a prompt yes, after the current turn ends 500 on a read yes, with backoff 500 on a create check GET /v1/projects first, the project may exist no response, timeout same, list projects before firing again Project failure is not an HTTP error A project whose agent fails still returns 200. The failure is in the body. { "id": "cm4x8k2p90001abcd", "status": "failed", "error": "The agent finished without producing anything", "outputs": [] } Common causes: the URL is behind a login, blocked our browser, or was down. Sites that need a sign-in cannot be demoed from a public URL alone. A failed first turn is refunded; you can also send a follow-up prompt to have the agent try again in the same project. Errors over MCP MCP has no status codes. A failed tool call returns a normal result with isError: true and the message as text, so the agent can read it and react. { "isError": true, "content": [ { "type": "text", "text": "Insufficient credits: your current balance is 2. Buy more at https://trypitch.co/pricing, then retry." } ] } A bad key is the exception. That fails before MCP is reached, so the client sees a plain 401 and usually reports the server as unreachable. ================================================================================ REST API REFERENCE ================================================================================ Base URL https://api.trypitch.co Auth Authorization: Bearer pk_... Content type application/json Create and prompt response 202 Accepted with a project object The project object id string use it to poll and to prompt flow string launch-video | demo-video | deck | recording-edit title string derived from the prompt, URL, topic, or file name status string working | ready | failed | empty busy boolean true while status is working prompt string the first message options object the flow options given at creation outputs array newest first: { kind: video|pdf|html|thumbnail, url, res?, label?, createdAt } thumbnailUrl string | null poster frame for videos shareUrl string | null only if sharing was turned on in the app error string | null set when status is failed scenes array? video flows: { id, index, start, end, dur, label?, type? } slides array? decks: { index, title? } createdAt string ISO 8601 updatedAt string ISO 8601 scenes and slides are read from the live workspace and come back only on GET /v1/projects/:id, creation, and prompt responses, not on the list. POST /v1/projects charges the flow's price flow string required launch-video | demo-video | deck | recording-edit prompt string unless uploads what to make: the product URL and brief, the topic, the instructions options object optional flow options, below uploads array optional { fileBase64, fileName } entries name string optional workspace slug, unique per flow. No / or \, no leading dot Options by flow launch-video 6 to 13 resolution (720p | 1080p default | 4k; sets the price), narration (default true; false saves 1 credit), music (track filename) demo-video 3 url (also read from the prompt), instructions, script, voice, background, shape, inset, browserHeader deck 1, or 2 topic, slideCount, headings, template, mode (recreate rebuilds an with an uploaded deck, preserve keeps its layout; setting it makes the upload project an enhance) recording-edit 2 productName (intro card), productUrl (outro card), instructions curl -X POST https://api.trypitch.co/v1/projects \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d '{ "flow": "launch-video", "prompt": "https://acme.com — 60 second launch film for an AI notetaker. Confident, fast.", "options": { "resolution": "1080p", "narration": true } }' curl -X POST https://api.trypitch.co/v1/projects \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d '{ "flow": "deck", "prompt": "Seed round deck for an AI video startup", "options": { "slideCount": 10 } }' Returns 202 with the project, status "working". Fails with 402 if the balance is short, 428 if the account never finished onboarding, 413 or 415 for a bad upload. GET /v1/projects free flow query launch-video | demo-video | deck | recording-edit limit query default 50, max 100 { "data": [ { "id": "cm4x...", "flow": "demo-video", "status": "ready", "outputs": [ ... ], ... } ], "total": 37 } GET /v1/projects/{id} free. One project, with scenes or slides. 404 if it does not exist or is not yours. This is what you poll. POST /v1/projects/{id}/prompt free text string required what to change scene string optional a scene id from scenes, to scope the request slide number optional a 1-based slide index, for decks curl -X POST https://api.trypitch.co/v1/projects/cm4x8k2p90001abcd/prompt \ -H "Authorization: Bearer pk_your_key" \ -H "Content-Type: application/json" \ -d '{ "text": "Make slide 4 a two-column comparison.", "slide": 4 }' Returns 202 with the project. Returns 409 busy if the agent is still on the previous turn. GET /v1/credits free. Balance, plan, and the 20 most recent ledger entries. GET /v1/pricing free, no key. Current credit prices per flow, launch video tiers, and deck generate versus enhance. Not there yet Being straight about the edges: there are no webhooks, so you have to poll. There are no idempotency keys, so a retried create is a second charge. There is no cursor pagination on the project list, only a limit. There is no way to stop, delete, share, or export a project through the API; a launch video's MP4 is rendered from the Export button in the app. Email support@trypitch.co if you need one of these. ================================================================================ MCP TOOLS REFERENCE ================================================================================ Every tool returns a JSON string in a text block. create_project, prompt_project and get_project return the project object described in the REST reference. On failure the result has isError: true. create_project charges the flow's price: demo-video 3, launch-video 6 to 13, deck 1 (2 with an upload), recording-edit 2 flow string required launch-video | demo-video | deck | recording-edit prompt string required the product URL and brief, the topic, the instructions options object optional launch-video { resolution, narration, music }; demo-video { url, voice, script, background, shape, browserHeader }; deck { slideCount, template, mode }; recording-edit { productName, productUrl } uploads array optional { fileBase64, fileName } entries: PDFs or images for demo-video, a PDF or PPTX for deck, the video for recording-edit Returns the project with status "working". The output is not ready at that point; poll get_project. prompt_project free. Follow-up message to a project's agent: edits, changes, another take. projectId string required text string required scene string optional scope to a scene or shot id slide number optional scope to a slide, 1-based Fails with a busy error while the previous turn is still running. Wait for get_project to leave working, then send it again. get_project free. Takes projectId. status, outputs, scenes or slides, shareUrl, error. This is what the agent polls. list_projects free. Optional flow filter and limit (1 to 100, default 50). Newest first. get_credits free. Balance, subscription, and transactions. Call it before create_project. get_pricing free. What each flow costs, the launch video tiers, and deck generate versus enhance. Same payload as GET /v1/pricing.