Notion API throwing `redirect_uri missing or invalid` error during OAuth flow

I’m working on setting up OAuth authentication with Notion’s public integration API. Everything seems configured correctly but I keep hitting a wall with this redirect URI error.

My HTML page:

<!DOCTYPE html>
<html>
  <head>
    <title>Notion Integration</title>
  </head>
  <body>
    <a href="https://api.notion.com/v1/oauth/authorize?client_id=b8e20155-957d-534e-ad77-e7cf8949dd64&redirect_uri=https%3A%2F%2Fmy-app-backend.herokuapp.com%2Fcallback%2Fnotion%2Fauth&response_type=code">
      Connect with Notion
    </a>
  </body>
</html>

Backend route setup:

app.get("/callback/notion/auth/:code/:state", (req, res) => {
    const authCode = req.params.code;
    const stateParam = req.params.state;
    console.log(authCode);
    res.json({
        "status": "callback received",
        "code": authCode,
        "state": stateParam
    });
    axios.post('https://api.notion.com/v1/oauth/token', {
        grant_type: "authorization_code",
        code: authCode,
        redirect_uri: 'https://my-app-backend.herokuapp.com/callback/notion/auth/'
    }).then((result) => {
        console.log(result)
    }).catch((error) => {
        console.log(`request failed:`, error);
    })
});

In my integration settings I set the redirect URI to https://my-app-backend.herokuapp.com/. The callback route never gets hit according to my server logs so I think there’s a mismatch somewhere in how I’m formatting these URLs. What am I missing here?

Found your problem - it’s a route mismatch. Your HTML link goes to /callback/notion/auth but your Express route expects /callback/notion/auth/:code/:state with path parameters. That won’t work because Notion sends the auth code as query parameters, not path parameters. Change your route to app.get("/callback/notion/auth", (req, res) => { and grab the code with req.query.code instead of req.params.code. Double-check that your redirect URI in the Notion dashboard matches your HTML link exactly - even small differences will break the validation.

There’s another issue beyond the route mismatch. Your integration settings have the redirect URI set to https://my-app-backend.herokuapp.com/ but your HTML authorization URL uses https://my-app-backend.herokuapp.com/callback/notion/auth. These need to match exactly. I hit this same problem last month and wasted hours debugging before I realized the integration dashboard didn’t match what I was using in code. Update your integration settings to use the full callback path, then fix the route to handle query parameters like davidw suggested. Notion validates the redirect URI before it even tries to hit your endpoint - that’s why you’re not seeing any server logs.

make sure your redirect uris are identical. in html it points to /callback/notion/auth, while server is listening for /:code/:state. also, check your integration settings—they should align as well. consistency is key!