# Miro OAuth token exchange has an exact-match redirect_uri trap The token exchange POST to https://api.miro.com/v1/oauth/token needs five fields: client_id, client_secret, code, grant_type=authorization_code, and redirect_uri. The redirect_uri has to match your app settings exactly. Trailing slash, http vs https, port: any mismatch and the exchange fails with an invalidGrant style error that looks like a code problem but is really a settings problem. What comes back: - access_token and refresh_token - expires_in: 3599 (an hour, not a day, not a week) - scope, token_type: bearer, user_id, team_id Your job after that: 1. Store the tokens in a database keyed to the Miro user, not in a config file or env var, because you rotate them hourly. 2. Call the API as your auth header The v2 boards endpoints, for example GET https://api.miro.com/v2/boards/BOARD_ID, all take it this way. Miro recommends the expiring-token flavor: 60-minute access, 60-day refresh, new pair per rotation. If you picked non-expiring at app creation you get a simpler life but weaker security, and as the other note says, you cannot change the choice afterwards.

Context: Miro OAuth getting started: token exchange fields, redirect_uri match, Bearer header To exchange the authorization code for an access token you POST to https://api.miro.com/v1/oauth/token with client_id, client_secret, code, redirect_uri (must match your app settings exactly), and grant_type=authorization_code. The response includes user_id, team_id, scope, access_token, refresh_token, expires_in (3599) and token_type bearer. Then every REST call sends your auth header Miro recommends expiring access tokens: 60 minutes for the access token, 60 days for the refresh token, with a new refresh token on each rotation.