Setting the redirect URL
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_urimatches the registered value. If it does not, no code is issued and 400invalid_redirect_uriis returned. - When the user approves, the browser is redirected to that URI with
?code=…&state=…added. - Your app exchanges
codefor tokens with the sameredirect_uri. A different value returns 401invalid_grant.
Sending codes only to the registered URI keeps third parties from intercepting them.
Registration rules
| Item | Rule |
|---|---|
| How many | One per app |
| How it is compared | Exact string match. Letter case, trailing slash and port all count (/callback and /callback/ are different). |
| Wildcards | Not supported (the match is exact). |
| Query string and fragment | Do 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. |
| Scheme | Use 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 URL | redirect_uri does not match the registered value, is missing, or the app has no URI registered | Make 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 request | Send the same value as in the authorize request. |
| The page does not open after approving | The server behind the registered URI cannot be reached (DNS, certificate, server down) | Open the URI directly in a browser to check. |
Related guides
-
Using the Store Information APIA guide to the REST API for fetching and updating a store's basic information (store name, store type, contact details, and address). Lets you implement a store information editing screen from token-authenticated clients such as staff apps.
-
Connect an AI agent to ReceiptRoller over MCPConnect Claude as an MCP connector, then build your own MCP client with dynamic client registration, PKCE and JSON-RPC calls over HTTP.
-
Using the Orders / OMS APIA guide to CRUD operations on the orders under a business account using ReceiptRoller's Orders / OMS API (/api/v1/orders). Covers creating, updating, transitioning status (confirm, process, cancel), and deleting orders, plus the flow for Android / iOS apps and server integrations.
-
Using the Business Hours APIA guide to ReceiptRoller's Business Hours API (/api/v1/stores/{storeId}/business-hours). Covers retrieving and updating per-day business hours, registering special business days (temporary closures and hour changes), configuring a store's workable hours (the upper bound for shift creation), and determining whether the store is currently open.
-
Using the Staff APIA guide to CRUD operations on the staff (employees) under a business account using ReceiptRoller's Staff API (/api/v1/staff). Supports fields compatible with Square Team Member (name kana, multiple job assignments, and the pay structure).