OverviewAuthenticationScopesEndpointsVideo previewErrors & limitsPluginsBuild a platform
Documentation

Scopes

16 permissions, resource-dot-action. Coarse on purpose — you should be able to pick the right ones without reading a manual.

A credential carries a set of scopes chosen when it is minted. An endpoint that needs a scope the credential lacks returns 403 and names the missing scope, so you never have to guess which one you forgot.

ScopeWhat it lets the holder do
account.readSee your plan and remaining creditsdefault
projects.readRead your projects and their edit documentsdefault
projects.writeCreate, edit and delete projects
media.readList the media you have uploadeddefault
media.writeUpload media and import it from a URL
render.readCheck render progress and fetch preview framesdefault
render.writeStart renders, using your monthly export allowancespends
ai.transcribeTranscribe audio, spending your creditsspends
ai.speechGenerate speech, spending your creditsspends
ai.imageGenerate images and stickers, spending your creditsspends
ai.videoGenerate video, spending your creditsspends
ai.agentRun the AI editing agent, spending your creditsspends
ai.analyzeAnalyse footage for scenes, faces and speechspends
stock.readSearch the stock media librarydefault
webhooks.manageManage webhook endpoints for this appnew
plugins.invokeCall third-party plugins you have installed, sending them the arguments of each callnew
spends can cost the owner real money. default is in the read-only preset offered when you mint a key. new did not exist before scopes did, so keys predating this release do not carry it — see grandfathered keys below.

The two rules

1. A scope is necessary but never sufficient. Passing the scope check does not skip the plan check or the credit check. ai.video on a Creator-plan account still returns 402. Without this rule, a scope would be a privilege-escalation path: mint yourself a permission your plan cannot buy and get the feature free.
2. Scopes are additive. A key minted before scopes existed stores null, and null means the legacy full grant — it behaves exactly as it always did. Those rows are deliberately never backfilled to an explicit full-scope string: keeping them distinguishable is what will let us contact the grandfathered holders and retire the grant. You will get 90 days’ notice before that happens.

Choosing them

Mint time is the one moment least-privilege is cheap. Afterwards it means rotating a credential that is already in production, which is why the picker is the first thing on the key form rather than hidden behind an “advanced” toggle.

A useful default for a render pipeline — read projects, write them, render, poll:

account.read projects.read projects.write render.read render.write
Note what is absent: no media.write (this pipeline points clips at URLs it already has) and no ai.* at all. If that key leaks, the worst case is somebody burning your export quota — not 500 credits a call.

The app ceiling

Each app carries a ceiling: the most any of its keys may ever request. New apps get the full vocabulary, so the ceiling is invisible until we lower it — which we do for apps not verified for the expensive generation scopes. A key requesting something above its app’s ceiling gets the intersection, silently and without error, so a misconfigured deploy narrows rather than fails. Check what a credential actually ended up with:

curl https://api.vidmoat.com/v1/me \
  -H "Authorization: Bearer $VIDMOAT_KEY"

Scopes that will never exist

Posting to Vidmoat Social — a public feed written to by third parties is a moderation liability with no revenue attached. Anything admin. And key management: keys never mint keys, which is enforced at the route rather than by the absence of a scope, so it cannot be undone by adding one.

The webhooks.manage scope exists in the vocabulary but the endpoints are Phase 3 — see endpoints.

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.