Scopes
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.
| Scope | What it lets the holder do | |
|---|---|---|
account.read | See your plan and remaining credits | default |
projects.read | Read your projects and their edit documents | default |
projects.write | Create, edit and delete projects | |
media.read | List the media you have uploaded | default |
media.write | Upload media and import it from a URL | |
render.read | Check render progress and fetch preview frames | default |
render.write | Start renders, using your monthly export allowance | spends |
ai.transcribe | Transcribe audio, spending your credits | spends |
ai.speech | Generate speech, spending your credits | spends |
ai.image | Generate images and stickers, spending your credits | spends |
ai.video | Generate video, spending your credits | spends |
ai.agent | Run the AI editing agent, spending your credits | spends |
ai.analyze | Analyse footage for scenes, faces and speech | spends |
stock.read | Search the stock media library | default |
webhooks.manage | Manage webhook endpoints for this app | new |
plugins.invoke | Call third-party plugins you have installed, sending them the arguments of each call | new |
The two rules
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.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.writemedia.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.
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.