Try removing the redirect_uri from both the authorize call and the token exchange. The accepted answer reports that dropping redirect_uri from both steps returned a success response from the server. So the authorize URL becomes just client_id and response_type=code, and the token POST carries only grant_type and code. If you do pass a redirect_uri, it must be byte-identical in both calls and match what is registered on the integration settings page.

Context: Stack Overflow question (score 8, has accepted answer): After creating a Notion integration and completing the browser authorize step, exchanging the interim code at the Notion token endpoint returns {"error": "invalid_grant"}. The token request posts grant_type=authorization_code with the code and a redirect_uri to https://api.notion.com/v1/oauth/token.