Setting the redirect URL

OAuth Redirect URI App registration Security

The redirect URI (callback URL) is the URL that receives the authorization code after the user presses Approve on the consent screen. You register one per developer app, and the redirect_uri you send to /oauth/authorize and to the token exchange must match it exactly. This article covers the registration rules, how to separate environments, how to change the URI, and common errors.

What the redirect URI does

  • Your app sends the user to the authorize URL, including redirect_uri.
  • ReceiptRoller checks that redirect_uri matches the registered value. If it does not, no code is issued and 400 invalid_redirect_uri is returned.
  • When the user approves, the browser is redirected to that URI with ?code=…&state=… added.
  • Your app exchanges code for tokens with the same redirect_uri. A different value returns 401 invalid_grant.

Sending codes only to the registered URI keeps third parties from intercepting them.

Registration rules

Item Rule
How manyOne per app
How it is comparedExact string match. Letter case, trailing slash and port all count (/callback and /callback/ are different).
WildcardsNot supported (the match is exact).
Query string and fragmentDo not include a fragment (anything after #). A query string is fine: code and state are then appended after &. Pass values that change per request in state, not in the URI.
SchemeUse an https:// URL in production.

Separating development and production

Because an app holds one URI, create one app per environment when development, staging and production use different URIs. Separate apps also give each environment its own client ID and secret, so the production secret never has to be shared with every developer.

My App (prod)  → https://app.example.com/oauth/callback
My App (stg)   → https://staging.example.com/oauth/callback
My App (dev)   → http://localhost:3000/oauth/callback

Do not register an http://localhost URI on your production app.

Registering or changing the URI

  • Open "Developer apps" from the business account's menu and open the app.
  • Press "Edit", enter the "Redirect URI" and save.

The change applies from the next authorize request. Only the business account's owner can edit apps. A code that was already issued is exchanged with the redirect_uri used in its authorize request.

Changing the URI of a live app

Because an app holds only one URI, authorization with the old URI fails with invalid_redirect_uri as soon as you save. Do one of the following.

  • Prepare your app's configuration for the new URI, then save the URI and deploy your app back to back, at a quiet time.
  • Create a new app with the new URI, test it, switch over, then delete the old app. In this case users need to authorize the new app again.

Using the state parameter

Put dynamic values, such as the page the user came from, in state rather than in the redirect URI. state comes back unchanged after authorization.

// Authorize request
https://receiptroller.io/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
  &scope=store.products.read
  &state=RANDOM_VALUE

// Callback
https://app.example.com/oauth/callback?code=…&state=RANDOM_VALUE

state also protects against CSRF. Create a random value for every request, keep it in the session, and check it on the callback.

Common errors

Symptom Cause What to do
400 invalid_redirect_uri on the authorize URLredirect_uri does not match the registered value, is missing, or the app has no URI registeredMake it identical to the registered value, including case, trailing slash and port.
401 invalid_grant on the token exchange (redirect_uri does not match the authorization request)The redirect_uri in the exchange differs from the authorize requestSend the same value as in the authorize request.
The page does not open after approvingThe server behind the registered URI cannot be reached (DNS, certificate, server down)Open the URI directly in a browser to check.

Related guides

Published: 2026-04-27 Updated: 2026-10-26
Tags
API (23) OAuth (18) Android (10) iOS (8) Webhook (5) App registration (4) Node.js (4) PIM (4) POS Integration (4) Quickstart (4)