# Authentication

> One header, two kinds of token. Which one you want depends on whose account the work belongs to.

Every request carries `Authorization: Bearer <token>`. There is no query-parameter form and no cookie form: the API is called from your server, and a token in a URL ends up in access logs. A missing, unknown, revoked or expired token gets `401 unauthorized` with a `WWW-Authenticate: Bearer realm="vidmoat"` header.

| Use | Token | Whose account |
| --- | --- | --- |
| **API key**, server to server | `vmk_live_…` or `vmk_test_…` | Yours. Everything it generates bills you. |
| **OAuth access token**, acting for a user | 64 hex characters | The user who granted consent. Always live. |

## API keys

Mint one in [Apps](https://developer.vidmoat.com/developer/apps). The plaintext is shown once and never again: only a SHA-256 hash is stored, so a leaked key is fixed by revoking it and minting another, not by looking it up.

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

> **Note: The prefix is for people.** `vmk_test_` and `vmk_live_` make the environment legible in a log line or a screenshot, so a test key pasted into production config is obvious at a glance. Whether a key is test or live is decided by how it was minted, not by its text. Keys minted before the developer platform have no environment segment and keep working unchanged.

### Test and live

- **Test keys** are free on every plan. They never spend credits and never queue a real render, but projects and edits they make are real. See [test and live keys](https://developer.vidmoat.com/developer/docs/concepts/test-and-live-keys).
- **Live keys** need three things: the developer agreement accepted, the app approved in review, and a plan with live API access (Studio, Team or Enterprise). A live key can spend real credits.

> **Important: A key can never manage keys.** The key-management routes under `/api/developer` reject API-key authentication outright. If a key could mint a key, it could grant itself a scope it does not have, and the scope ceiling would be decoration. Keys are managed only from a signed-in console session.

## Sign in with Vidmoat (OAuth 2.0)

When your product edits *your user's* videos rather than your own, you want an access token for them, not your key. Vidmoat speaks OAuth 2.0 authorization code with PKCE, plus RFC 7591 dynamic client registration. Discovery metadata is where you expect it:

```bash
curl https://api.vidmoat.com/.well-known/oauth-authorization-server
```

| Step | Endpoint |
| --- | --- |
| Authorize (the user consents here) | `https://www.vidmoat.com/oauth/authorize` |
| Exchange the code, refresh a token | `POST https://api.vidmoat.com/api/oauth/token` |
| Register a client dynamically | `POST https://api.vidmoat.com/api/oauth/register` (10 an hour per IP) |

- Grants: `authorization_code` and `refresh_token`. Client authentication: `client_secret_post` or `client_secret_basic`.
- Use PKCE with `S256`. (`plain` is accepted for older MCP clients; do not choose it.)
- Redirect URIs are allowlisted per client and support an `https://prefix*` form, because some connector platforms mint a fresh callback per install.
- The consent screen lists the scopes you ask for, with the ones that can spend credits flagged, using the same descriptions as the [scope reference](https://developer.vidmoat.com/developer/docs/scopes).
- For a client that belongs to a developer app, access tokens last **1 hour** and refresh tokens **90 days**, rotating on every use. Store the newest refresh token each time.
- Users can see and revoke every app they connected at `https://www.vidmoat.com/oauth/connected`. A revoked grant fails with `401` on the next call.
- OAuth tokens are clamped to the scopes the user consented to and to your app's scope ceiling.

## What the credential does not change

A token identifies a real Vidmoat user, always. There are no anonymous sub-identities, and that is deliberate: every generated frame is attributable to an account with a monitored contact address, which is what makes moderation and takedown possible. Plan limits, credit balance, export allowance and content moderation apply to the token's owner exactly as they do in the editor.

So a scope is **necessary but never sufficient**. A key carrying `ai.video` on a plan without video generation still returns `402 plan_required`. Read [scopes](https://developer.vidmoat.com/developer/docs/scopes) next.

## When a request is refused

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `unauthorized` | No token, or one that is unknown, revoked or expired. |
| 403 | `insufficient_scope` | The token lacks a scope. The error names it (`scope`) and lists what you have (`granted`). |
| 403 | `access_suspended` | The owning account is restricted. `restriction` says what, until when and how to appeal. |
| 403 | `app_suspended` | The app's kill switch is on, independent of the account. |

---

Source: https://developer.vidmoat.com/developer/docs/authentication
Previous: [Quickstart](https://developer.vidmoat.com/developer/docs/quickstart.md)
Next: [Scopes](https://developer.vidmoat.com/developer/docs/scopes.md)
All documentation: https://developer.vidmoat.com/llms-full.txt
