Documentation
Endpoints
27 endpoints under
https://api.vidmoat.com/v1. Every one needs a bearer token; the scope column is what the credential must carry.Copy any block below, set
VIDMOAT_KEY, and it runs. Use a test key while you are exploring — it never spends credits and never queues a real render. Every response is { data… } on success and { error: { code, message, docs_url } } on failure; see errors.Account
Who the credential belongs to, what it may do, and what it has spent.
GET/v1/meaccount.read · —The owner of this credential: plan, credit balance, and the scopes this key actually carries. The first call to make when a request is 403-ing and you are not sure why.
curl https://api.vidmoat.com/v1/me \
-H "Authorization: Bearer $VIDMOAT_KEY"GET/v1/usageaccount.read · —Request counts (with the 4xx/5xx split) and credits spent, for building your own dashboard or a spend alarm.
curl "https://api.vidmoat.com/v1/usage?days=30" \
-H "Authorization: Bearer $VIDMOAT_KEY"GET/v1/schema/commandsno scope · —The full command vocabulary the editor accepts, verbatim from COMMAND_SCHEMA. Unauthenticated, because it is a description of the format rather than of your data.
curl https://api.vidmoat.com/v1/schema/commandsThis is the same schema the editor and the MCP server validate against. If an op is not in here, it does not exist.
Projects
A project is a timeline document. Every mutation goes through the same command reducer the editor uses, so there is exactly one definition of what an edit means.
GET/v1/projectsprojects.read · —List your projects, newest first.
curl "https://api.vidmoat.com/v1/projects?limit=20" \
-H "Authorization: Bearer $VIDMOAT_KEY"POST/v1/projectsprojects.write · project quotaCreate an empty project. Counts against the plan project limit.
curl -X POST https://api.vidmoat.com/v1/projects \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"My first API edit"}'GET/v1/projects/{id}projects.read · —One project including its full edit document — clips, tracks, settings.
curl https://api.vidmoat.com/v1/projects/$ID \
-H "Authorization: Bearer $VIDMOAT_KEY"PATCH/v1/projects/{id}projects.write · —Rename, or change output settings such as resolution and frame rate.
curl -X PATCH https://api.vidmoat.com/v1/projects/$ID \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Renamed"}'DELETE/v1/projects/{id}projects.write · —Delete a project and its render jobs.
curl -X DELETE https://api.vidmoat.com/v1/projects/$ID \
-H "Authorization: Bearer $VIDMOAT_KEY"POST/v1/projects/{id}/commandsprojects.write · plan-gated opsApply an ordered list of edit commands. This is the only way to change a timeline, and it is the same reducer the editor calls — anything you can do by hand you can do here.
curl -X POST https://api.vidmoat.com/v1/projects/$ID/commands \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"commands":[
{"op":"addTextClip","text":"Hello","start":0,"duration":3},
{"op":"addAdjustmentLayer"},
{"op":"setColor","saturation":1.1}
]}'Commands apply atomically: if one fails validation, none are written. Individual ops can still be plan-gated — a pro effect on a free plan returns 402 while the rest of the batch is rolled back.
POST/v1/projects/{id}/validateprojects.read · —Dry-run a command batch and get back the lint warnings without persisting anything. Overlapping text, off-canvas elements, unreadable font sizes.
curl -X POST https://api.vidmoat.com/v1/projects/$ID/validate \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"commands":[{"op":"addTextClip","text":"Hi","start":0,"duration":2}]}'Read the lint. Numeric x/y/fontSize choices routinely overlap on the real canvas, and this is cheaper than a render to find out.
Rendering
Renders are queued, not synchronous. A 5-second clip takes seconds; a three-minute one takes minutes.
POST/v1/rendersrender.write · export quotaQueue an export. Resolution cap, watermark and concurrency come from the owner’s plan.
curl -X POST https://api.vidmoat.com/v1/renders \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"projectId":"'$ID'","format":"mp4","quality":"standard"}'GET/v1/rendersrender.read · —List your render jobs, newest first. Filter by `projectId` or `status`, and page with `cursor`.
curl "https://api.vidmoat.com/v1/renders?limit=20&status=completed" \
-H "Authorization: Bearer $VIDMOAT_KEY"GET/v1/renders/{id}render.read · —Poll a job: PENDING → PROCESSING → COMPLETED or FAILED, with progress 0-100 and the download URL on completion.
curl https://api.vidmoat.com/v1/renders/$JOB_ID \
-H "Authorization: Bearer $VIDMOAT_KEY"Poll every few seconds, not every few hundred milliseconds — the rate limiter does not distinguish an eager poller from an attack. render.completed webhooks land in Phase 3 and are the right answer.
GET/v1/projects/{id}/previewrender.read · —Three representations: metadata (default, launches no browser), the full scrubable composition HTML, or one rendered frame. See the video-preview page.
curl "https://api.vidmoat.com/v1/projects/$ID/preview" \
-H "Authorization: Bearer $VIDMOAT_KEY"
# a real frame, as JPEG bytes
curl "https://api.vidmoat.com/v1/projects/$ID/preview?format=image&at=2.5" \
-H "Authorization: Bearer $VIDMOAT_KEY" -o frame.jpgformat=json is free and starts no browser. format=image launches Chromium and carries its own 10/min limit on top of your plan rate.
Media
Bring footage in. Everything you upload is attributed to the app owner’s account, and the usual moderation applies.
GET/v1/mediamedia.read · —List the media on the account, with the URLs a clip src can point at.
curl https://api.vidmoat.com/v1/media \
-H "Authorization: Bearer $VIDMOAT_KEY"POST/v1/media/uploadsmedia.write · upload capUpload a file directly, multipart.
curl -X POST https://api.vidmoat.com/v1/media/uploads \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-F "file=@clip.mp4"POST/v1/media/importmedia.write · upload capFetch a public http(s) URL server-side. Destinations pass through the SSRF guard, so private ranges and metadata endpoints are refused.
curl -X POST https://api.vidmoat.com/v1/media/import \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/clip.mp4"}'GET/v1/stock/searchstock.read · —Search the stock library by keyword, then import a result’s downloadUrl with /v1/media/import.
curl "https://api.vidmoat.com/v1/stock/search?query=city+at+night&type=video" \
-H "Authorization: Bearer $VIDMOAT_KEY"Generation
These spend credits, and they are the reason scopes exist. Every one runs the prompt-safety check; refusals are a 400 with a reason, not a silent empty result.
POST/v1/ai/transcriptionsai.transcribe · 10 cr / 5 minWord-level timings for captions. Feed words[] straight into an addCaptions command.
curl -X POST https://api.vidmoat.com/v1/ai/transcriptions \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"src":"https://example.com/voice.mp3","language":"en"}'Priced per 5 minutes of media, rounded up, and the length is measured before any work starts — anything over 30 minutes is refused with a 400 rather than transcribed and billed. Passing `script` + `duration` instead of `src` needs no provider and is free.
POST/v1/ai/speechai.speech · 15 creditsText to speech. Input is capped at 1,000 characters, which is what keeps a flat price honest.
curl -X POST https://api.vidmoat.com/v1/ai/speech \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Ship it.","voice":"nova"}'POST/v1/ai/imagesai.image · 60 creditsGenerate a still.
curl -X POST https://api.vidmoat.com/v1/ai/images \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"a lighthouse in fog, cinematic"}'POST/v1/ai/stickersai.image · 20 creditsGenerate a cut-out sticker with a transparent background.
curl -X POST https://api.vidmoat.com/v1/ai/stickers \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"a winking crab"}'POST/v1/ai/videosai.video · 80 credits/second (400 for the 5s default)Generate a clip. The dearest action on the platform, and Studio-only.
curl -X POST https://api.vidmoat.com/v1/ai/videos \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"drone over a harbour at dawn","durationSec":5}'Priced per second, not flat: 80 credits/second on grok-imagine-video (128 on 1.5), so 5s costs 400 and the 15s maximum costs 1,200. `durationSec` is clamped to 1-15 and the SAME clamped number is both sent to the provider and billed.
GET/v1/ai/jobs/{id}matching ai.* · —Poll a generation job. Same shape for every generator.
curl https://api.vidmoat.com/v1/ai/jobs/$JOB_ID \
-H "Authorization: Bearer $VIDMOAT_KEY"POST/v1/ai/agentai.agent · 5 creditsDescribe an edit in `message` and let the agent emit and apply the commands. One turn per call, charged once — you drive the loop.
curl -X POST https://api.vidmoat.com/v1/ai/agent \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"projectId":"'$ID'","message":"cut the silences and add captions"}'Add `dryRun: true` to get the plan back without applying it — the credit is still spent, because the model call is what it pays for.
POST/v1/ai/analyzeai.analyze · per actionNOT LIVE YET. Scene, audio and voice-activity analysis over a media URL — what to feed a cut decision.
curl -X POST https://api.vidmoat.com/v1/ai/analyze \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"src":"https://example.com/clip.mp4","kind":"visual"}'Not shipped in this release. The scope exists and the analysis runs internally, but the public endpoint is not exposed, so this path returns 404 today. Documented so you can design against the shape.
Webhooks
Phase 3. Documented here so you can design against the shape now; the endpoints are not live yet.
GET · POST · DELETE/v1/webhooks[/{id}]webhooks.manage · —Register a destination for render.completed, generation.completed, credits.low and friends. Signed Stripe-style with a timestamped HMAC prefix so a replayed body is detectable.
# not yet live
curl -X POST https://api.vidmoat.com/v1/webhooks \
-H "Authorization: Bearer $VIDMOAT_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hooks/vidmoat","events":["render.completed"]}'Until this ships, poll GET /v1/renders/{id}. Destination URLs will pass through the same SSRF guard as media import.
Idempotency and retries
GET is safe to retry freely. POST /v1/projects/{id}/commands is not idempotent — a retried batch appends the clips again — so treat a timeout as unknown and re-read the project before deciding. POST /v1/renders re-queues, which spends another export from the quota. An Idempotency-Key header is on the roadmap; until it exists, guard mutations on your side.
A note on polling
# right: back off, and stop
for i in $(seq 1 120); do
s=$(curl -s https://api.vidmoat.com/v1/renders/$JOB \
-H "Authorization: Bearer $VIDMOAT_KEY" | jq -r .render.status)
[ "$s" = "COMPLETED" ] && break
[ "$s" = "FAILED" ] && { echo "render failed"; exit 1; }
sleep 5
doneThe rate limiter cannot tell an eager poller from an attack, and a tight loop against a three-minute render will exhaust your budget before the video exists. Five seconds is plenty. Webhooks (Phase 3) remove the loop entirely.
Versioning. Additive changes ship into
v1 — new fields, new endpoints, new enum values. Ignore unknown fields; a client that rejects them will break on a routine release. Breaking changes get a /v2, and v1 is supported for at least 12 months after one exists. Send Vidmoat-Version: 2026-08-01 to pin date-based behaviour within v1.