Errors
Everything that is not a 2xx is documented here. Key off statusCode first, then message when you need to tell variants apart.
MCP replies are JSON-RPC, so a transport level failure arrives as { "jsonrpc": "2.0", "error": { "code": -32000, "message": "..." }, "id": null } with the HTTP status named below. A failure inside a tool is different: see MCP tool errors.
400, Bad Request
The request shape is wrong. Common cases: a path parameter that fails validation on REST, or a malformed JSON-RPC body on MCP.
{ "statusCode": 400, "message": "...specific reason...", "error": "Bad Request" }401, Unauthorized
REST bodies vary by cause:
// Authorization header missing
{ "message": "Unauthorized", "statusCode": 401 }
// Header present, key unknown
{ "message": "Invalid API Key", "error": "Unauthorized", "statusCode": 401 }
// Key recognised but revoked or expired
{ "message": "API Key Expired", "error": "Unauthorized", "statusCode": 401 }MCP returns JSON-RPC errors, and separates a key that is wrong from a key that is absent.
// Key present, not valid. Both routes, HTTP 401.
{ "jsonrpc": "2.0", "error": { "code": -32000, "message": "Invalid API Key" }, "id": null }
// No key at all, on /mcp/oauth, calling a tool that needs an account. HTTP 401.
{ "jsonrpc": "2.0", "error": { "code": -32001, "message": "\"get_profile\" needs a Remoet account. search_jobs and get_listing work without one." }, "id": 1 }On https://api.remoet.dev/mcp/oauth the 401 also carries a WWW-Authenticate: Bearer resource_metadata="..." header, which is how an OAuth client discovers the flow. On https://api.remoet.dev/mcp it deliberately does not.
With no key at all, https://api.remoet.dev/mcp does not return 401 in the first place. The request succeeds, and the tool reports in band that it needs an account, pointing at the oauth route. search_jobs and get_listing need no account and answer normally.
API Key Expired is the one to plan for: keys are valid for 365 days from creation, and a revoked key returns the same body. Both are terminal, so do not retry them.
Recovery: mint a new key at www.remoet.dev/agents , or reconnect the connector. See Authorization for rotation.
403, Origin not allowed
Only browser clients can hit this. The MCP routes check the Origin header against an allowlist as required by the MCP spec, so a page on an unknown origin cannot drive your agent’s connection.
{ "jsonrpc": "2.0", "error": { "code": -32000, "message": "Origin not allowed" }, "id": null }Command line and server side clients send no Origin and are unaffected.
404, Not Found
A resource that does not exist, or is not yours:
{ "statusCode": 404, "message": "Link tree not found", "error": "Not Found" }There is no MCP session 404. The MCP server is stateless, so there is no session to go stale and nothing to re-initialize.
405, Method Not Allowed
GET on an MCP route. The stateless transport has no server to client stream, so use POST.
{ "jsonrpc": "2.0", "error": { "code": -32000, "message": "Method not allowed (stateless transport)" }, "id": null }429, Too Many Requests
Two separate layers, told apart by message.
Burst, from the per minute limiter (MCP: 120 per minute per key on each route; REST: 60 per minute per endpoint, 30 on /user/full):
{ "statusCode": 429, "message": "Rate limit exceeded", "error": "Too Many Requests" }It also sets X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, the last in seconds until the window rolls.
Daily, at 5,000 requests per account per surface:
{
"statusCode": 429,
"message": "Daily API request limit reached (5000/day). This is an abuse threshold, not a product gate: if you are hitting it legitimately, get in touch.",
"limit": 5000
}On MCP the same message comes back as a JSON-RPC error and says Daily MCP request limit reached.
Recovery: on burst, back off about 5 seconds with jitter. On the daily limit, wait for 00:00 UTC, or get in touch if you have a real workload that needs more. There is nothing to buy. See Limits.
500, Internal Server Error
{ "statusCode": 500, "message": "Internal server error" }Retry with exponential backoff. If a route fails consistently, open an issue at github.com/remoet-labs/remoet-docs or ping us on Discord .
MCP tool errors
A tool that fails still returns HTTP 200 and a valid JSON-RPC result, with isError set:
{
"result": {
"isError": true,
"content": [{ "type": "text", "text": "Error: ...explanation..." }]
}
}One bad tool call never breaks the connection. Surface the text to the user and let them retry or call something else.
Retry policy
| Status | Retry? | How |
|---|---|---|
| 400 | No | Fix the request |
| 401 | No | Refresh credentials |
| 403 | No | Use an allowed origin, or a client that sends none |
| 404 | No | The resource is gone or was never yours |
| 405 | No | Use POST |
| 429 burst | Yes | Back off about 5s with jitter |
| 429 daily | Not today | Honour the 00:00 UTC reset |
| 500, 502, 503, 504 | Yes | Exponential backoff: 1s, 2s, 4s, 8s, then give up |
Tool isError | Depends | Read the message; it is usually a bad argument |
See also
- Authorization, the header shape
- Limits, the daily and burst numbers
- MCP server, transport behaviour and tool results