The Mouve API is in active development and not yet live. Follow the changelog for the launch announcement.

Mouve Developers
Authentication

OAuth 2.1

Authorization-code flow with PKCE, dynamic client registration, account switching, prompts, token lifetimes, and refresh rotation.

Mouve implements OAuth 2.1: the authorization-code grant with PKCE (S256) required for every client. Implicit and password grants do not exist. If you have used any modern OAuth provider, nothing here will surprise you.

Not yet live

App registration and the endpoints below open with the API launch.

Registering an app

Two ways, one registry:

  • Developer portal (recommended): mouve.it/developers — create an app, set redirect URIs, get a client_id (and, for confidential clients, a client secret). Creating an app requires accepting the Developer Terms (see terms).
  • Dynamic client registration (RFC 7591): open, anonymous POST to the registration endpoint — this is how MCP clients such as Claude Desktop self-register. DCR apps start on the restricted rate tier; claim your app in the portal to lift it. A registration that names no scope is registered for the entire scope catalog. What your app can actually do is decided by the user on the consent screen, never by registration alone — in particular, health-data scopes are granted only if the user ticks the separate health-data checkbox there, so always read the scope field of the token response rather than assuming you received what you requested.

Redirect URIs must be exact-match HTTPS (loopback excepted for native apps).

Discovery

Standard metadata endpoints, suitable for any off-the-shelf OAuth client:

https://api.mouve.it/.well-known/oauth-authorization-server
https://api.mouve.it/.well-known/oauth-protected-resource

scopes_supported in the metadata is exactly the public scope catalog.

The flow

  1. Send the user to the authorization endpoint with client_id, redirect_uri, code_challenge (+_method=S256), state, and the space-separated scope list.
  2. Mouve shows a consent screen listing every requested scope in plain language — the same wording as the scope catalog. Health-data scopes appear in a separate opt-in section: if the user leaves it unticked, the grant simply proceeds without them (the token response's scope tells you what you actually got). The grant is recorded as a consent record.
  3. The user approves; your redirect_uri receives code + state.
  4. Exchange the code at the token endpoint with your code_verifier (and client secret, if confidential):
{
    "access_token": "eyJhbGciOiJSUzI1NiIs…",
    "token_type": "Bearer",
    "expires_in": 3600,
    "refresh_token": "…",
    "scope": "profile.read posts.write"
}

Prompts and account switching

The consent screen always lets the user switch accounts ("Not you? Use another account"). That signs the current Mouve session out and returns the user to sign-in for the same authorization request — the original client_id, redirect_uri, scope, state, PKCE code_challenge and resource are kept, so your flow completes without starting over. The pending request expires after 10 minutes.

Send the standard OIDC prompt parameter (space-separated) to control the account step:

promptBehaviour
loginForce a fresh sign-in, even when a Mouve session already exists. A session created before the authorization request does not count.
select_accountShow the account step even when a session exists — the user signs in again and may pick another account.
consentAlways show the consent screen. Mouve already shows it on every authorization, so this is the default behaviour.
https://api.mouve.it/oauth/authorize
  ?response_type=code
  &client_id=…
  &redirect_uri=https%3A%2F%2Fyour.app%2Fcallback
  &code_challenge=…
  &code_challenge_method=S256
  &scope=profile.read%20workouts.read
  &state=…
  &prompt=login

prompt=none and unknown values are rejected with HTTP 400 and error code invalid_request: a silent authorization needs a session check this endpoint cannot perform, and ignoring prompt=login would hand back the account the user just asked to change.

Token lifetimes and refresh rotation

  • Access tokens live 1 hour. Plan long-running work (e.g. a slow thread of scheduled replies) to stay under that, or refresh between steps.
  • Refresh tokens rotate on every use: each refresh returns a new refresh token and invalidates the old one. Persist the new one immediately — before acting on the new access token. A short server-side grace window absorbs crash-retry races, but reuse of a rotated token outside it revokes the whole grant (theft detection), and the user must reconnect.
  • A grant whose refresh token goes unused long enough to expire (about 30 days idle) requires the user to reconnect.

Errors you must handle

ResponseMeaningYour move
401 with TOKEN_EXPIREDAccess token past its hourRefresh, retry once
401 with TOKEN_REVOKEDUser disconnected, or rotation-reuse trippedStop; ask the user to reconnect
invalid_grant (token endpoint)Refresh token no longer validSame — reconnect
403Token is valid; the action is not permittedDo not refresh; see errors

Revocation is checked per request — see authentication.