Val Town is an OAuth 2.0 authorization server. Instead of asking users to paste an API token, your app can send them through a standard OAuth flow and receive an access token scoped to just the permissions you request. This is also how MCP clients connect to Val Town’s MCP server.
At a glance
Section titled “At a glance”- Issuer:
https://www.val.town/oauth - Authorization endpoint:
https://www.val.town/oauth/auth - Token endpoint:
https://www.val.town/oauth/token - Dynamic client registration:
https://www.val.town/oauth/reg - Metadata:
https://www.val.town/.well-known/oauth-authorization-server - Grant types:
authorization_code,refresh_token,client_credentials - PKCE (S256) is required for all clients
- Access tokens are opaque bearer tokens, valid for 24 hours
- Refresh tokens last 90 days and rotate on every use
Dynamic client registration
Section titled “Dynamic client registration”You don’t need to fill out a form or email us to create an OAuth client. Registration (RFC 7591) is open — POST your client metadata and you get a client back:
curl -X POST https://www.val.town/oauth/reg \ -H "Content-Type: application/json" \ -d '{ "client_name": "My App", "redirect_uris": ["https://myapp.example.com/callback"], "token_endpoint_auth_method": "none" }'redirect_urisis required.grant_typesdefaults to["authorization_code"]andresponse_typesdefaults to["code"].client_name,logo_uri, andclient_uriare shown to users on the consent screen, so it’s worth setting them.- Use
token_endpoint_auth_method: "none"for public clients (SPAs, native apps, MCP clients) — they authenticate with PKCE instead of a secret. Confidential clients receive aclient_secretin the registration response; store it securely, as it cannot be retrieved again.
Registration is rate limited to 10 requests per minute per IP.
Scopes
Section titled “Scopes”Request scopes as a space-separated list. Each Val Town resource has a read
(_r) or read/write (_rw) scope, mirroring
API token scopes:
| Scope | Grants |
|---|---|
user_r |
Read your user account details |
project_rw |
Read and write vals |
blob_rw |
Read and write blob storage |
sqlite_rw |
Read and write your SQLite databases |
email_rw |
Send emails |
telemetry_r |
Read logs and traces |
openid, profile, email |
OpenID Connect identity claims |
offline_access |
Receive a refresh token |
The scopes users grant on the consent screen become the permissions on the
issued access token — an app granted only sqlite_rw cannot touch your vals
or send email.
Scope naming caveats
Section titled “Scope naming caveats”A few quirks to be aware of, for historical reasons:
- Vals are covered by
project_rw, notval_rw. Vals were briefly called “projects”, and the scope name stuck. If you want to read or write a user’s vals, requestproject_rw. val_randval_rware deprecated. They exist only for backward compatibility with older clients and grant access to the legacy (pre-2025) val API — not to current vals. New clients should never request them.- Most scopes are read/write only. Aside from
user_randtelemetry_r, there are currently no read-only (_r) variants — for example, there is noproject_rfor read-only access to vals. If your app only needs to read, you still have to request the_rwscope today.
Using access tokens
Section titled “Using access tokens”Access tokens work exactly like API tokens — pass them as a Bearer token to api.val.town:
curl https://api.val.town/v1/me \ -H "Authorization: Bearer <access_token>"Tokens are opaque (not JWTs) and expire after 24 hours. If you requested
offline_access, use your refresh token at the token endpoint to get a new
one. Refresh tokens are single-use: each refresh returns a new refresh token
and invalidates the old one.
Resource indicators
Section titled “Resource indicators”Val Town supports RFC 8707
resource indicators. The default (and currently only meaningful) resource is
https://api.val.town. Scopes on the issued token are intersected with the
scopes the API resource server supports, so requesting a scope outside the
supported set silently drops it from the token.
MCP clients
Section titled “MCP clients”Val Town’s MCP server at https://api.val.town/v3/mcp uses this OAuth
server for authentication, following the standard MCP authorization flow:
- An unauthenticated request to the MCP endpoint returns
401with aWWW-Authenticateheader pointing athttps://api.val.town/.well-known/oauth-protected-resource. - That metadata names
https://www.val.town/oauthas the authorization server and lists the supported scopes. - The client registers itself via dynamic client registration, runs the authorization code + PKCE flow, and connects with the resulting token.
Any MCP client that implements the MCP authorization spec — Claude, Cursor, VS Code, and friends — handles this automatically; users just click through the consent screen.