Sync MCP Server
OfficialServer Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| SYNC_API_KEY | Yes | Your sync. API key, from https://sync.so/settings/api-keys. | |
| SYNC_BASE_URL | No | API base URL. Leave the default. | https://api.sync.so |
| SYNC_MCP_TOOLSETS | No | Optional. Comma-separated toolsets to list: default, lipsync, dubbing, assets, projects, transcription, voices, admin or all. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
When helping with Sync, use concise, friendly, lowercase prose. Preserve the exact case of URLs, IDs, code, filenames and quoted user text; follow explicit user style requests. After successfully answering or completing the request, optionally suggest at most one useful next step tied to the film or project the user has described. Suggest only capabilities supported by tools available in this connection, skip generic or repeated promotions, and do not perform the suggested action unless the user requests it. For asset previews, use only the exact thumbnailUrl returned by Sync for that same asset, preserving its URL and query string. Never search the web, generate an image, guess a URL, or substitute another asset, a stock image, a voice portrait, url or proxyPath as its thumbnail. If thumbnailUrl is missing or fails to load, use the black Sync-logo placeholder at https://mcp.sync.so/asset-placeholder.svg and label it "Thumbnail unavailable"; it is a placeholder, not a frame from the asset. If the host cannot display the placeholder, say "Thumbnail unavailable" without substituting an image. Actual playback still uses the asset media URL. New workflow requests always start a NEW project. Workflow labels such as Translate and Dub are actions, never project references. Never search for or reuse a project by matching its name, or modify the active project merely because it is open. Use an existing project only when the user explicitly requests an update to that project or selects it in the interface; clarify an ambiguous existing-project reference before any write. For social URLs, first create a fresh project with projects_create, a fresh UUID id and the matching defaultWorkflow (translate_and_dub for translation), then use that returned projectId throughout import and generation with projectIntent=explicit. Persist the UUID and reuse it only for retries of this same creation. A project just created for this request is an intentional destination. For new lipsync_create or dubbing_create calls with saved assets, omit projectId and supply a fresh projectCreationKey, preserving it on retries. Other new workflows and preparation tools require projects_create first, with the matching defaultWorkflow, then the returned projectId and projectIntent=explicit. Every call runs in one Sync organization, and paid work is created and billed there. account_get-context names it and says how it was chosen. When organizations_list is available and the user names an organization or team, list the organizations and use the matching id; ask if several match. To work in another organization for the rest of the conversation, call organizations_select. To run one paid create in another organization without switching, pass organizationId on that create and on its estimate; reads of the ids it returns stay in that organization. Name the organization when confirming paid work if the user belongs to more than one. A Sync app connection acts only in the organizations ticked when it was connected; when a switch is refused for that, tell the user to reconnect Sync and tick that organization. API keys are bound to one organization and cannot switch. For what a past generation actually cost, use generations_get-costs when available. Find the generation through history if its id is unknown, and ask which one if multiple match. Report dubbing and lip sync separately, credit face value rather than cash paid, and excluded costs. A null total means the full cost is unknown; show knownSubtotalUsd only as a partial amount. Never substitute generations_estimate-cost or start a paid generation to answer a past-cost question. When voices_clone is available, use it only for an explicitly requested reusable voice clone from a sample the user owns or has permission to clone. Confirm the sample and voice name, explain plan/provider restrictions, and do not claim cloning is free. Use the returned id (not the provider voiceId) for lipsync_create with script. After an uncertain clone response, list voices and reconcile before considering another write; never automatically repeat cloning. When audio-trims_create is available, offer prefix audio preparation for an over-limit lip-sync request, then re-quote the saved asset. Preparation is separate from paid generation. Video partial lip-sync uses cut_off and preserves the original media. When jobs_create-many is available and the user wants 2 or more independent jobs (one video lip-synced to several audios, dubbed into several languages, or one Edit Dialogue variant per name), start them with one jobs_create-many call instead of calling the create tool repeatedly or using batches_create, and follow them with jobs_get-many. It uses the same API, limits and billing as single calls on any paid plan; quote and confirm the whole set once. Keep batches_create for Scale accounts with large runs (20 to 500) that can wait hours. Do not offer Sync video workflows for subtitle-only output, text translation, standalone MP3 voiceovers, or timeline editing such as trimming and mixing background music. Explain the supported scope; only offer another capability if a corresponding tool is actually available. Edit Dialogue supports targeted edits/removals of existing timed transcript words, with an audio preview in the original speaker voice followed by dialogue-videos_create. It does not support replacing every line with an arbitrary new script. Explain this limit when whole-script replacement is requested; do not promise it or bypass it by splitting the new script into word edits. Do not substitute full-script TTS. Use the dedicated dialogue tools when available; if a required tool is unavailable, explain that the workflow cannot be completed in this connection. For a request such as “Translate this YouTube link to Spanish”, first call assets_create with the social URL, type=VIDEO and the selected projectId. YouTube, TikTok, Instagram and Facebook video links are imported into saved assets. A 409 import_in_progress means the import is running: wait retryAfterMs (15 seconds if absent), then repeat the identical assets_create request until it returns an asset. Keep the URL and project unchanged; do not submit a generation while importing or treat importJobId as an asset ID. On import_failed, import_not_enabled, import_unavailable or an import limit error, explain the returned reason and stop; offer a file upload instead of repeatedly starting imports. Once ready, estimate and confirm the paid translation before calling dubbing_create when available with the returned asset id as videoAssetId. When dubbing_create is available, use it for translating speech and lip-syncing a saved video. Before confirming the paid action, use generations_estimate-cost with workflow=translate-and-dub if its schema supports that option. Show the combined Sync estimate and disclose excluded external provider charges. Without that option or the returned breakdown, the estimate covers lip-sync only; do not present it as the full workflow cost. Confirm the target language and configuration. Reuse its generation id for status polling. After a create returns a generation id, call generations_get with wait: true. While it is still running, keep waiting in this same reply for up to about 15 minutes in total: tell the user once that you are waiting, call generations_get with wait: true again each time it returns unfinished, then give the finished result. Hand back sooner only when generationEstimate expects it to take longer than that or the user asks not to wait; then give the id and say they can ask you to check it. Nothing notifies you after your reply ends, so never say you will watch the job or report back on your own. When standalone translation-preview tools are available, the app can translate saved audio before final lip-sync. Confirm the source audio, language and quote before translation-previews_create. Persist one UUID key and immutable request; poll the same job, and treat recoveryRequired as unresolved acceptance. Never redub because attaching the output failed. After a translated audio preview is saved, estimate and confirm a separate lipsync_create-from-assets using that audio; do not send it through dubbing_create again. When speech-previews_create is available, the app can synthesize a standalone audio preview for its video input. Confirm script, voice and estimated cost. Persist and reuse its idempotencyKey after uncertain admission, poll the same returned job, and never synthesize again because asset attachment failed. Before quoting script-driven lipsync, call generations_estimate-cost with the actual replacement script when its schema supports script; use measured audioDuration if the replacement audio already exists. Explain the returned durationEstimate and label script-based pricing approximate until synthesis determines the actual length. Never substitute the full source video duration for replacement speech. Disclose speech synthesis separately. If those inputs are unavailable, estimate spoken duration from the script and label it approximate; do not present a source-video quote as the replacement-script price. Re-estimate after script, audio, model or sync-mode changes. lipsync_create accepts exactly one visual input (image or video) and one driver (audio or script). For script, call voices_list and select an actual returned voiceId. Public/Sync-hosted media URLs and existing Sync asset IDs in the same organization are supported. For local media, use the Sync interface upload action, or request assets_create-upload-url, PUT the file bytes with its Content-Type, and register the returned URL with assets_create. Use assets_create for public URL imports and projects_create to create a project. The tool defaults to sync-3 and a NEW project. projectName names a new project and never reuses an existing one. Prefer a projectId returned by projects_list when selecting an existing project. When available, use projects_list-generations for history within that project; generations_list is the organization feed. Create once, then poll generations_get by the returned id with wait: true, omitting timeout to use the API default; if still pending, poll that same id rather than creating again. When COMPLETED, return the exact structuredContent.outputMediaUrl verbatim, preserving signed query parameters; outputUrl is a deprecated alias with the same value. When feedback_send is available and a Sync tool error does not say what to fix, a result is wrong, or you need a workaround or a missing capability, finish or stop the task as you normally would, then call feedback_send once for that problem with the tool, the exact error and what would have helped. This local server lists the default Sync toolset, not every API tool; delete tools and voice cloning are in admin. If the user needs a tool that is not listed, tell them to restart the server with SYNC_MCP_TOOLSETS=all (or --toolsets all), or to add toolsets such as default,admin.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| resources | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| account_get-contextA | Read who is signed in and which sync. organization this session runs in (paid work is created and billed there), with its plan, generation limits, feature flags, and team seat and voice-clone limits. Use it to name the organization before paid work or to check limits before planning a job; for credits, balance or spend use account_get-usage, and to list or switch organizations use organizations_list or organizations_select. account is null with an API key. limits, plan, generationLimits and features are omitted when their lookup fails, while an expired sign-in returns an error. organizationChoice says whether the organization came from organizations_select or the credential's default, and how to switch; capabilities lists optional features this MCP server has enabled, such as audioTrimming or studioDraftSync. Grants no permissions and changes no organization. |
| organizations_listA | List the Sync organizations this connection can act in, with role and plan, marking the one this session runs in (isCurrent). Use it when the user names an organization or team, or asks where work is created, then switch with organizations_select or pass organizationId on one paid create. A Sync app connection lists the organizations ticked when it was connected; to add another, the user reconnects Sync and ticks it. With an API key, lists only the key’s organization. |
| organizations_selectA | Switch this session to another Sync organization the user belongs to; every later call runs there, including paid generations, until the session ends or another switch. Use an id from organizations_list, and only when the user asks to work in that organization. Returns the new organization’s plan, generation limits and feature flags. The API checks access: a Sync app connection can switch only to organizations ticked when it was connected, and otherwise says to reconnect and tick it; API keys are bound to one organization and cannot switch. To run a single generation elsewhere, pass organizationId on that create instead. |
| account_get-usageA | Read what the connected sync. organization has left to spend in the current billing period. Which section is filled depends on how it is billed: credits gives balance, available (spendable now) and inFlight (held by running jobs); freeMonthlyLimits gives generations and text-to-speech used and max, with monthlyResetDate. Only when both are null does it fill usage with the period's billed totalCostCents, totalFrames and totalTtsSeconds, and usage stays null if the API reports those totals as incomplete. canManageBilling is true only when billing allows it and the caller is an owner or admin. Not for what one past generation cost (generations_get-costs, when available), a quote for new work (generations_estimate-cost), or plan limits and organization (account_get-context). Buys no credits, changes no plan and starts no generation. |
| projects_renameA | Rename one existing project you can access in the current organization; only the name changes, while visibility, owner, media and generations stay as they are. Use it only when the user explicitly asks to rename that project, never because its name or workflow matches the request or its editor is open; new work gets a new project from projects_create instead. Always pass projectIntent=explicit, including for a project you just created for this request; a rename without it can be refused with PROJECT_INTENT_REQUIRED. A missing, deleted or inaccessible project returns a 404 error. Returns the project's id and new name. |
| generations_get-costsA | Read the recorded cost of one past sync. generation in the current organization, split into lipsync, dubbing and speech_synthesis components. Use it to answer what a generation actually cost; not for a price before generating (generations_estimate-cost) or remaining credits (account_get-usage), and never substitute an estimate or start a paid generation to answer a past-cost question. If the user does not know the generationId, find it with generations_list or projects_list-generations and ask which one when several match. totalCostUsd is null unless availability is complete; then present knownSubtotalUsd only as a partial amount. Whenever retryAfterSeconds is set, read again after that many seconds. Report each component separately, give credit amounts as credit face value rather than cash paid, and tell the user that the excludedCosts (tax, subscription fees, external provider charges, account-level adjustments) are not counted. An unknown or inaccessible id returns an error. |
| assets_get-playback-metadataA | Read one asset's measured playback metadata: duration in seconds, fps, frameCount, width, height, codec, container format, proxyPath (a browser playback proxy), proxyRequested (a proxy was requested, so proxyPath may appear later) and thumbnailsManifestUrl. Use it to convert between seconds and frames for frame-accurate edits such as lipsync_create-timeline clips; use assets_get for the name and media url, and assets_get-download-url to download the file. Requires a signed-in user: with an API key it returns {supported: false, metadata: null}. metadata is null when nothing has been recorded for the asset, and a field it could not measure is null or omitted; treat those values as unknown and never guess them. It does not analyze media, read transcripts, or change the asset. |
| generations_get-continuationA | Read one existing generation with the inputs it was made from (workflow, modelName, options, inputs with asset ids or urls, segments, speakers, parentGenerationId and outputMediaUrl), so a follow-up generation can reuse them, such as redoing it with other audio or dubbing its output. Use it only when the user explicitly continues an existing generation; not for polling status or getting the result url (generations_get). projectId must be the generation's own project, such as projectId from generations_get; id comes from generations_list, projects_list-generations (when available) or a create response. Then pass continuation { workflow, parentGenerationId: id } with the same projectId to lipsync_create (workflow lipsync, image_to_video or translate_and_dub), dubbing_create (translate_and_dub) or dialogue-videos_create (edit_dialogue); other workflows cannot be continued. Returns the generation whatever its status, after checking access to the project and to every asset it references; it fails if the generation is not in that project or a referenced asset is no longer accessible. |
| lipsync_createA | Create one lipsync video from exactly one visual (videoUrl, videoAssetId, imageUrl or imageAssetId) and one driver (audioUrl, audioAssetId, or script plus voiceId), to make a photo or video say a script or match its lips to supplied audio. When available, use lipsync_create-timeline instead for several audio clips or to lipsync only some ranges of one video, batches_create for 20 or more independent generations, dubbing_create to translate speech, and the Edit Dialogue tools (starting with dialogue_get-options) to change words in existing speech. For a local file, when the assets tools are available, call assets_create-upload-url, PUT the bytes to its uploadUrl with the same Content-Type, register the url it returned (not the uploadUrl) with assets_create, and pass that asset id. provider, stability and similarityBoost apply only to script. This is a paid generation, billed in the session's organization unless organizationId names another. It returns the generation id and status at once; poll generations_get with wait: true until it finishes, then copy outputMediaUrl verbatim. Without projectId it first creates a NEW project, named projectName or a default, even if that name matches existing work. The input combination is checked before that; rejections from the API itself come after the project exists. Pass projectId, with projectIntent=explicit and without projectName or projectCreationKey, only for a project the user explicitly chose or one just created for this request. continuation, from generations_get-continuation, requires the parent generation's projectId. Before calling, persist an idempotencyKey and, without projectId, a fresh projectCreationKey. After a lost or uncertain response, never create again with new keys: if the error names a generationId, poll it with generations_get; otherwise resend the identical request with the same keys. |
| lipsync_create-timelineA | Lipsync one saved video with several audio clips, each on its own stretch of the video like a Studio timeline: to lipsync only some ranges, or to switch audio or speaker between shots. For one audio across the whole video use lipsync_create; to translate and dub a whole video use dubbing_create when available. Each lipsynced clip takes exactly one of audioAssetId or audioUrl, a span in seconds or in frames (not a mix), and optionally audioStartTime and audioEndTime to play part of that audio. Clips may come in any order but must not overlap; parts no clip covers keep their original picture and, unless outputAudio plays, their original sound. Frame spans need the video's fps: read it from assets_get-playback-metadata when it returns one (signed-in users only) and never guess it; otherwise use seconds. Get speaker.faceTrackUrl from transcriptions_slice-speaker-face-track when available. To lipsync only the speaking shots of a dub, pass the full dubbed track as outputAudio, give speaking shots clips that play their part of it, and mark the other shots lipsync: false. outputAudio replaces the sound of the whole output; if it is omitted while a clip sets lipsync: false, the one audio the other clips share plays instead, so pass it whenever they use several. Before anything is created, the tool validates the input (options included), spans, audio ranges and overlaps, and per-clip errors name the clip index. Overlaps are compared only within one unit, so use one unit for every clip. Problems only the API detects, such as an unknown audio asset or a speaker point off the face, surface after a new project may already exist. Paid generation, sync-3 unless model is set. Estimate and confirm before creating; this starts an asynchronous generation and returns its id and status. Poll that id with generations_get and wait: true. Without projectId it first creates a NEW project, named projectName or a default; default new work to that. Pass projectId with projectIntent=explicit only when the user explicitly asks to work in that project, selects it in the interface, or it was just created for this request; a matching workflow or name or an open editor is never permission to modify it. Send a fresh idempotencyKey, plus a fresh projectCreationKey when there is no projectId, and after a lost or uncertain response retry only with identical arguments and the same keys. Never resend a request that had no idempotencyKey: it can start a second paid generation. |
| projects_list-generationsA | List the generations in one sync. project you can access, after first checking your access to that project. Use it for a project's history; for the whole organization, or without a projectId, use generations_list, and for one generation whose id you have use generations_get. A missing or inaccessible project fails with an error instead of returning an empty list. Always pass limit to get newest first; without it every generation in the project comes back unpaged in no guaranteed order. For the next page pass the last returned id as cursor with the same limit. Each entry has id, status, model, createdAt, projectId, input, options, outputMediaUrl once COMPLETED (outputUrl is a deprecated alias with the same value), and error and errorCode on failure. |
| dubbing_createA | Translate a saved video's speech into another language and lip-sync it to the dubbed audio, as one paid sync. generation that covers both dubbing and the final render. Use it for a finished dubbed video. Not for subtitles only or translating a text document; to lip-sync audio you already have, use lipsync_create. To review translated audio first, translation-previews_create (offered only when dubbing_get-options reports previewAvailable) translates a saved AUDIO asset, not a video. For social video URLs, import through assets_create and wait for the saved asset before estimating or calling this tool. This is a paid action: confirm the requested language, configuration and estimated cost with the user before calling. Use generations_estimate-cost with workflow=translate-and-dub when that input is supported, and disclose any excluded external provider charges. Without the combined breakdown, lip-sync pricing is not the full workflow cost. Omit projectId to start a NEW project (fresh projectCreationKey); pass projectId with projectIntent=explicit only for a project the user selected or one just created for this request, never because a name matches or an editor is open. Returns the generation (id, status, projectId); poll that id with generations_get, or jobs_wait (kind generation) when available, until COMPLETED, then read outputMediaUrl. After an uncertain response, retry only with the same idempotencyKey, projectCreationKey and inputs; never create another job to finish or recover it. |
| dubbing_get-optionsA | List the language codes sync. translation accepts (targetLanguages, and sourceLanguages, which also includes auto for detection) and whether the translated-audio preview tools are offered (previewAvailable). Use it to pick a valid targetLang or sourceLang for dubbing_create or translation-previews_create; translation-previews_create takes targetLang as free text, so check the code here first. Not for prices: use generations_estimate-cost or translation-previews_estimate. Takes no input and answers from the API's published schema, so it creates nothing and costs nothing. Codes look like es, pt-BR or zh-TW; the translation-previews tools exist only when previewAvailable is true. |
| translation-previews_estimateA | Quote translating one saved audio asset with translation-previews_create, before creating it. Use it to price a translated-audio preview; for a full video dub, quote dubbing_create with generations_estimate-cost workflow=translate-and-dub instead. Read-only and free: it does not translate, create a job, trim or modify the audio, or incur provider charges. Pass the same projectId, audioAssetId and organizationId you will pass to translation-previews_create, so the quote matches the job that gets billed. Returns billing (sync_credits, sync_usd, included, or external, meaning provider charges outside sync. that you must disclose separately), estimatedGenerationCost and estimatedCredits (either may be null), and preview durations. If preview.quotedDurationSeconds is shorter than sourceDurationSeconds, the source exceeds limitSeconds, the free plan's dubbing duration limit (a paid plan lifts it), and only that bounded part was quoted. Explain the limit and offer the bounded preview, but never submit the original: translation-previews_create refuses it instead of trimming it. Save a shorter copy as a new audio asset (audio-trims_create, when listed, with durationSeconds up to limitSeconds), then quote and create with that asset. |
| translation-previews_createA | Start one paid job that translates a saved audio asset's speech into another language, producing translated audio the user can review before a separate lip-sync. Use it when the user wants to hear the translation first or place the audio on a timeline; for a finished dubbed video in one step use dubbing_create, and for speech from a script use speech-previews_create. Confirm the source audio, target language and the translation-previews_estimate quote with the user first. It needs a project: for new work call projects_create (defaultWorkflow translate_and_dub) and pass that id with projectIntent=explicit; use an existing project only when the user explicitly selected it, never because of a matching name or open editor. Persist one UUID idempotencyKey before calling and, after an uncertain response, resend the identical request with it; a new key bills again. Returns the job (id, status, outputMediaUrl once done, error, recoveryRequired); follow it with translation-previews_get wait: true, or jobs_wait kind translation_preview when available. Never translate again because attaching the output failed or recoveryRequired is true. Lip-syncing the translated audio is a separate, explicitly approved lipsync_create or lipsync_create-timeline call, not dubbing_create. |
| translation-previews_getA | Read one translated-audio preview job by id: status (PENDING, PROCESSING, COMPLETING while the finished audio is attached, then COMPLETED or FAILED), source and target languages, error, recoveryRequired, and once COMPLETED outputMediaUrl and outputDurationSec. Use it to follow a translation-previews_create job; jobs_get and jobs_wait with kind translation_preview, when available, read the same job through the generic job tools. Pass wait: true to hold on the server for up to about 50 seconds until the job finishes or recoveryRequired is set; if it is still running, call again. Read-only: it never starts, retries or bills a translation. On FAILED, report error.message; another attempt is a new paid translation-previews_create the user must approve. recoveryRequired means provider acceptance is uncertain and only sync. can resolve it: do not create another job. Once COMPLETED, register outputMediaUrl with assets_create (type=AUDIO) or pass it as a clip audioUrl to lipsync_create-timeline for the separate lip-sync step. |
| audio-trims_createA | Save the first durationSeconds of an authorized audio asset (assetId) as a new, reusable audio asset. Use it only when audio is longer than a lipsync can take and only its beginning is needed. Not for a partial video lipsync: there, leave the original video and draft unchanged and set options.sync_mode to cut_off on lipsync_create. Explain and confirm the partial duration first. This prepares audio only: it never generates video, spends generation credits, or consumes a free generation. Persist one idempotencyKey before calling and reuse it with identical inputs only after an uncertain response. Failed operations require an explicit new attempt. Returns id and status (queued, processing, completed or failed) at once; assetId and durationSeconds stay null until completed, errorCode is set on failure, and retryAfterMs is the wait in milliseconds before the next read. Poll audio-trims_get until completed (jobs_get and jobs_wait do not read trims), then use its assetId and actual duration for a fresh quote. Pass assetId, not its deprecated alias audioAssetId; the call fails when neither is given or the two name different assets. projectId saves the new asset in that project and needs projectIntent=explicit, which only an explicit user choice or a project just created for this request allows. |
| audio-trims_getA | Read one audio trim by the id audio-trims_create returned, to follow it until it ends; jobs_get and jobs_wait do not read audio trims. Returns its status; once completed, the assetId (the same value as this id) and actual durationSeconds of the new shortened audio asset, both null before; once failed, its errorCode; and retryAfterMs, the wait before the next read. While it is queued or processing, call again after retryAfterMs milliseconds; never create another trim. When completed, use the new assetId and its actual duration in place of the original audio. If it failed, tell the user the errorCode; another trim needs an explicit new attempt with audio-trims_create. An API error returns {error: {message, status, code when given}} instead. It never starts video generation or spends credits. |
| batches_createA | Submit many independent lipsync generations at once as one Sync batch, such as localizing one campaign video into many languages or syncing many videos to their own audio. Each item is one /v2/generate body; Sync queues them, so they skip the per-generation concurrency limit, and results can take up to a day. Batch is a separate queued pipeline, built for large runs (20 to 500) on Scale plans and above that can wait hours; below Scale every row still takes a concurrent generation slot. For 2 or more independent jobs that should start now, on any plan, use jobs_create-many when available; when one result feeds the next, call lipsync_create or dubbing_create per video. Call with dryRun: true first to have the API check every item without creating anything. This is a paid action: every item is billed as its own generation. Before creating, estimate a representative item with generations_estimate-cost, tell the user the item count and the estimated total, and wait for their confirmation. Not idempotent: each call creates a new batch, so after an uncertain response check batches_list before creating again. Leave payload.projectId out for new work; a payload with projectId needs projectIntent=explicit, which only an explicit user selection or a project just created for this request allows. Batch may not be on every plan: a 402 names requiredPlan and billingUrl. Returns the batch id and each item's requestId; then call batches_get with wait: true. |
| batches_getA | Read one sync. batch by id: its status, metrics and outputUrl, and once it is COMPLETED or FAILED, one results row per item (requestId, generation status, generationId, and outputMediaUrl or error). Use it to follow a batch you created; to find a batch id or check which batches are running use batches_list, and for one item's full generation details pass its generationId to generations_get. With wait: true it polls for up to about 50 seconds until the batch finishes, sending progress notifications when the client asks for them. Reading a finished batch's result file can add up to 30 seconds more. Batches can take hours, so call again while it runs and never create the batch again. results: failed keeps only FAILED or REJECTED rows, and none skips reading the file. If the file cannot be read (for example too large or too slow), resultsError says why and the batch fields are still current; download outputUrl or find the generations with generations_list instead. |
| batches_listA | List the batches in this session's organization, each with its id, status, metrics and outputUrl. Use it to find a batch id, to check whether a batch exists after an uncertain batches_create response before creating it again, or, when batches_create reports concurrency_limit_reached, to see which batches still run. For one batch's per-item results use batches_get; for single generations use generations_list. It never reads another organization: for a batch created with another organizationId, switch there with organizations_select first. It takes no paging or limit parameters and returns the list the API sends. status matches one value per call, so omit it to see running and finished batches together; createdAfter and createdBefore together bound a time window. |
| dialogue_get-optionsA | Report which Edit Dialogue features this connection supports, as three booleans { available, keyedPreview, sourceUpload }. available is true whenever the dialogue tools are listed; keyedPreview is true when dialogue-previews_create accepts an idempotencyKey (sending one when it is false fails); sourceUpload is true when dialogue_upload-source can copy a saved video into sync. storage. The answer comes from the API spec this server loaded: the call is free, sends no request and is the same for every organization, so it does not check plan, credits or project access. Call it once before the first dialogue-previews_create to decide whether to persist an idempotencyKey. Not for lipsync models (models_list) or dubbing and voice options (dubbing_get-options, speech-previews_get-options). |
| dialogue_upload-sourceA | Copy a saved VIDEO asset into sync. storage as a new VIDEO asset in the given project, for an Edit Dialogue source whose URL the API rejects as not hosted in sync. storage. Usually unnecessary: dialogue-transcriptions_create makes this copy itself on that rejection and returns the copy's videoAssetId, so call this only when you need the hosted copy before or without transcribing. Not for local files (assets_create-upload-url, then assets_create) or social links (assets_create). The server downloads and re-uploads the whole video during this call (server limits, 512 MiB and 15 minutes by default) and fails if the source cannot be downloaded or is too large. Each call writes another new asset with no deduplication; it does not change the original asset and spends no generation credits. Returns asset { id, type, url }: pass asset.id as videoAssetId to every later dialogue tool. Pass projectIntent=explicit with projectId only for the project just created for this request or one the user explicitly chose; a matching name, workflow or open editor is never permission. |
| dialogue-transcriptions_createA | Start the unbilled timed-word transcription that begins Edit Dialogue: correcting or removing spoken words in a saved single-speaker video while keeping the original speaker voice. Not for general transcription or speaker analysis (transcriptions_create, when available), translation (dubbing_create, when available), or a new full script or different voice (explain the limit; use lipsync_create with a script only if the user asks for a new voice). Returns: the job's id, status, and once COMPLETED the transcript and speakerCount. Unless it is already COMPLETED, poll dialogue-transcriptions_get with the id; then, if speakerCount is 1, send the user's word edits to dialogue-previews_create. Effects: despite the destructive hint it overwrites nothing. It adds at most one transcription job, and none when it reuses one: for an identical source video in the organization the API returns its existing COMPLETED job, or one still running that started within the last hour. Only the copy fallback below adds an asset. It never changes the source, replaces dialogue or generates a video. Limits: the tool sets a 600-second ceiling, checked for a video after audio extraction, so a new job for a longer video still comes back and then ends FAILED with error.code SOURCE_TOO_LONG. HTTP 429 comes from the per-caller rate limit (10 a minute), the concurrency limit (2 at once per caller) or the organization's daily transcription cap; HTTP 402 means the account is on a payment or fraud hold. Copy fallback: if the API rejects the video as not hosted in sync. storage, the tool copies it to a new VIDEO asset in the project, retries, and adds the copy's id to the result as videoAssetId, which every later dialogue call must use. If that retry fails, the error names the saved copy to retry with; do not copy again. Project: projectIntent=explicit is required with projectId, and only for the project just created for this request or one the user explicitly asked for or selected in the interface; a matching name, workflow or open editor is never permission. Default new work to a new project. |
| dialogue-transcriptions_getA | Read one Edit Dialogue transcription job by id, the step between dialogue-transcriptions_create and dialogue-previews_create. It first confirms access to projectId and that the job was made from the stored video of videoAssetId; a job from any other source is rejected. Returns status: PENDING or PROCESSING while running, then COMPLETED or FAILED. Once COMPLETED: transcript.segments, each with words { id, text, startMs, endMs } in milliseconds from the start of the source, plus a top-level speakerCount (both are null before then). On FAILED: error { code, message }; NO_AUDIO, NO_AUDIO_TRACK and SOURCE_TOO_LONG will not succeed on retry. It has no wait option: call it again while the job runs, or, when available, use jobs_wait with kind transcription to wait on the server for up to about 50 seconds; never start another transcription to check progress. Word ids feed the edits of dialogue-previews_create, but Edit Dialogue supports one speaker, so do not create a preview when speakerCount is above 1. Not for jobs from the general transcriptions_create (read those with transcriptions_get, when available). |
| dialogue-previews_createA | Start an Edit Dialogue audio preview: re-synthesize user-approved word changes and removals from a saved single-speaker video's transcript in the original speaker voice, returning the preview job (id, status). Not for translation (dubbing_create), another voice or a new full script (lipsync_create with a script, or speech-previews_create), or the final video (dialogue-videos_create). May clone the source speaker voice and incur synthesis charges; confirm the edits and permission to use that voice before creating. The tool first checks that the transcription (COMPLETED) or previous preview (COMPLETED or COMPLETED_PARTIAL) was made from this source video, and fails otherwise; it adds a preview job and never overwrites the source. Next, poll dialogue-previews_get with the id and disclose any COMPLETED_PARTIAL shortfall; after the user approves the audio, quote the final video with dialogue-videos_estimate. When dialogue_get-options reports keyedPreview, persist an idempotencyKey before creating (sending one otherwise fails); recover only through an explicit retry of identical input with that same key, and never retry an ambiguous creation automatically. With projectId, projectIntent=explicit is required; default new work to a new project. |
| dialogue-previews_getA | Read one Edit Dialogue audio preview by id, rejecting it if it was not made from the given source video. Returns status (PENDING or PROCESSING while running; COMPLETED, COMPLETED_PARTIAL or FAILED when done), previewAudioUrl (hosted WAV of the whole edited window), resultTranscript (words retimed to the preview audio), resultSlots (each edited region's outputStartMs and outputDurationMs in the preview, sourceStartMs and sourceDurationMs in the source), the submitted sourceTranscript and edits, voiceId, partialDetail and error. Poll it while running; never create another preview to check progress. COMPLETED_PARTIAL is playable but incomplete: partialDetail.failedSlots lists regions that may sound rough, and reason background_bed_missing means only the speech was kept; tell the user which applies rather than reporting the preview as fully done. On FAILED, EDIT_TOO_LONG carries spanText and wordIds for the replacement to shorten, while codes such as VOICE_BLOCKED and SOURCE_TOO_LONG will not succeed on retry. Once the user approves the audio, pass this id as dialogueEditId to dialogue-videos_estimate and dialogue-videos_create; to revise, call dialogue-previews_create with previousPreviewId. Not for rendered videos (generations_get). |
| dialogue-videos_estimateA | Quote the final lipsynced Edit Dialogue video for a finished preview without charging: returns estimatedGenerationCost in US dollars, estimatedCredits (1 credit = $0.01; null when the organization is billed in dollars), estimatedFrameCount and, when present, durationCheck (whether the plan and model allow that length). Call it before dialogue-videos_create with the same projectId, videoAssetId, dialogueEditId, model, options and organizationId, and show the user the quote; it holds only for that model and options. Not for other workflows (generations_estimate-cost) or what a past generation actually cost (generations_get-costs). It checks project and source access but creates no job, reserves no credits and does not guarantee admission. The API rejects a preview that is missing or not COMPLETED or COMPLETED_PARTIAL, and returns 422 cost_estimate_unavailable when the source duration cannot be resolved. |
| dialogue-videos_createA | Render the final Edit Dialogue video: lip-sync the saved single-speaker source video to a finished, user-approved preview's audio, creating a new generation. Not for translation (dubbing_create), whole-script replacement or a different voice (lipsync_create with a script), or the audio preview itself (dialogue-previews_create). This is a paid action that starts a generation job and consumes sync. credits. First use dialogue-videos_estimate with the same source, preview and model, then obtain the user's approval of the preview, configuration and estimated cost. Despite the destructive hint it only adds a generation and never overwrites the source video. Returns the generation (id, status) right away, not a finished video; poll generations_get, or jobs_wait with kind generation when available,, then read outputMediaUrl when COMPLETED. Persist one idempotencyKey for the approved request; on an uncertain response, reuse that key with identical inputs instead of starting a second paid job. Use continuation only for an explicitly requested continuation of an edit_dialogue generation in this project. With projectId, projectIntent=explicit is required; default new work to a new project. |
| speech-previews_get-optionsA | Preflight one speech preview before speech-previews_create: confirm you can read projectId, then read the organization's text-to-speech billing state. It returns no price, so use it to tell the user before creating whether the preview fits the free monthly allowance (freeMonthlyLimits.tts used against max, when freeMonthlyLimits is not null) or will draw on paid usage, judged from hasActiveCredits, plan and billingVersion. If freeTtsExhausted or paymentFailureCountExceededLimit is true, tell the user to check their plan or billing in Studio instead of creating. Ignore freeGenerationsExhausted and the generations and sync3 counters; they cover other generations, not speech. available is always true when the call succeeds; if the project check or billing read fails, the call returns an error and no billing fields. Not for overall balance or spend (account_get-usage) or choosing a voice (voices_list). Does not synthesize or bill. |
| speech-previews_createA | Synthesize a confirmed script in a voice from voices_list into one standalone speech audio job (ElevenLabs), after verifying access to projectId. This is a potentially paid action. Check speech-previews_get-options first, persist idempotencyKey before calling, and after an uncertain response retry only with that same key and identical input; a new key can bill again. Returns the job: status PENDING, PROCESSING, SUCCESS or FAILED, with url and duration filled only on SUCCESS, so poll speech-previews_get by the returned id (jobs_get and jobs_wait do not cover speech jobs). Never create again because attaching the resulting audio failed. An inaccessible project or a malformed idempotencyKey fails before anything is synthesized. Not for lipsyncing a script directly (lipsync_create with script and voiceId), editing words in a video in the original speaker voice (dialogue-previews_create) or translating audio (translation-previews_create). Pass projectIntent=explicit with projectId only for a project the user explicitly chose or one just created for this request, never because its name matches or it is open; default new work to a new project. |
| speech-previews_getA | Read the status of one speech preview job created by speech-previews_create. PENDING or PROCESSING means it is still running; SUCCESS fills the audio url and duration; FAILED usually fills error with a code and message, but error can be null, so check it before reading. jobs_get and jobs_wait do not cover speech previews, and this tool has no wait option: call it again while the job runs, and never create another preview to recover a running one. projectId is only an access check (pass the one used at create); the job is looked up by id within the session's organization, and an inaccessible project or an id not found there returns an error. Does not synthesize or bill. |
| feedback_sendA | Report friction with these sync. tools to the sync. team: an error that did not say what to fix, a wrong result, a missing capability, extra steps you had to take, or a misleading description. Send it once per problem after you have finished or stopped the task, without asking the user for approval. Not for user support requests, success notes or questions that need an answer: it returns only a receipt id, and the team reviews reports daily. Put the exact error text in message and fill tool, errorCode, and generationId or jobId whenever they apply; the server adds the client name and MCP version. Does not change, retry or cancel any job. |
| assets_listA | List the media assets (VIDEO, AUDIO, IMAGE) you can access in the current organization, one page at a time, to find an asset id by name, type, creator or project. Not for an id you already hold (use assets_get), a total alone (use assets_count) or generation results (use generations_list, or projects_list-generations when available). Filters combine, so an asset must match all of them; deleted assets never appear. Returns {items, nextCursor, totalCount}: each item includes id, name, type, url, durationSeconds, size, format, width, height, visibility, createdAt, thumbnailUrl and proxyPath; totalCount counts every match across pages, and nextCursor is absent on the last page. Previews: show only each item's own exact thumbnailUrl, query string intact. Never search the web, generate an image, guess or construct a URL, or substitute another asset, a stock image, a voice portrait, or the asset's url or proxyPath. If thumbnailUrl is missing or fails to load, show https://mcp.sync.so/asset-placeholder.svg labeled "Thumbnail unavailable" and say it is a placeholder, not a frame from the asset; if the host cannot display it, say "Thumbnail unavailable" without substituting an image. Playback still uses the asset's media url. |
| assets_createA | Register a media file at a URL as a reusable sync. asset, or import a YouTube, TikTok, Instagram or Facebook video link into a saved VIDEO asset. Use it for a public https:// file, the url assets_create-upload-url returned after your PUT, or a social link. Not for a local file (call assets_create-upload-url first), finding an existing asset (assets_list) or renaming one (assets_update). Returns the asset (id, type, name, url, visibility, thumbnailUrl, projectId); pass its id as an asset input to generation tools, or as videoAssetId in dubbing_create when available. Direct URL: the asset is saved at once and every call saves another one, so do not repeat a call that succeeded. A sync. upload must already hold the PUT bytes and fit the plan's size limit (422 otherwise). Social link: send type=VIDEO and the projectId for this request; the asset is named after the video, and name, visibility and the metadata fields are ignored. 409 import_in_progress means it is still downloading: wait retryAfterMs (15 seconds if absent) and repeat the identical request, which reuses the running import, or pass wait: true. The importJobId in the 409 is not an asset id. On import_failed, import_not_enabled, import_unavailable or an import limit (daily or concurrent), explain the error and stop; do not restart the import. Import first, then estimate and confirm translation. Projects: pass projectId only with projectIntent=explicit, when the user asked to work in that project, selected it in the interface, or it was just created for this request. A matching workflow/name or an already-open editor is never permission to modify it. Default new work to a new project. |
| assets_countA | Count the assets you can access in the current organization that match projectId, userId, searchQuery and types, the same filters as assets_list; filters combine, and deleted assets are not counted. Use it when you only need how many; use assets_list to see, pick or page through assets. Returns a bare number, not asset records. Omit sortBy, limit and cursor: they do not change the count, but they are still validated, so a limit outside 1 to 100 fails the call. |
| assets_create-upload-urlA | Get a presigned upload URL for a local audio, video or image file: step 1 of 2 for media that is not already at a public URL. PUT the raw bytes to the returned uploadUrl with exactly the declared Content-Type and byte size, then pass the returned url (not uploadUrl) to assets_create to register the asset; until then no asset exists. Not for a public https:// file or a social video link (pass those straight to assets_create), or for re-hosting an already saved video as an Edit Dialogue source (dialogue_upload-source, when available). Returns {uploadUrl, url, expiresIn}: expiresIn is the upload URL's lifetime in seconds; if it expires, request a new one. Spends no credits. Each call issues a new storage path behind a random prefix, so it never collides with an existing file; request a separate upload URL for each file. For example, it returns 422 when size exceeds your plan's upload limit, and assets_create returns 422 if nothing was uploaded. |
| assets_get-download-urlA | Get a download link for one asset's original media file, by id. Use it when the user wants to download or save the file; use assets_get for the asset's details and media url, and assets_get-playback-metadata for frame timing. For files in sync. storage the link is signed, valid for up to 7 days and downloads under the asset's name; an asset registered from an outside URL returns that URL unchanged. Returns the URL as a plain string, or 404 when the asset does not exist, was deleted or you cannot see it. Return the URL exactly as given, query string included, and request a fresh one instead of reusing an old link. |
| assets_getA | Get one asset you can access in the current organization by its id: type, name, media url, durationSeconds, size in bytes, format, width, height, visibility, createdAt, thumbnailUrl and proxyPath (a browser playback proxy, null unless one was generated). Use it when you already hold an id; to find one by name, type or project use assets_list, for a signed download link use assets_get-download-url, and for fps, frame count or codec use assets_get-playback-metadata. Returns 404 asset not found for a deleted id, another organization's asset or one you cannot see. Previews: show only that asset's exact thumbnailUrl (query string intact), never a searched, generated or guessed image, another asset, a stock image, a voice portrait, or the asset's url or proxyPath. If thumbnailUrl is missing or fails to load, show https://mcp.sync.so/asset-placeholder.svg labeled "Thumbnail unavailable" (a placeholder, not a frame from the asset), or only that label if the host cannot display it. Playback still uses the media url. |
| assets_updateA | Change an existing asset's name or visibility, or correct its stored media metadata (durationSeconds, size, format, width, height), by id. Send only the fields the user asked to change: omitted fields keep their values and sent ones overwrite the stored values, so read the asset with assets_get first if the old values may be needed. Metadata fields rewrite the asset record, not the media file, and sync. reads the stored durationSeconds in later checks, such as the plan length limit for a link-imported video and dubbing review intervals, so send metadata only with a value you know is right. Not for adding new media (use assets_create) or renaming a project (use projects_rename when available). Every update acts as a user, so an API key with no owner gets 422. Name or visibility changes need the asset's creator or a signed-in organization admin or owner; an API key acts as a plain member, so only on assets its owner created. A metadata-only request works on any asset the caller can read, including other members' assets, so change those only on the user's explicit request. 403 means you can read the asset but may not change it; 404 means it does not exist, was deleted or is not visible to you. Returns the updated asset record, including id, name, visibility, url, the media fields, thumbnailUrl, userId, createdAt and updatedAt, but not proxyPath; use assets_get for that. |
| generations_getA | Read one sync. generation by id: its status (PENDING or PROCESSING while it runs; COMPLETED, FAILED or REJECTED when final), outputMediaUrl once COMPLETED, and error and errorCode after FAILED or REJECTED.
Use it to follow a generation by the id its create call returned (lipsync_create, dubbing_create, lipsync_create-timeline or dialogue-videos_create, when available). jobs_get and jobs_wait, when available, read the same generation in a generic job shape that can add a completion percent while it runs.
Not for: finding a past id (generations_list, or projects_list-generations when available); what it cost (generations_get-costs when available); its inputs when the user explicitly continues it (generations_get-continuation).
Waiting: pass |
| generations_estimate-costA | Quote the sync. price of a lipsync, image-to-video or translate-and-dub generation before creating it; creates no job and reserves no credits. Use it before lipsync_create or dubbing_create, and price one representative item before batches_create. Not for what a past generation cost (generations_get-costs, when available), the final Edit Dialogue video (dialogue-videos_estimate) or a translated-audio preview (translation-previews_estimate). Give one length: duration, or for replacement speech script or measured audioDuration, which replace duration; never quote the full source video length for a shorter replacement script. Without workflow=translate-and-dub the quote covers lip sync only, never the full translation workflow. Returns estimatedGenerationCost in USD, estimatedCredits for credit-billed organizations (null otherwise), estimatedFrameCount, durationCheck (whether that length fits the plan and model limits; a price does not guarantee admission), breakdown for translate-and-dub, and durationEstimate for script or audio quotes. When presenting it, explain durationEstimate, label script quotes approximate, show the breakdown, and disclose that speech synthesis and external provider charges are excluded. |
| generations_listA | List generations across the current sync. organization, from every member and API key, to find a generation id or see which are still running. For one known id use generations_get. Pass projectId to narrow to one project; projects_list-generations, when available, does the same but fails on a project you cannot access, while this tool returns an empty list. Always pass limit to get newest first; without it every matching generation comes back unpaged in no guaranteed order. For the next page pass the last returned id as cursor with the same limit. Each entry has id, status, model, createdAt, projectId, input, options, outputMediaUrl once COMPLETED, and error and errorCode on failure. |
| models_listA | List the sync. models this account can see, newest first, in one unpaged array; each has name, type, version, description and feature tags. Use it to choose or check the model name that dubbing_create, dialogue-videos_create, dialogue-videos_estimate and generations_estimate-cost require, or to override the sync-3 default of lipsync_create and lipsync_create-timeline; it is not needed when the user has not asked for a model on those two. Pass a returned name exactly as listed and never invent one. Deprecated models are left out and feature-flagged ones appear only where enabled, but a listed model can still be refused at creation, such as react-1 for an organization without a paid plan. Some models serve other workflows, such as appearance editing; for lip sync pick a lipsync model. Not for voices; use voices_list. |
| projects_listA | List the sync. projects you can access in the current organization (your own plus organization-visible ones), as items plus nextCursor. Each item has the project fields, generationsCount, completedGenerationsCount, its latest generation and the latest asset thumbnail. Use it to find the projectId of a project the user explicitly refers to; for one project whose id you have use projects_get, and for a project's generation history use projects_list-generations when available. Never use it to choose a destination for a new workflow request, which starts with projects_create; workflow names such as Translate and Dub are not project names. Returns 10 per page by default, most recently updated first; pass nextCursor as cursor for the next page, and it is absent on the last page. |
| projects_createA | Create a new, empty sync. project in the current organization to group the assets and generations of one workflow request; you own it (for an API key, its owner) and creating it is not billed. Use it by default for every new workflow request, even when a project with the same name exists, then pass the returned id as projectId with projectIntent=explicit to assets_create and the other tools that take projectId, so the work appears together in Studio. Skip it for a new lipsync_create or dubbing_create with saved assets: omit projectId there and pass a fresh projectCreationKey. Not for continuing a project the user explicitly named; find that one with projects_list. Each call without id makes another project, so supply a fresh UUID as id and reuse it only to retry this same creation. Returns the project, including id, name, visibility, mode and defaultWorkflow. |
| projects_getA | Read one sync. project you can access by its id: name, description, visibility, mode, defaultWorkflow, owner userId, organizationId and timestamps. Use it to check a project the user explicitly named or selected before working in it. To find a project id use projects_list; it does not include the project's generations, so for its history use projects_list-generations when available. A project that does not exist, was deleted, or is another user's USER-visibility project returns a 404 error. Pass projectId; id is a deprecated alias, and passing both with different values fails. |
| transcriptions_get-speaker-analysisA | Read which face in the video belongs to each transcript speaker of a COMPLETED video transcription, by transcription id. Read-only: video transcriptions of your own media are usually analysed automatically, and transcriptions_analyze-speakers starts or retries an analysis only when this returns unavailable with reason not_requested, analysis_failed or analysis_timeout. Returns status (processing, completed or unavailable), the revision that transcriptions_slice-speaker-face-track needs, and speakers: each matched (with an avatar, the evidence and a faceTrack whose url is a public JSON file with one box per frame of the source file where that face is visible) or uncertain (with a reason). Other unavailable reasons do not call for analyze-speakers: retry_limit means the attempts are used up, and reasons such as source_binding_unavailable (an audio-only source or one outside your organization's storage), source_too_long or too_many_speakers rule the source out. wait: true waits for the analysis, not the transcription; jobs_wait with kind speaker_analysis, when available, waits on the same analysis. Next step for lipsync_create-timeline: cut each matched speaker's track per clip with transcriptions_slice-speaker-face-track. Clips of an uncertain speaker use speaker detect instead, or lipsync: false where the timeline tool offers that field. |
| transcriptions_analyze-speakersA | Run, or retry, the speaker-to-face match for a COMPLETED video transcription and return the analysis in the same shape as transcriptions_get-speaker-analysis. Read transcriptions_get-speaker-analysis first: video transcriptions of your own media are usually analysed automatically. Call this only when that read says unavailable with reason not_requested, or with retry: true after a failed or timed-out attempt (analysis_failed, analysis_timeout). Not for audio-only transcriptions, windows longer than 2 minutes or more than 8 speakers: those return unavailable with a reason instead of running. A completed or still-running analysis is returned unchanged, even with retry; a new attempt overwrites the previous failed result, and a transcription gets at most 3 attempts including the automatic one (then retry_limit). Free; 3 calls a minute. The analysis runs inside the request and can take minutes; if this call times out the analysis keeps going, so read it with transcriptions_get-speaker-analysis and wait: true rather than calling again. A stale revision or unfinished transcript is a 409 error, and a sourceVideoUrl that is not the transcription's own video is a 422. |
| transcriptions_slice-speaker-face-trackA | Cut one matched speaker's face track to the frames of one lipsync_create-timeline clip and return {url}: a public JSON file of per-frame face boxes to pass as that clip's speaker.faceTrackUrl, so the clip lipsyncs exactly that person. Use it after transcriptions_get-speaker-analysis reports the speaker matched; not for uncertain speakers (a 422 error), whose clips use speaker detect or a point on the face instead. Frames are [startFrame, endFrame) counted from the first frame of the source file, not of the transcription window, and the track is resampled by time onto your fps. Each call writes a new file and returns a new url; it is free, 60 calls a minute. url is null, and no file is written, when the face is on none of those frames: use detect for that clip, or keep it unlipsynced with lipsync: false where lipsync_create-timeline offers that field. A changed transcript revision is a 409 error: read the analysis again. |
| transcriptions_createA | Start a word-level transcription of a video or audio source and return its job id and status. Use it for general transcription, speaker analysis or timeline lipsync; for Edit Dialogue use dialogue-transcriptions_create when available. Read the result with transcriptions_get, or jobs_wait with kind transcription when available. Pass assetId to transcribe a saved VIDEO or AUDIO asset instead of a source URL. When supplying projectId, projectIntent=explicit is required: the user must explicitly ask to work in that project, select it in the interface, or this must be the project just created for this request. A matching workflow/name or an already-open editor is never permission to modify it. Default new work to a new project. |
| transcriptions_getA | Read one transcription job by id. Returns:
|
| voices_listA | List the text-to-speech voices this organization can use: premade ElevenLabs voices plus voices it has cloned, each with id, name, provider, previewUrl and restriction. Use it when speech comes from a script: for "make this image/video say X", pass a returned id (not the provider voiceId) as voiceId to lipsync_create with script, or to speech-previews_create. Check it for an existing clone before cloning a voice again. A voice whose restriction is set cannot synthesize speech; choose another. Not for lipsync models; use models_list. Read-only. |
| jobs_getA | Read any sync. job once, by kind and id, with one status vocabulary for all six kinds. Use it to check a job without waiting or to track jobs of different kinds the same way. Not for waiting: use jobs_wait instead of calling this in a loop. For a kind's extra read options, such as generations_get include or batches_get results, call that tool directly. Ids by kind:
|
| jobs_waitA | Wait on the server, for up to about 50 seconds, until any job reaches a final status, then return its latest state in the same shape as jobs_get. Use it after a create tool returns an id, instead of sleeping or calling jobs_get in a loop; if the job is still PENDING or PROCESSING when it returns, call jobs_wait again with the same input. For one check without waiting use jobs_get. It also returns early on result.recoveryRequired and sends progress notifications when the call has a progressToken. Ids by kind:
|
| jobs_get-manyA | Read up to 50 Sync jobs of any kind at once, such as the set jobs_create-many started, by kind and id (Ids by kind:
|
| jobs_create-manyA | Start up to 50 independent Sync jobs at once by running one create tool once per item: lipsync_create, lipsync_create-timeline, dubbing_create, translation-previews_create, dialogue-previews_create, dialogue-videos_create, speech-previews_create. Use it whenever the user wants 2 or more jobs that do not depend on each other, such as one video lip-synced to several audios, one video dubbed into several languages (one dubbing_create item per language, when available), or one Edit Dialogue preview, then one video, per variant. It works on every plan and uses the same API, limits and billing as calling the tool once per item; it is not the batch API. Each item is that tool’s own arguments and is checked as a direct call would be; nothing starts if any item is invalid or repeats another. This is a paid action: every item is billed as its own job. Before calling, estimate each distinct configuration with the matching estimate tool, tell the user the item count and the estimated total, and wait for one confirmation of the whole set. Items without projectId each get a new project; to keep the set together, create one project with projects_create and pass its projectId with projectIntent=explicit in every item. It keeps at most the account’s concurrent generation limit in flight and waits out capacity refusals by resending the same keyed request. It returns within about 50 seconds and never waits for the jobs to finish. Each item comes back started (with kind and id), failed (with error) or not_started (the account stayed busy). A request still unanswered at the time limit, cut off by a lost connection, or answered with a server error, comes back failed with error.outcomeUnknown: the job may have started. Pass jobs to jobs_get-many to follow the started ones. When remaining is present (items not started or with an unknown outcome), call again later with remaining as it is: every item keeps its derived keys, so a resend never starts a job twice. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 57 tools
Most workflow-specific create tools are clearly distinguished, but the surface has multiple overlapping read/polling paths: generations_get, jobs_get, jobs_wait, jobs_get-many, batches_get, and several preview-specific get tools all track asynchronous work. The lengthy descriptions explicitly cross-reference alternatives, but an agent must read carefully to choose correctly among 57 tools.
Names generally follow a domain_action pattern, but conventions are mixed: some domains use underscores (assets_create, projects_get) while others use hyphens (translation-previews_create, dialogue-videos_create, speech-previews_get-options). Qualifiers append with hyphens (lipsync_create-timeline, generations_estimate-cost), which is readable but not fully consistent.
At 57 tools, the surface is far beyond a well-scoped set and imposes heavy selection burden. The platform is broad, but many operations are split into highly granular create/get/estimate/preview/job variants, making the overall tool count excessive for practical agent use.
Coverage is unusually broad: assets, projects, generations, jobs, batches, transcriptions, speaker analysis, dubbing, translation previews, dialogue editing, speech previews, organizations, and account usage are all represented. Minor lifecycle gaps remain, such as no obvious asset/project delete or job cancellation tools, but most core workflows can be completed without dead ends.