OAUTH PREVIEW — Create and manage OAuth clients in the Developer PortalStable portal →
Token Lifecycle

Token lifecycle and recovery

Treat expiration, refresh, revocation, and reauthorization as separate events. Your integration should expect every token to stop working eventually.

Token lifecycle: authorization produces an access token and sometimes a refresh token; access tokens expire, refresh may replace tokens, and terminal failures lead to reauthorization.

Teal boxes show the normal token lifecycle. The amber box marks a terminal failure that requires the user to authorize again.

Determine expiration

Access tokens are deliberately short-lived. Record when the token response arrives and add its expires_in duration to determine the expected expiry. Refresh slightly before that point while accounting for clock skew. Do not parse the opaque access token or assume a fixed lifetime.

Refresh access

A refresh token is issued after a successful authorization-code exchange only when the registered client includes offline_access. It lasts longer than an access token, but it is not permanent.

Send a form-encoded grant_type=refresh_token request to the discovered token endpoint. Confidential clients authenticate with client_secret_basic; public clients send their client ID without a secret. A refresh never expands or switches the grant's registered scopes.

Persist every replacement refresh token before discarding the previous value, and serialize refreshes for a grant. Lunch Money rotates refresh tokens; replay of a consumed token can invalidate the token family. If refresh fails terminally, delete unusable credentials and start a fresh authorization.

Revoke access

Send the token to the discovered revocation endpoint using the authentication method appropriate to the client. Revocation returns success without revealing unnecessary token state. Locally discard the access token, refresh token, and related session state even if the network response is ambiguous.

Revocation endpoint
The currently resolved endpoint is https://api.lunchmoney.dev/oauth/revoke. Use authorization-server discovery to configure deployed applications so they receive the current endpoint automatically.

For a quick development test, the following requests revoke an access token. They assume the values have already been loaded into shell variables from your secure development configuration.

curl --request POST 'https://api.lunchmoney.dev/oauth/revoke' \
  --user "$LUNCH_MONEY_CLIENT_ID:$LUNCH_MONEY_CLIENT_SECRET" \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode "token=$LUNCH_MONEY_ACCESS_TOKEN" \
  --data 'token_type_hint=access_token'
curl --request POST 'https://api.lunchmoney.dev/oauth/revoke' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode "client_id=$LUNCH_MONEY_CLIENT_ID" \
  --data-urlencode "token=$LUNCH_MONEY_ACCESS_TOKEN" \
  --data 'token_type_hint=access_token'

Do not paste a real client secret or token directly into a command where it may be saved in shell history. If the authorization also has a refresh token, revoke or otherwise invalidate it before testing a flow that should require the user to authorize again, then discard both tokens locally.

Revocation is different from expiration: expiration ends one credential naturally, while revocation deliberately removes access. Deleting or disabling an application and loss of user access can also make tokens unusable.

Recovery rules

See troubleshooting for error-specific guidance.