Troubleshooting
401 vs 403, insufficient_scope, invalid_target, PKCE, Origin, and legacy SSE
The MCP endpoint and the REST API share one auth stack. Errors below are the ones clients hit most often.
401 invalid_token
The Bearer token is missing, expired, revoked, or not an accepted audience. Response includes:
WWW-Authenticate: Bearer error="invalid_token", resource_metadata="https://developer.servicebay.io/.well-known/oauth-protected-resource"Fix: rediscover the authorization server from that RFC 9728 document and run the authorization-code + PKCE flow again. Static keys: confirm the secret is sk_live_… and has not been rotated or revoked.
Revocation is effective in seconds (Worker tombstones). A token that worked a moment ago can 401 after Connected Apps revoke.
403 insufficient_scope
The credential is valid but lacks a required entity:action. Body includes requiredScopes and:
WWW-Authenticate: Bearer error="insufficient_scope", scope="quotes:write invoices:write", resource_metadata="…"MCP tool results also set _meta["mcp/www_authenticate"] so the client can step up.
Fix: reconnect and consent to the listed scopes, or widen/rotate the API key. On scope v2, write does not imply delete. Quote convert needs both quotes:write and invoices:write.
403 unknown_resource
The path is not in the deny-by-default route map (requiredScopesFor). You are calling a URL the API does not expose.
403 use_not_allowed
The API key's allowedUses does not include mcp (or rest on a REST call). Create a key with both uses, or PATCH allowedUses.
403 ip_not_allowed
The request IP is not on the key's ipAllowlist.
invalid_target
The resource parameter was set to something other than:
https://oauth.servicebay.io/mcp(canonical)https://developer.servicebay.iohttps://developer.servicebay.io/api/v1
Those aliases are rewritten to the MCP resource. Anything else is
invalid_target. On /authorize, that error is a redirect to the
client-validated redirect_uri (never an unvalidated URI). If the
authorization request itself cannot be parsed (unknown client, bad
redirect), the server returns JSON 400 instead of redirecting.
Fix: omit resource (it defaults) or send the MCP URL.
PKCE errors
PKCE S256 is mandatory for every client, including confidential ones. plain is rejected. The code_verifier on /token must match the code_challenge from /authorize.
Cursor Connect / DCR invalid_client_metadata
Cursor desktop registers cursor://anysphere.cursor-mcp/oauth/callback (and often http://localhost:8787/callback) without application_type: native. Servicebay accepts those redirects. If registration still returns 400 with a redirect_uris must be… description, the OAuth Worker needs the deploy that allows non-reserved custom schemes.
Workaround without redeploying: use a static API key in .cursor/mcp.json (headers.Authorization: Bearer sk_live_…) — see Cursor. Also free port 8787 if another process (commonly wrangler dev) is blocking Cursor's preferred loopback callback.
Origin 403
The Worker allows any https Origin and localhost. http:// non-loopback origins are rejected. Browser clients must be served over HTTPS (or localhost).
Legacy SSE clients
Servicebay does not expose /sse. Point those clients at a stdio bridge:
{
"mcpServers": {
"servicebay": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://oauth.servicebay.io/mcp"]
}
}
}fetch rejects the id
fetch only accepts sb:{entity}:{id} where {entity} is a known MCP
entity key (tickets, customers, …). Extra segments, unknown
entities, or a bare document id return a tool error — not a REST 404.
Citations 404
search / fetch cite
https://developer.servicebay.io/r/{org}/{entity}/{id}. That page is
login-gated: only a Pro admin of the org sees the summary.
Unknown entities, missing records, and non-admins get 404.
503 from REST (not MCP)
Production REST returns 503 when Upstash Redis is not configured
(API limiter). OAuth introspection also returns 503
(Unable to validate access token. Please try again.) when the Worker
is down, returns 5xx/429, or the shared internal secret does not
match — that is not a 401 and does not charge authfail. Quote
requestId from the 5xx body if you contact support.
See Error Handling for the shared envelope.