## Summary
Every attempt to exchange a valid OAuth authorization_code for an access token via POST /oauth2/token fails with:
{ “message”: “Not Authorized”, “type”: “service.not_authorized” }
This happens 100% of the time, regardless of which application, which seller account, or which environment (Sandbox or Production) is used. I’ve isolated this specifically to the authorization_code grant type — directly using an already-issued access token, or using the Developer Console’s own “Renew token” feature, both work fine. So this doesn’t look like a general account restriction — it looks like something specifically blocking the OAuth code-exchange step for my developer account.
## Reproduction steps
1. Send user to /oauth2/authorize with valid client_id, scope, state.
2. User approves on the real Square consent screen (confirmed working correctly — correct app name, correct scopes shown).
3. Square redirects back with a valid, unused, unexpired code.
4. POST to /oauth2/token with client_id, client_secret, code, grant_type: authorization_code (JSON body, Content-Type: application/json) — fails every time with the error above.
## What I’ve ruled out
- Wrong/malformed secret: Regenerated the Sandbox Application secret 3 times from the correct location (OAuth tab, not Credentials tab), confirmed correct prefix (sandbox-sq0csb-), correct length, no whitespace/newlines each time.
- App-specific corruption: Reproduced on a completely brand-new application (sq0idp-c8q8A9cboJ1FxTYZ5pjpJg, “Shears and Beards Site”) created specifically to rule this out, in addition to the original app (sq0idp-wFcpcPpH6pGphYd46BsDQg, “Shears and Beards Booking”). Identical failure on both.
- Seller-account-specific issue: Reproduced with 3 different Sandbox test accounts (“Default Test Account”, “Shears & Beards (Shop)”, “Marcus Stand-in (Contractor)”). Identical failure with all three.
- Sandbox-only bug: Reproduced in Production as well, with my own real Square merchant account logging in and approving through the real consent screen. Identical “Not Authorized” error.
- Client-side code bug: Reproduced the exact same failure using Square’s own official API Explorer tool (Square API Explorer, Obtain Token endpoint) with the same client_id/client_secret/code — so this isn’t a bug in my server code.
- Minimal scope: Reproduced even when requesting just MERCHANT_PROFILE_READ alone (no Appointments/Items scopes), so it’s not a permissions/feature-gating issue on a specific scope.
## What does work (for contrast)
- Using the Developer Console’s “Renew token” button for an existing test-account token, then calling GET /v2/merchants with that token: returns 200 OK with real merchant data. So Sandbox itself, and token-based API auth in general, are healthy for my account.
- This confirms the failure is specific to the authorization_code to /oauth2/token exchange step itself, not a broader account or API-access problem.
## Apps/IDs involved
- Sandbox: sandbox-sq0idb-VAd1FszISeUaXu4E3Wj3uA (app: Shears and Beards Booking, sq0idp-wFcpcPpH6pGphYd46BsDQg)
- Sandbox: sandbox-sq0idb-1p5m6AjpcScGaz5FtcL17g (app: Shears and Beards Site, sq0idp-c8q8A9cboJ1FxTYZ5pjpJg)
- Production: sq0idp-c8q8A9cboJ1FxTYZ5pjpJg (same app, Production credentials)
## Ask
Could someone look into whether there’s an account-level restriction or flag blocking the authorization_code OAuth grant type specifically for my developer account? Happy to provide timestamps of specific failed requests if that helps you find them in logs.
Thanks!